diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage index ebb9c3dc86dd2..1c1d21024ce91 100644 --- a/.ci/Jenkinsfile_coverage +++ b/.ci/Jenkinsfile_coverage @@ -12,8 +12,12 @@ kibanaPipeline(timeoutMinutes: 240) { ]) { workers.base(name: 'coverage-worker', size: 'l', ramDisk: false, bootstrapped: false) { catchError { + + kibanaPipeline.bash(""" + echo '${TIME_STAMP}' + """, "### Print Canonical Time Stamp") + kibanaCoverage.runTests() - kibanaTeamAssign.load('team_assignment', "### Upload Team Assignment JSON") handleIngestion(TIME_STAMP) } handleFail() @@ -30,8 +34,8 @@ def handleIngestion(timestamp) { kibanaCoverage.collectVcsInfo("### Collect VCS Info") kibanaCoverage.generateReports("### Merge coverage reports") kibanaCoverage.uploadCombinedReports() - kibanaCoverage.ingest(env.JOB_NAME, BUILD_NUMBER, BUILD_URL, timestamp, previousSha, '### Ingest && Upload') kibanaCoverage.uploadCoverageStaticSite(timestamp) + kibanaCoverage.ingest(env.JOB_NAME, BUILD_NUMBER, BUILD_URL, timestamp, previousSha, teamAssignmentsPath(), '### Generate Team Assignments && Ingest') } def handlePreviousSha() { @@ -42,7 +46,7 @@ def handlePreviousSha() { def handleFail() { def buildStatus = buildUtils.getBuildStatus() - if(params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED' && buildStatus != 'UNSTABLE') { + if (params.NOTIFY_ON_FAILURE && buildStatus != 'SUCCESS' && buildStatus != 'ABORTED' && buildStatus != 'UNSTABLE') { slackNotifications.sendFailedBuild( channel: '#kibana-qa', username: 'Kibana QA' @@ -50,3 +54,7 @@ def handleFail() { } } +def teamAssignmentsPath() { + return 'src/dev/code_coverage/ingest_coverage/team_assignment/team_assignments.txt' +} + diff --git a/.eslintrc.js b/.eslintrc.js index 3161a25b70870..a0363e77e3596 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,9 +17,6 @@ * under the License. */ -const { readdirSync } = require('fs'); -const { resolve } = require('path'); - const APACHE_2_0_LICENSE_HEADER = ` /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -288,7 +285,7 @@ module.exports = { }, { target: [ - '(src|x-pack)/legacy/**/*', + 'src/legacy/**/*', '(src|x-pack)/plugins/**/(public|server)/**/*', 'examples/**/*', ], @@ -319,14 +316,11 @@ module.exports = { }, { target: [ - '(src|x-pack)/legacy/**/*', + 'src/legacy/**/*', '(src|x-pack)/plugins/**/(public|server)/**/*', 'examples/**/*', '!(src|x-pack)/**/*.test.*', '!(x-pack/)?test/**/*', - // next folder contains legacy browser tests which can't be migrated to jest - // which import np files - '!src/legacy/core_plugins/kibana/public/__tests__/**/*', ], from: [ '(src|x-pack)/plugins/**/(public|server)/**/*', @@ -341,14 +335,6 @@ module.exports = { '(src|x-pack)/plugins/**/*', '!(src|x-pack)/plugins/**/server/**/*', - 'src/legacy/core_plugins/**/*', - '!src/legacy/core_plugins/**/server/**/*', - '!src/legacy/core_plugins/**/index.{js,mjs,ts,tsx}', - - 'x-pack/legacy/plugins/**/*', - '!x-pack/legacy/plugins/**/server/**/*', - '!x-pack/legacy/plugins/**/index.{js,mjs,ts,tsx}', - 'examples/**/*', '!examples/**/server/**/*', ], @@ -370,12 +356,7 @@ module.exports = { }, { target: ['src/core/**/*'], - from: [ - 'plugins/**/*', - 'src/plugins/**/*', - 'src/legacy/core_plugins/**/*', - 'src/legacy/ui/**/*', - ], + from: ['plugins/**/*', 'src/plugins/**/*', 'src/legacy/ui/**/*'], errorMessage: 'The core cannot depend on any plugins.', }, { @@ -388,12 +369,6 @@ module.exports = { target: [ 'test/plugin_functional/plugins/**/public/np_ready/**/*', 'test/plugin_functional/plugins/**/server/np_ready/**/*', - 'src/legacy/core_plugins/**/public/np_ready/**/*', - 'src/legacy/core_plugins/vis_type_*/public/**/*', - '!src/legacy/core_plugins/vis_type_*/public/legacy*', - 'src/legacy/core_plugins/**/server/np_ready/**/*', - 'x-pack/legacy/plugins/**/public/np_ready/**/*', - 'x-pack/legacy/plugins/**/server/np_ready/**/*', ], allowSameFolder: true, errorMessage: @@ -413,6 +388,7 @@ module.exports = { */ { files: [ + '**/*.stories.tsx', 'x-pack/test/apm_api_integration/**/*.ts', 'x-pack/test/functional/apps/**/*.js', 'x-pack/plugins/apm/**/*.js', @@ -443,22 +419,14 @@ module.exports = { settings: { // instructs import/no-extraneous-dependencies to treat certain modules // as core modules, even if they aren't listed in package.json - 'import/core-modules': ['plugins', 'legacy/ui'], + 'import/core-modules': ['plugins'], 'import/resolver': { '@kbn/eslint-import-resolver-kibana': { forceNode: false, rootPackageName: 'kibana', kibanaPath: '.', - pluginMap: readdirSync(resolve(__dirname, 'x-pack/legacy/plugins')).reduce( - (acc, name) => { - if (!name.startsWith('_')) { - acc[name] = `x-pack/legacy/plugins/${name}`; - } - return acc; - }, - {} - ), + pluginMap: {}, }, }, }, @@ -764,16 +732,6 @@ module.exports = { }, }, - /** - * GIS overrides - */ - { - files: ['x-pack/legacy/plugins/maps/**/*.js'], - rules: { - 'react/prefer-stateless-function': [0, { ignorePureComponents: false }], - }, - }, - /** * ML overrides */ @@ -812,7 +770,7 @@ module.exports = { }, { // typescript only for front and back end - files: ['x-pack/{,legacy/}plugins/security_solution/**/*.{ts,tsx}'], + files: ['x-pack/plugins/security_solution/**/*.{ts,tsx}'], rules: { // This will be turned on after bug fixes are complete // '@typescript-eslint/explicit-member-accessibility': 'warn', @@ -858,7 +816,7 @@ module.exports = { // }, { // typescript and javascript for front and back end - files: ['x-pack/{,legacy/}plugins/security_solution/**/*.{js,mjs,ts,tsx}'], + files: ['x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}'], plugins: ['eslint-plugin-node', 'react'], env: { mocha: true, @@ -1089,7 +1047,7 @@ module.exports = { { // typescript only for front and back end files: [ - 'x-pack/{,legacy/}plugins/{alerts,alerting_builtins,actions,task_manager,event_log}/**/*.{ts,tsx}', + 'x-pack/plugins/{alerts,alerting_builtins,actions,task_manager,event_log}/**/*.{ts,tsx}', ], rules: { '@typescript-eslint/no-explicit-any': 'error', @@ -1238,10 +1196,7 @@ module.exports = { * TSVB overrides */ { - files: [ - 'src/plugins/vis_type_timeseries/**/*.{js,mjs,ts,tsx}', - 'src/legacy/core_plugins/vis_type_timeseries/**/*.{js,mjs,ts,tsx}', - ], + files: ['src/plugins/vis_type_timeseries/**/*.{js,mjs,ts,tsx}'], rules: { 'import/no-default-export': 'error', }, @@ -1275,5 +1230,20 @@ module.exports = { '@typescript-eslint/prefer-ts-expect-error': 'error', }, }, + { + files: [ + '**/public/**/*.{js,mjs,ts,tsx}', + '**/common/**/*.{js,mjs,ts,tsx}', + 'packages/**/*.{js,mjs,ts,tsx}', + ], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: ['lodash/*', '!lodash/fp'], + }, + ], + }, + }, ], }; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0bdddddab8de5..fc9c55e7868f4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,15 +2,20 @@ # Identify which groups will be pinged by changes to different parts of the codebase. # For more info, see https://help.github.com/articles/about-codeowners/ +# The #CC# prefix delineates Code Coverage, +# used for the 'team' designator within Kibana Stats + # App /x-pack/plugins/dashboard_enhanced/ @elastic/kibana-app /x-pack/plugins/discover_enhanced/ @elastic/kibana-app /x-pack/plugins/lens/ @elastic/kibana-app /x-pack/plugins/graph/ @elastic/kibana-app +/src/plugins/advanced_settings/ @elastic/kibana-app /src/plugins/charts/ @elastic/kibana-app /src/plugins/dashboard/ @elastic/kibana-app /src/plugins/discover/ @elastic/kibana-app /src/plugins/input_control_vis/ @elastic/kibana-app +/src/plugins/management/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app /src/plugins/vis_default_editor/ @elastic/kibana-app /src/plugins/vis_type_markdown/ @elastic/kibana-app @@ -23,6 +28,34 @@ /src/plugins/vis_type_vislib/ @elastic/kibana-app /src/plugins/vis_type_xy/ @elastic/kibana-app /src/plugins/visualize/ @elastic/kibana-app +/src/plugins/visualizations/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app +#CC# /src/plugins/vis_type @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/common/utils @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/migrations @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app +#CC# /src/legacy/core_plugins/console_legacy @elastic/kibana-app +#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-app +#CC# /src/legacy/core_plugins/timelion @elastic/kibana-app +#CC# /src/legacy/core_plugins/vis_type_tagcloud @elastic/kibana-app +#CC# /src/legacy/core_plugins/vis_type_vega @elastic/kibana-app +#CC# /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app +#CC# /src/legacy/server/sample_data/ @elastic/kibana-app +#CC# /src/legacy/server/url_shortening/ @elastic/kibana-app +#CC# /src/legacy/ui/public/state_management @elastic/kibana-app +#CC# /src/plugins/charts/public/static/color_maps @elastic/kibana-app +#CC# /src/plugins/index_pattern_management/public @elastic/kibana-app +#CC# /src/plugins/input_control_vis/ @elastic/kibana-app +#CC# /src/plugins/kibana_legacy/ @elastic/kibana-app +#CC# /src/plugins/timelion @elastic/kibana-app +#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-app +#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-app +#CC# /x-pack/plugins/lens/ @elastic/kibana-app # App Architecture /examples/bfetch_explorer/ @elastic/kibana-app-arch @@ -38,7 +71,6 @@ /examples/url_generators_explorer/ @elastic/kibana-app-arch /packages/elastic-datemath/ @elastic/kibana-app-arch /packages/kbn-interpreter/ @elastic/kibana-app-arch -/src/plugins/advanced_settings/ @elastic/kibana-app-arch /src/plugins/bfetch/ @elastic/kibana-app-arch /src/plugins/data/ @elastic/kibana-app-arch /src/plugins/embeddable/ @elastic/kibana-app-arch @@ -47,36 +79,66 @@ /src/plugins/kibana_react/ @elastic/kibana-app-arch /src/plugins/kibana_react/public/code_editor @elastic/kibana-canvas /src/plugins/kibana_utils/ @elastic/kibana-app-arch -/src/plugins/management/ @elastic/kibana-app-arch /src/plugins/navigation/ @elastic/kibana-app-arch /src/plugins/share/ @elastic/kibana-app-arch /src/plugins/ui_actions/ @elastic/kibana-app-arch -/src/plugins/visualizations/ @elastic/kibana-app-arch /x-pack/examples/ui_actions_enhanced_examples/ @elastic/kibana-app-arch /x-pack/plugins/data_enhanced/ @elastic/kibana-app-arch /x-pack/plugins/embeddable_enhanced/ @elastic/kibana-app-arch /x-pack/plugins/ui_actions_enhanced/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch +#CC# /src/legacy/core_plugins/status_page/public @elastic/kibana-app-arch +#CC# /src/legacy/server/index_patterns/ @elastic/kibana-app-arch +#CC# /src/legacy/ui/public/field_editor @elastic/kibana-app-arch +#CC# /src/legacy/ui/public/management @elastic/kibana-app-arch +#CC# /src/plugins/advanced_settings/ @elastic/kibana-app-arch +#CC# /src/plugins/bfetch/ @elastic/kibana-app-arch +#CC# /src/plugins/charts/ @elastic/kibana-app-arch +#CC# /src/plugins/index_pattern_management/public/service @elastic/kibana-app-arch +#CC# /src/plugins/inspector/ @elastic/kibana-app-arch +#CC# /src/plugins/saved_objects/ @elastic/kibana-app-arch +#CC# /src/plugins/share/ @elastic/kibana-app-arch +#CC# /src/plugins/vis_default_editor @elastic/kibana-app-arch +#CC# /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch +#CC# /x-pack/plugins/drilldowns/ @elastic/kibana-app-arch +#CC# /packages/kbn-interpreter/ @elastic/kibana-app-arch # APM /x-pack/plugins/apm/ @elastic/apm-ui /x-pack/test/functional/apps/apm/ @elastic/apm-ui /src/plugins/apm_oss/ @elastic/apm-ui /src/apm.js @watson @vigneshshanmugam +#CC# /src/plugins/apm_oss/ @elastic/apm-ui +#CC# /src/legacy/core_plugins/apm_oss/ @elastic/apm-ui +#CC# /src/legacy/ui/public/apm @elastic/apm-ui +#CC# /x-pack/legacy/plugins/apm/ @elastic/apm-ui +#CC# /x-pack/plugins/observability/ @elastic/apm-ui # Client Side Monitoring (lives in APM directories but owned by Uptime) /x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm @elastic/uptime +/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature @elastic/uptime /x-pack/plugins/apm/public/application/csmApp.tsx @elastic/uptime /x-pack/plugins/apm/public/components/app/RumDashboard @elastic/uptime /x-pack/plugins/apm/server/lib/rum_client @elastic/uptime /x-pack/plugins/apm/server/routes/rum_client.ts @elastic/uptime +/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @elastic/uptime /x-pack/plugins/apm/server/projections/rum_overview.ts @elastic/uptime +#CC# /x-pack/legacy/plugins/uptime @elastic/uptime # Beats +/x-pack/plugins/beats_management/ @elastic/beats /x-pack/legacy/plugins/beats_management/ @elastic/beats +#CC# /x-pack/plugins/beats_management/ @elastic/beats # Canvas /x-pack/plugins/canvas/ @elastic/kibana-canvas /x-pack/test/functional/apps/canvas/ @elastic/kibana-canvas +#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-canvas +#CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-canvas # Core UI # Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon @@ -84,18 +146,21 @@ /src/plugins/home/server/*.ts @elastic/kibana-core-ui /src/plugins/home/server/services/ @elastic/kibana-core-ui /x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui +#CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui +#CC# /src/plugins/newsfeed @elastic/kibana-core-ui +#CC# /src/plugins/home/public @elastic/kibana-core-ui +#CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui +#CC# /src/plugins/home/ @elastic/kibana-core-ui +#CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core-ui # Observability UIs -/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/plugins/infra/ @elastic/logs-metrics-ui /x-pack/plugins/ingest_manager/ @elastic/ingest-management -/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management /x-pack/plugins/observability/ @elastic/observability-ui /x-pack/plugins/monitoring/ @elastic/stack-monitoring-ui /x-pack/plugins/uptime @elastic/uptime # Machine Learning -/x-pack/legacy/plugins/ml/ @elastic/ml-ui /x-pack/plugins/ml/ @elastic/ml-ui /x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui /x-pack/test/functional/services/machine_learning/ @elastic/ml-ui @@ -107,12 +172,19 @@ /x-pack/test/functional/services/transform.ts @elastic/ml-ui # Maps -/x-pack/legacy/plugins/maps/ @elastic/kibana-gis /x-pack/plugins/maps/ @elastic/kibana-gis /x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis /x-pack/test/functional/apps/maps/ @elastic/kibana-gis /x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis /x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis +#CC# /src/legacy/core_plugins/region_map @elastic/kibana-gis +#CC# /src/legacy/core_plugins/tile_map @elastic/kibana-gis +#CC# /src/plugins/maps_legacy/ @elastic/kibana-gis +#CC# /x-pack/plugins/file_upload @elastic/kibana-gis +#CC# /x-pack/plugins/maps_legacy_licensing @elastic/kibana-gis +#CC# /src/plugins/home/server/tutorials @elastic/kibana-gis +#CC# /src/plugins/tile_map/ @elastic/kibana-gis +#CC# /src/plugins/region_map/ @elastic/kibana-gis # Operations /src/dev/ @elastic/kibana-operations @@ -135,6 +207,7 @@ /src/legacy/server/warnings/ @elastic/kibana-operations /.ci/es-snapshots/ @elastic/kibana-operations /vars/ @elastic/kibana-operations +#CC# /packages/kbn-expect/ @elastic/kibana-operations # Quality Assurance /src/dev/code_coverage @elastic/kibana-qa @@ -161,6 +234,31 @@ /src/plugins/status_page/ @elastic/kibana-platform /src/plugins/saved_objects_management/ @elastic/kibana-platform /src/dev/run_check_published_api_changes.ts @elastic/kibana-platform +#CC# /src/core/server/csp/ @elastic/kibana-platform +#CC# /src/legacy/core_plugins/kibana/server/lib @elastic/kibana-platform +#CC# /src/legacy/core_plugins/kibana/server/lib/management/saved_objects @elastic/kibana-platform +#CC# /src/legacy/core_plugins/kibana/server/routes/api/import/ @elastic/kibana-platform +#CC# /src/legacy/core_plugins/kibana/server/routes/api/export/ @elastic/kibana-platform +#CC# /src/legacy/core_plugins/elasticsearch @elastic/kibana-platform +#CC# /src/legacy/core_plugins/testbed @elastic/kibana-platform +#CC# /src/legacy/server/config/ @elastic/kibana-platform +#CC# /src/legacy/server/http/ @elastic/kibana-platform +#CC# /src/legacy/server/status/ @elastic/kibana-platform +#CC# /src/legacy/ui/public/new_platform @elastic/kibana-platform +#CC# /src/legacy/ui/public/plugin_discovery @elastic/kibana-platform +#CC# /src/legacy/ui/public/chrome @elastic/kibana-platform +#CC# /src/legacy/ui/public/notify @elastic/kibana-platform +#CC# /src/legacy/ui/public/documentation_links @elastic/kibana-platform +#CC# /src/legacy/ui/public/autoload @elastic/kibana-platform +#CC# /src/plugins/legacy_export/ @elastic/kibana-platform +#CC# /src/plugins/status_page/ @elastic/kibana-platform +#CC# /src/plugins/testbed/server/ @elastic/kibana-platform +#CC# /x-pack/legacy/plugins/xpack_main/server/ @elastic/kibana-platform +#CC# /x-pack/legacy/server/lib/ @elastic/kibana-platform +#CC# /x-pack/plugins/cloud/ @elastic/kibana-platform +#CC# /x-pack/plugins/features/ @elastic/kibana-platform +#CC# /x-pack/plugins/global_search/ @elastic/kibana-platform +#CC# /src/legacy/plugin_discovery/ @elastic/kibana-platform # Security /src/core/server/csp/ @elastic/kibana-security @elastic/kibana-platform @@ -180,12 +278,19 @@ /x-pack/test/security_functional/ @elastic/kibana-security /x-pack/test/spaces_api_integration/ @elastic/kibana-security /x-pack/test/token_api_integration/ @elastic/kibana-security +#CC# /src/legacy/ui/public/capabilities @elastic/kibana-security +#CC# /x-pack/legacy/plugins/encrypted_saved_objects/ @elastic/kibana-security +#CC# /x-pack/plugins/security_solution/ @elastic/kibana-security +#CC# /x-pack/plugins/security/ @elastic/kibana-security +#CC# /x-pack/plugins/audit_trail/ @elastic/kibana-security # Kibana Localization /src/dev/i18n/ @elastic/kibana-localization /src/legacy/server/i18n/ @elastic/kibana-localization /src/core/public/i18n/ @elastic/kibana-localization /packages/kbn-i18n/ @elastic/kibana-localization +#CC# /src/legacy/server/i18n/ @elastic/kibana-localization +#CC# /x-pack/plugins/translations/ @elastic/kibana-localization # Kibana Telemetry /packages/kbn-analytics/ @elastic/kibana-telemetry @@ -214,33 +319,23 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services +#CC# /x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services +#CC# /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services +#CC# /x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services +#CC# /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services +#CC# /x-pack/plugins/alerting_builtins @elastic/kibana-alerting-services # Enterprise Search # Shared /x-pack/plugins/enterprise_search/ @elastic/enterprise-search-frontend /x-pack/test/functional_enterprise_search/ @elastic/enterprise-search-frontend -# App Search -/x-pack/plugins/enterprise_search/public/applications/app_search @elastic/app-search-frontend -/x-pack/plugins/enterprise_search/server/routes/app_search @elastic/app-search-frontend -/x-pack/plugins/enterprise_search/server/collectors/app_search @elastic/app-search-frontend -/x-pack/plugins/enterprise_search/server/saved_objects/app_search @elastic/app-search-frontend -# Workplace Search -/x-pack/plugins/enterprise_search/public/applications/workplace_search @elastic/workplace-search-frontend -/x-pack/plugins/enterprise_search/server/routes/workplace_search @elastic/workplace-search-frontend -/x-pack/plugins/enterprise_search/server/collectors/workplace_search @elastic/workplace-search-frontend -/x-pack/plugins/enterprise_search/server/saved_objects/workplace_search @elastic/workplace-search-frontend # Elasticsearch UI /src/plugins/dev_tools/ @elastic/es-ui /src/plugins/console/ @elastic/es-ui /src/plugins/es_ui_shared/ @elastic/es-ui -/x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui +/x-pack/plugins/cross_cluster_replication/ @elastic/es-ui /x-pack/plugins/index_lifecycle_management/ @elastic/es-ui -/x-pack/legacy/plugins/index_management/ @elastic/es-ui -/x-pack/legacy/plugins/license_management/ @elastic/es-ui -/x-pack/legacy/plugins/rollup/ @elastic/es-ui -/x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui -/x-pack/legacy/plugins/upgrade_assistant/ @elastic/es-ui /x-pack/plugins/console_extensions/ @elastic/es-ui /x-pack/plugins/es_ui_shared/ @elastic/es-ui /x-pack/plugins/grokdebugger/ @elastic/es-ui @@ -254,6 +349,14 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/plugins/upgrade_assistant/ @elastic/es-ui /x-pack/plugins/watcher/ @elastic/es-ui /x-pack/plugins/ingest_pipelines/ @elastic/es-ui +/packages/kbn-ace/ @elastic/es-ui +/packages/kbn-monaco/ @elastic/es-ui +#CC# /x-pack/legacy/plugins/rollup/ @elastic/es-ui +#CC# /x-pack/legacy/server/lib/create_router/ @elastic/es-ui +#CC# /x-pack/legacy/server/lib/check_license/ @elastic/es-ui +#CC# /x-pack/plugins/console_extensions/ @elastic/es-ui +#CC# /x-pack/plugins/cross_cluster_replication/ @elastic/es-ui +#CC# /x-pack/plugins/es_ui_shared/ @elastic/es-u # Endpoint /x-pack/plugins/endpoint/ @elastic/endpoint-app-team @elastic/siem @@ -263,6 +366,9 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team @elastic/siem /x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team @elastic/siem /x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team @elastic/siem +#CC# /x-pack/legacy/plugins/siem/ @elastic/siem +#CC# /x-pack/plugins/siem/ @elastic/siem +#CC# /x-pack/plugins/security_solution/ @elastic/siem # Security Solution /x-pack/plugins/security_solution/ @elastic/siem @elastic/endpoint-app-team @@ -277,6 +383,7 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib # Design (at the bottom for specificity of SASS files) **/*.scss @elastic/kibana-design +#CC# /packages/kbn-ui-framework/ @elastic/kibana-design # Core design /src/plugins/dashboard/**/*.scss @elastic/kibana-core-ui-designers @@ -298,3 +405,9 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/plugins/endpoint/**/*.scss @elastic/security-design /x-pack/plugins/security_solution/**/*.scss @elastic/security-design +# Logstash +#CC# /x-pack/plugins/logstash/ @elastic/logstash + +# Reporting +#CC# /x-pack/plugins/reporting/ @elastic/kibana-reporting-services + diff --git a/.telemetryrc.json b/.telemetryrc.json index 818f9805628e1..d3446b45033ee 100644 --- a/.telemetryrc.json +++ b/.telemetryrc.json @@ -6,10 +6,7 @@ "src/plugins/kibana_react/", "src/plugins/testbed/", "src/plugins/kibana_utils/", - "src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts", - "src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts", - "src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts", - "src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts" + "src/plugins/kibana_usage_collection/server/collectors/ui_metric/telemetry_ui_metric_collector.ts" ] } ] diff --git a/config/kibana.yml b/config/kibana.yml index 72e0764f849a0..58ae8b9346f51 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -27,11 +27,6 @@ # The URLs of the Elasticsearch instances to use for all your queries. #elasticsearch.hosts: ["http://localhost:9200"] -# When this setting's value is true Kibana uses the hostname specified in the server.host -# setting. When the value of this setting is false, Kibana uses the hostname of the host -# that connects to this Kibana instance. -#elasticsearch.preserveHost: true - # Kibana uses an index in Elasticsearch to store saved searches, visualizations and # dashboards. Kibana creates a new index if the index doesn't already exist. #kibana.index: ".kibana" @@ -81,9 +76,6 @@ # Time in milliseconds for Elasticsearch to wait for responses from shards. Set to 0 to disable. #elasticsearch.shardTimeout: 30000 -# Time in milliseconds to wait for Elasticsearch at Kibana startup before retrying. -#elasticsearch.startupTimeout: 5000 - # Logs queries sent to Elasticsearch. Requires logging.verbose set to true. #elasticsearch.logQueries: false diff --git a/docs/api/saved-objects/create.asciidoc b/docs/api/saved-objects/create.asciidoc index 900f87ec29d8a..e6f3301bfea2b 100644 --- a/docs/api/saved-objects/create.asciidoc +++ b/docs/api/saved-objects/create.asciidoc @@ -13,7 +13,7 @@ experimental[] Create {kib} saved objects. `POST :/api/saved_objects//` -`POST :/s//api/saved_objects/` +`POST :/s//saved_objects/` [[saved-objects-api-create-path-params]] ==== Path parameters diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc index db2f85c54c762..d629a95073a74 100644 --- a/docs/apm/service-maps.asciidoc +++ b/docs/apm/service-maps.asciidoc @@ -2,11 +2,6 @@ [[service-maps]] === Service maps -beta::[] - -WARNING: Service map support for Internet Explorer 11 is extremely limited. -Please use Chrome or Firefox if available. - A service map is a real-time visual representation of the instrumented services in your application's architecture. It shows you how these services are connected, along with high-level metrics like average transaction duration, requests per minute, and errors per minute. diff --git a/docs/canvas/canvas-edit-workpads.asciidoc b/docs/canvas/canvas-edit-workpads.asciidoc index 6558def8a7474..6ad2d89be4a42 100644 --- a/docs/canvas/canvas-edit-workpads.asciidoc +++ b/docs/canvas/canvas-edit-workpads.asciidoc @@ -25,12 +25,12 @@ For example, to change the index pattern for a set of charts: Specify the variable options. [role="screenshot"] -image::images/specify_variable_syntax.png[Specify the variable syntax] +image::images/specify_variable_syntax.png[Image describing how to specify the variable syntax] Copy the variable, then apply it to each element you want to update in the *Expression editor*. [role="screenshot"] -image::images/copy_variable_syntax.png[Copy the variable syntax] +image::images/copy_variable_syntax.png[Image demonstrating expression editor] [float] [[apply-changes-to-the-entire-workpad]] @@ -85,7 +85,7 @@ To use an element with the same functionality and appearance in multiple places, Select the element, then click *Edit > Clone*. [role="screenshot"] -image::images/clone_element.gif[Clone elements] +image::images/clone_element.gif[Image showing how to clone elements] [float] [[move-and-resize-elements]] diff --git a/docs/canvas/canvas-expression-lifecycle.asciidoc b/docs/canvas/canvas-expression-lifecycle.asciidoc index 895c1382c4d36..7d48c593f9e18 100644 --- a/docs/canvas/canvas-expression-lifecycle.asciidoc +++ b/docs/canvas/canvas-expression-lifecycle.asciidoc @@ -30,7 +30,7 @@ The filtered <> becomes the _context_ of the next functi Let’s look at another expression, which uses the same <> function, but instead produces a pie chart. -image::images/canvas-functions-can-take-arguments-pie-chart.png[Pie Chart, height=400] +image::images/canvas-functions-can-take-arguments-pie-chart.png[Pie chart showing output of demodata function] [source,text] ---- filters @@ -47,7 +47,7 @@ If the expression stopped there, it would produce a `pointseries` data type as t The end result is a simple pie chart that uses the default color palette, but the <> function can take additional arguments that control how it gets rendered. For example, you can provide a `hole` argument to turn your pie chart into a donut chart by changing the expression to: -image::images/canvas-functions-can-take-arguments-donut-chart.png[Donut Chart, height=400] +image::images/canvas-functions-can-take-arguments-donut-chart.png[Alternative output as donut chart] [source,text] ---- filters @@ -83,7 +83,7 @@ You can substitute one function for another to change the output. For example, y Let’s change that last pie chart into a bubble chart by replacing the <> function with the <> function. This is possible because both functions can accept a `pointseries` data type as their _context_. Switching the functions will work, but it won’t produce a useful visualization on its own since you don’t have the x-axis and y-axis defined. You will also need to modify the <> function to change its output. In this case, you can change the `size` argument to `y`, so the maximum price values are plotted on the y-axis, and add an `x` argument using the `@timestamp` field in the data to plot those values over time. This leaves you with the following expression and produces a bubble chart showing the max price of each state over time: -image::images/canvas-change-your-expression-chart.png[Bubble Chart, height=400] +image::images/canvas-change-your-expression-chart.png[Bubble Chart, with price along x axis, and time along y axis] [source,text] ---- filters @@ -95,7 +95,7 @@ filters Similar to the <> function, the <> function takes arguments that control the design elements of the visualization. As one example, passing a `legend` argument with a value of `false` to the function will hide the legend on the chart. -image::images/canvas-change-your-expression-chart-no-legend.png[Bubble Chart Without Legend, height=400] +image::images/canvas-change-your-expression-chart-no-legend.png[Bubble Chart Without Legend] [source,text,subs=+quotes] ---- filters diff --git a/docs/canvas/canvas-present-workpad.asciidoc b/docs/canvas/canvas-present-workpad.asciidoc index a6d801b74fce1..b1492f57e46f8 100644 --- a/docs/canvas/canvas-present-workpad.asciidoc +++ b/docs/canvas/canvas-present-workpad.asciidoc @@ -18,7 +18,7 @@ image::images/canvas-autoplay-interval.png[Element autoplay interval] . To start your presentation, click *View > Enter fullscreen mode*. + [role="screenshot"] -image::images/canvas-fullscreen.png[Fullscreen mode] +image::images/canvas-fullscreen.png[Image showing how to enter fullscreen mode from view dropdown] . When you are ready to exit fullscreen mode, press the Esc (Escape) key. @@ -33,7 +33,7 @@ To get a closer look at a portion of your workpad, use the zoom options. . Select the zoom option. + [role="screenshot"] -image::images/canvas-zoom-controls.png[Zoom controls] +image::images/canvas-zoom-controls.png[Zoom controls, also in view dropdown] [float] [[configure-auto-refresh-interval]] diff --git a/docs/canvas/canvas-share-workpad.asciidoc b/docs/canvas/canvas-share-workpad.asciidoc index f6cd2d93a9372..4887eb6ca870d 100644 --- a/docs/canvas/canvas-share-workpad.asciidoc +++ b/docs/canvas/canvas-share-workpad.asciidoc @@ -13,7 +13,7 @@ Create a JSON file of your workpad that you can export outside of {kib}. Click *Share > Download as JSON*. [role="screenshot"] -image::images/canvas-export-workpad.png[Export single workpad] +image::images/canvas-export-workpad.png[Export single workpad through JSON, from Share dropdown] Want to export multiple workpads? Go to the *Canvas* home page, select the workpads you want to export, then click *Export*. @@ -26,7 +26,7 @@ If you have a subscription that supports the {report-features}, you can create a Click *Share > PDF reports > Generate PDF*. [role="screenshot"] -image::images/canvas-generate-pdf.gif[Generate PDF] +image::images/canvas-generate-pdf.gif[Image showing how to generate a PDF] For more information, refer to <>. @@ -39,7 +39,7 @@ If you have a subscription that supports the {report-features}, you can create a Click *Share > PDF reports > Copy POST URL*. [role="screenshot"] -image::images/canvas-create-URL.gif[Create POST URL] +image::images/canvas-create-URL.gif[Image showing how to create POST URL] For more information, refer to <>. @@ -58,7 +58,7 @@ beta[] Canvas allows you to create _shareables_, which are workpads that you dow 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::canvas/images/canvas-embed_workpad.gif[Share the workpad on a website] +image::canvas/images/canvas-embed_workpad.gif[Image showing how to 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. diff --git a/docs/canvas/canvas-tutorial.asciidoc b/docs/canvas/canvas-tutorial.asciidoc index a861b30db784f..ea4d2c8cc6a83 100644 --- a/docs/canvas/canvas-tutorial.asciidoc +++ b/docs/canvas/canvas-tutorial.asciidoc @@ -30,7 +30,7 @@ The default Elastic logo image appears on the page. . To replace the Elastic logo with your own image, select the image, then use the editor. [role="screenshot"] -image::images/canvas-image-element.png[] +image::images/canvas-image-element.png[Image showing how to add the image element] [float] === Customize your data with metrics @@ -70,7 +70,7 @@ You're now looking at the raw data syntax that Canvas uses to display the elemen .. Click *Run*. [role="screenshot"] -image::images/canvas-metric-element.png[] +image::images/canvas-metric-element.png[Image showing changes to the Canvas workpad] [float] === Show off your data with charts @@ -96,7 +96,7 @@ To show what your data can do, add charts, graphs, progress monitors, and more t .. From the *Y-axis* drop-down lists, select *Value*, then select *taxless_total_price*. [role="screenshot"] -image::images/canvas-chart-element.png[] +image::images/canvas-chart-element.png[Image showing Canvas workpad with sample data graph] [float] === Show how your data changes over time @@ -110,7 +110,7 @@ To focus your data on a specific time range, add the time filter. . To use the date time field from the sample data, enter `order_date` in the *Column* field, then click *Set*. [role="screenshot"] -image::images/canvas-timefilter-element.png[] +image::images/canvas-timefilter-element.png[Image showing Canvas workpad with filtered sample data graph] To see how the data changes, set the time filter to *Last 7 days*. As you change the time filter options, the elements automatically update. diff --git a/docs/developer/advanced/development-basepath.asciidoc b/docs/developer/advanced/development-basepath.asciidoc index cb341b9591174..30086288de834 100644 --- a/docs/developer/advanced/development-basepath.asciidoc +++ b/docs/developer/advanced/development-basepath.asciidoc @@ -7,11 +7,11 @@ You can set this explicitly using `server.basePath` in <>. Use the server.rewriteBasePath setting to tell {kib} if it should remove the basePath from requests it receives, and to prevent a deprecation warning at startup. This setting cannot end in a slash (/). -If you want to turn off the basepath when in development mode, start {kib} with the `--no-basepath` flag +If you want to turn off the basepath when in development mode, start {kib} with the `--no-base-path` flag [source,bash] ---- -yarn start --no-basepath +yarn start --no-base-path ---- diff --git a/docs/developer/architecture/development-plugin-saved-objects.asciidoc b/docs/developer/architecture/development-plugin-saved-objects.asciidoc new file mode 100644 index 0000000000000..0d31f5d90f668 --- /dev/null +++ b/docs/developer/architecture/development-plugin-saved-objects.asciidoc @@ -0,0 +1,311 @@ +[[development-plugin-saved-objects]] +== Using Saved Objects + +Saved Objects allow {kib} plugins to use {es} like a primary +database. Think of it as an Object Document Mapper for {es}. Once a +plugin has registered one or more Saved Object types, the Saved Objects client +can be used to query or perform create, read, update and delete operations on +each type. + +By using Saved Objects your plugin can take advantage of the following +features: + +* Migrations can evolve your document's schema by transforming documents and +ensuring that the field mappings on the index are always up to date. +* a <> is automatically exposed for each type (unless +`hidden=true` is specified). +* a Saved Objects client that can be used from both the server and the browser. +* Users can import or export Saved Objects using the Saved Objects management +UI or the Saved Objects import/export API. +* By declaring `references`, an object's entire reference graph will be +exported. This makes it easy for users to export e.g. a `dashboard` object and +have all the `visualization` objects required to display the dashboard +included in the export. +* When the X-Pack security and spaces plugins are enabled these transparently +provide RBAC access control and the ability to organize Saved Objects into +spaces. + +This document contains developer guidelines and best-practices for plugins +wanting to use Saved Objects. + +=== Registering a Saved Object type +Saved object type definitions should be defined in their own `my_plugin/server/saved_objects` directory. + +The folder should contain a file per type, named after the snake_case name of the type, and an `index.ts` file exporting all the types. + +.src/plugins/my_plugin/server/saved_objects/dashboard_visualization.ts +[source,typescript] +---- +import { SavedObjectsType } from 'src/core/server'; + +export const dashboardVisualization: SavedObjectsType = { + name: 'dashboard_visualization', // <1> + hidden: false, + namespaceType: 'single', + mappings: { + dynamic: false, + properties: { + description: { + type: 'text', + }, + hits: { + type: 'integer', + }, + }, + }, + migrations: { + '1.0.0': migratedashboardVisualizationToV1, + '2.0.0': migratedashboardVisualizationToV2, + }, +}; +---- +<1> Since the name of a Saved Object type forms part of the url path for the +public Saved Objects HTTP API, these should follow our API URL path convention +and always be written as snake case. + +.src/plugins/my_plugin/server/saved_objects/index.ts +[source,typescript] +---- +export { dashboardVisualization } from './dashboard_visualization'; +export { dashboard } from './dashboard'; +---- + +.src/plugins/my_plugin/server/plugin.ts +[source,typescript] +---- +import { dashboard, dashboardVisualization } from './saved_objects'; + +export class MyPlugin implements Plugin { + setup({ savedObjects }) { + savedObjects.registerType(dashboard); + savedObjects.registerType(dashboardVisualization); + } +} +---- + +=== Mappings +Each Saved Object type can define it's own {es} field mappings. +Because multiple Saved Object types can share the same index, mappings defined +by a type will be nested under a top-level field that matches the type name. + +For example, the mappings defined by the `dashboard_visualization` Saved +Object type: + +.src/plugins/my_plugin/server/saved_objects/dashboard_visualization.ts +[source,typescript] +---- +import { SavedObjectsType } from 'src/core/server'; + +export const dashboardVisualization: SavedObjectsType = { + name: 'dashboard_visualization', + ... + mappings: { + properties: { + dynamic: false, + description: { + type: 'text', + }, + hits: { + type: 'integer', + }, + }, + }, + migrations: { ... }, +}; +---- + +Will result in the following mappings being applied to the `.kibana` index: +[source,json] +---- +{ + "mappings": { + "dynamic": "strict", + "properties": { + ... + "dashboard_vizualization": { + "dynamic": false, + "properties": { + "description": { + "type": "text", + }, + "hits": { + "type": "integer", + }, + }, + } + } + } +} +---- + +Do not use field mappings like you would use data types for the columns of a +SQL database. Instead, field mappings are analogous to a SQL index. Only +specify field mappings for the fields you wish to search on or query. By +specifying `dynamic: false` in any level of your mappings, {es} will +accept and store any other fields even if they are not specified in your mappings. + +Since {es} has a default limit of 1000 fields per index, plugins +should carefully consider the fields they add to the mappings. Similarly, +Saved Object types should never use `dynamic: true` as this can cause an +arbitrary amount of fields to be added to the `.kibana` index. + +=== References +When a Saved Object declares `references` to other Saved Objects, the +Saved Objects Export API will automatically export the target object with all +of it's references. This makes it easy for users to export the entire +reference graph of an object. + +If a Saved Object can't be used on it's own, that is, it needs other objects +to exist for a feature to function correctly, that Saved Object should declare +references to all the objects it requires. For example, a `dashboard` +object might have panels for several `visualization` objects. When these +`visualization` objects don't exist, the dashboard cannot be rendered +correctly. The `dashboard` object should declare references to all it's +visualizations. + +However, `visualization` objects can continue to be rendered or embedded into +other dashboards even if the `dashboard` it was originally embedded into +doesn't exist. As a result, `visualization` objects should not declare +references to `dashboard` objects. + +For each referenced object, an `id`, `type` and `name` are added to the +`references` array: + +[source, typescript] +---- +router.get( + { path: '/some-path', validate: false }, + async (context, req, res) => { + const object = await context.core.savedObjects.client.create( + 'dashboard', + { + title: 'my dashboard', + panels: [ + { visualization: 'vis1' }, // <1> + ], + indexPattern: 'indexPattern1' + }, + { references: [ + { id: '...', type: 'visualization', name: 'vis1' }, + { id: '...', type: 'index_pattern', name: 'indexPattern1' }, + ] + } + ) + ... + } +); +---- +<1> Note how `dashboard.panels[0].visualization` stores the `name` property of +the reference (not the `id` directly) to be able to uniquely identify this +reference. This guarantees that the id the reference points to always remains +up to date. If a visualization `id` was directly stored in +`dashboard.panels[0].visualization` there is a risk that this `id` gets +updated without updating the reference in the references array. + +==== Writing Migrations + +Saved Objects support schema changes between Kibana versions, which we call +migrations. Migrations are applied when a Kibana installation is upgraded from +one version to the next, when exports are imported via the Saved Objects +Management UI, or when a new object is created via the HTTP API. + +Each Saved Object type may define migrations for its schema. Migrations are +specified by the Kibana version number, receive an input document, and must +return the fully migrated document to be persisted to Elasticsearch. + +Let's say we want to define two migrations: +- In version 1.1.0, we want to drop the `subtitle` field and append it to the + title +- In version 1.4.0, we want to add a new `id` field to every panel with a newly + generated UUID. + +First, the current `mappings` should always reflect the latest or "target" +schema. Next, we should define a migration function for each step in the schema +evolution: + +src/plugins/my_plugin/server/saved_objects/dashboard_visualization.ts +[source,typescript] +---- +import { SavedObjectsType, SavedObjectMigrationFn } from 'src/core/server'; +import uuid from 'uuid'; + +interface DashboardVisualizationPre110 { + title: string; + subtitle: string; + panels: Array<{}>; +} +interface DashboardVisualization110 { + title: string; + panels: Array<{}>; +} + +interface DashboardVisualization140 { + title: string; + panels: Array<{ id: string }>; +} + +const migrateDashboardVisualization110: SavedObjectMigrationFn< + DashboardVisualizationPre110, // <1> + DashboardVisualization110 +> = (doc) => { + const { subtitle, ...attributesWithoutSubtitle } = doc.attributes; + return { + ...doc, // <2> + attributes: { + ...attributesWithoutSubtitle, + title: `${doc.attributes.title} - ${doc.attributes.subtitle}`, + }, + }; +}; + +const migrateDashboardVisualization140: SavedObjectMigrationFn< + DashboardVisualization110, + DashboardVisualization140 +> = (doc) => { + const outPanels = doc.attributes.panels?.map((panel) => { + return { ...panel, id: uuid.v4() }; + }); + return { + ...doc, + attributes: { + ...doc.attributes, + panels: outPanels, + }, + }; +}; + +export const dashboardVisualization: SavedObjectsType = { + name: 'dashboard_visualization', // <1> + /** ... */ + migrations: { + // Takes a pre 1.1.0 doc, and converts it to 1.1.0 + '1.1.0': migrateDashboardVisualization110, + + // Takes a 1.1.0 doc, and converts it to 1.4.0 + '1.4.0': migrateDashboardVisualization140, // <3> + }, +}; +---- +<1> It is useful to define an interface for each version of the schema. This +allows TypeScript to ensure that you are properly handling the input and output +types correctly as the schema evolves. +<2> Returning a shallow copy is necessary to avoid type errors when using +different types for the input and output shape. +<3> Migrations do not have to be defined for every version. The version number +of a migration must always be the earliest Kibana version in which this +migration was released. So if you are creating a migration which will be +part of the v7.10.0 release, but will also be backported and released as +v7.9.3, the migration version should be: 7.9.3. + +Migrations should be written defensively, an exception in a migration function +will prevent a Kibana upgrade from succeeding and will cause downtime for our +users. Having said that, if a document is encountered that is not in the +expected shape, migrations are encouraged to throw an exception to abort the +upgrade. In most scenarios, it is better to fail an upgrade than to silently +ignore a corrupt document which can cause unexpected behaviour at some future +point in time. + +It is critical that you have extensive tests to ensure that migrations behave +as expected with all possible input documents. Given how simple it is to test +all the branch conditions in a migration function and the high impact of a bug +in this code, there's really no reason not to aim for 100% test code coverage. \ No newline at end of file diff --git a/docs/developer/architecture/index.asciidoc b/docs/developer/architecture/index.asciidoc index ac25fe003df08..dc15b90b69d1a 100644 --- a/docs/developer/architecture/index.asciidoc +++ b/docs/developer/architecture/index.asciidoc @@ -15,12 +15,16 @@ A few services also automatically generate api documentation which can be browse A few notable services are called out below. * <> +* <> * <> * <> +include::security/index.asciidoc[leveloffset=+1] + +include::development-plugin-saved-objects.asciidoc[leveloffset=+1] + include::add-data-tutorials.asciidoc[leveloffset=+1] include::development-visualize-index.asciidoc[leveloffset=+1] -include::security/index.asciidoc[leveloffset=+1] diff --git a/docs/developer/contributing/development-ci-metrics.asciidoc b/docs/developer/contributing/development-ci-metrics.asciidoc new file mode 100644 index 0000000000000..d4d54f1da7b8b --- /dev/null +++ b/docs/developer/contributing/development-ci-metrics.asciidoc @@ -0,0 +1,65 @@ +[[ci-metrics]] +== CI Metrics + +In addition to running our tests, CI collects metrics about the Kibana build. These metrics are sent to an external service to track changes over time, and to provide PR authors insights into the impact of their changes. + + +[[ci-metric-types]] +=== Metric types + + +[[ci-metric-types-bundle-size-metrics]] +==== Bundle size + +These metrics help contributors know how they are impacting the size of the bundles Kibana creates, and help make sure that Kibana loads as fast as possible. + +[[ci-metric-page-load-bundle-size]] `page load bundle size` :: +The size of the entry file produced for each bundle/plugin. This file is always loaded on every page load, so it should be as small as possible. To reduce this metric you can put any code that isn't necessary on every page load behind an https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports[`async import()`]. ++ +Code that is shared statically with other plugins will contribute to the `page load bundle size` of that plugin. This includes exports from the `public/index.ts` file and any file referenced by the `extraPublicDirs` manifest property. + +[[ci-metric-async-chunks-size]] `async chunks size` :: +An "async chunk" is created for the files imported by each https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports[`async import()`] statement. This metric tracks the sum size of these chunks, in bytes, broken down by plugin/bundle id. You can think of this as the amount of code users will have to download if they access all the components/applications within a bundle. + +[[ci-metric-misc-asset-size]] `miscellaneous assets size` :: +A "miscellaneous asset" is anything that isn't an async chunk or entry chunk, often images. This metric tracks the sum size of these assets, in bytes, broken down by plugin/bundle id. + +[[ci-metric-bundle-module-count]] `@kbn/optimizer bundle module count` :: +The number of separate modules included in each bundle/plugin. This is the best indicator we have for how long a specific bundle will take to be built by the `@kbn/optimizer`, so we report it to help people know when they've imported a module which might include a surprising number of sub-modules. + + +[[ci-metric-types-distributable-size]] +==== Distributable size + +The size of the Kibana distributable is an essential metric as it not only contributes to the time it takes to download, but it also impacts time it takes to extract the archive once downloaded. + +There are several metrics that we don't report on PRs because gzip-compression produces different file sizes even when provided the same input, so this metric would regularly show changes even though PR authors hadn't made any relevant changes. + +All metrics are collected from the `tar.gz` archive produced for the linux platform. + +[[ci-metric-distributable-file-count]] `distributable file count` :: +The number of files included in the default distributable. + +[[ci-metric-oss-distributable-file-count]] `oss distributable file count` :: +The number of files included in the OSS distributable. + +[[ci-metric-distributable-size]] `distributable size` :: +The size, in bytes, of the default distributable. _(not reported on PRs)_ + +[[ci-metric-oss-distributable-size]] `oss distributable size` :: +The size, in bytes, of the OSS distributable. _(not reported on PRs)_ + + +[[ci-metric-types-saved-object-field-counts]] +==== Saved Object field counts + +Elasticsearch limits the number of fields in an index to 1000 by default, and we want to avoid raising that limit. + +[[ci-metric-saved-object-field-count]] `Saved Objects .kibana field count` :: +The number of saved object fields broken down by saved object type. + + +[[ci-metric-adding-new-metrics]] +=== Adding new metrics + +You can report new metrics by using the `CiStatsReporter` class provided by the `@kbn/dev-utils` package. This class is automatically configured on CI and its methods noop when running outside of CI. For more details checkout the {kib-repo}blob/{branch}/packages/kbn-dev-utils/src/ci_stats_reporter[`CiStatsReporter` readme]. \ No newline at end of file diff --git a/docs/developer/contributing/index.asciidoc b/docs/developer/contributing/index.asciidoc index 99ab83bc2f073..ecb37ffe9c97b 100644 --- a/docs/developer/contributing/index.asciidoc +++ b/docs/developer/contributing/index.asciidoc @@ -9,6 +9,7 @@ Read <> to get your environment up and running, the * <> * <> * <> +* <> * <> * <> * <> @@ -78,6 +79,8 @@ include::development-tests.asciidoc[leveloffset=+1] include::interpreting-ci-failures.asciidoc[leveloffset=+1] +include::development-ci-metrics.asciidoc[leveloffset=+1] + include::development-documentation.asciidoc[leveloffset=+1] include::development-pull-request.asciidoc[leveloffset=+1] diff --git a/docs/developer/getting-started/development-plugin-resources.asciidoc b/docs/developer/getting-started/development-plugin-resources.asciidoc index 8f81138b81ed7..1fe211c87c660 100644 --- a/docs/developer/getting-started/development-plugin-resources.asciidoc +++ b/docs/developer/getting-started/development-plugin-resources.asciidoc @@ -33,7 +33,7 @@ To enable TypeScript support, create a `tsconfig.json` file at the root of your ["source","js"] ----------- { - // extend {kib}'s tsconfig, or use your own settings + // extend Kibana's tsconfig, or use your own settings "extends": "../../kibana/tsconfig.json", // tell the TypeScript compiler where to find your source files diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index b426621fed296..da62d1707f065 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -70,8 +70,20 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module. -|{kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions] -|This plugin provides methods which will parse & execute an expression pipeline +|<> +|Expression pipeline is a chain of functions that *pipe* its output to the +input of the next function. Functions can be configured using arguments provided +by the user. The final output of the expression pipeline can be rendered using +one of the *renderers* registered in `expressions` plugin. + +All the arguments to expression functions need to be serializable, as well as input and output. +Expression functions should try to stay 'pure'. This makes functions easy to reuse and also +make it possible to serialize the whole chain as well as output at every step of execution. + +Expressions power visualizations in Dashboard and Lens, as well as, every +*element* in Canvas is backed by an expression. + +This plugin provides methods which will parse & execute an *expression pipeline* string for you, as well as a series of registries for advanced users who might want to incorporate their own functions, types, and renderers into the service for use in their own application. @@ -168,9 +180,16 @@ It also provides a stateful version of it on the start contract. which also contains the timelion APIs and backend, look at the vis_type_timelion plugin. -|{kib-repo}blob/{branch}/src/plugins/ui_actions/README.md[uiActions] +|<> |An API for: +- creating custom functionality (`actions`) +- creating custom user interaction events (`triggers`) +- attaching and detaching `actions` to `triggers`. +- emitting `trigger` events +- executing `actions` attached to a given `trigger`. +- exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. + |{kib-repo}blob/{branch}/src/plugins/url_forwarding/README.md[urlForwarding] |This plugins contains helpers to redirect legacy URLs. It can be used to forward old URLs to their new counterparts. @@ -504,8 +523,14 @@ in their infrastructure. |Contains HTTP endpoints and UiSettings that are slated for removal. +|{kib-repo}blob/{branch}/x-pack/plugins/drilldowns/url_drilldown/README.md[urlDrilldown] +|NOTE: This plugin contains implementation of URL drilldown. For drilldowns infrastructure code refer to ui_actions_enhanced plugin. + + |=== include::{kibana-root}/src/plugins/dashboard/README.asciidoc[leveloffset=+1] +include::{kibana-root}/src/plugins/expressions/README.asciidoc[leveloffset=+1] +include::{kibana-root}/src/plugins/ui_actions/README.asciidoc[leveloffset=+1] include::{kibana-root}/x-pack/plugins/dashboard_enhanced/README.asciidoc[leveloffset=+1] include::{kibana-root}/x-pack/plugins/embeddable_enhanced/README.asciidoc[leveloffset=+1] diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.md index 903462ac3039d..470a41f30afbf 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.md @@ -29,4 +29,5 @@ export interface SavedObjectsFindOptions | [sortField](./kibana-plugin-core-public.savedobjectsfindoptions.sortfield.md) | string | | | [sortOrder](./kibana-plugin-core-public.savedobjectsfindoptions.sortorder.md) | string | | | [type](./kibana-plugin-core-public.savedobjectsfindoptions.type.md) | string | string[] | | +| [typeToNamespacesMap](./kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md) | Map<string, string[] | undefined> | This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the type and namespaces fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md new file mode 100644 index 0000000000000..4af8c9ddeaff4 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsFindOptions](./kibana-plugin-core-public.savedobjectsfindoptions.md) > [typeToNamespacesMap](./kibana-plugin-core-public.savedobjectsfindoptions.typetonamespacesmap.md) + +## SavedObjectsFindOptions.typeToNamespacesMap property + +This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the `type` and `namespaces` fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. + +Signature: + +```typescript +typeToNamespacesMap?: Map; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.md index 804c83f7c1b48..ce5c20e60ca11 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.md @@ -29,4 +29,5 @@ export interface SavedObjectsFindOptions | [sortField](./kibana-plugin-core-server.savedobjectsfindoptions.sortfield.md) | string | | | [sortOrder](./kibana-plugin-core-server.savedobjectsfindoptions.sortorder.md) | string | | | [type](./kibana-plugin-core-server.savedobjectsfindoptions.type.md) | string | string[] | | +| [typeToNamespacesMap](./kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md) | Map<string, string[] | undefined> | This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the type and namespaces fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md new file mode 100644 index 0000000000000..8bec759f05580 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsFindOptions](./kibana-plugin-core-server.savedobjectsfindoptions.md) > [typeToNamespacesMap](./kibana-plugin-core-server.savedobjectsfindoptions.typetonamespacesmap.md) + +## SavedObjectsFindOptions.typeToNamespacesMap property + +This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved object client wrapper. If this is defined, it supersedes the `type` and `namespaces` fields when building the Elasticsearch query. Any types that are not included in this map will be excluded entirely. If a type is included but its value is undefined, the operation will search for that type in the Default namespace. + +Signature: + +```typescript +typeToNamespacesMap?: Map; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.find.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.find.md index 1b562263145da..d3e93e7af2aa0 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.find.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.find.md @@ -7,14 +7,14 @@ Signature: ```typescript -find({ search, defaultSearchOperator, searchFields, rootSearchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespaces, type, filter, preference, }: SavedObjectsFindOptions): Promise>; +find(options: SavedObjectsFindOptions): Promise>; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| { search, defaultSearchOperator, searchFields, rootSearchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespaces, type, filter, preference, } | SavedObjectsFindOptions | | +| options | SavedObjectsFindOptions | | Returns: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md index 14d3741425987..1d11d5262a9c4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md @@ -24,7 +24,7 @@ export declare class SavedObjectsRepository | [delete(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.delete.md) | | Deletes an object | | [deleteByNamespace(namespace, options)](./kibana-plugin-core-server.savedobjectsrepository.deletebynamespace.md) | | Deletes all objects from the provided namespace. | | [deleteFromNamespaces(type, id, namespaces, options)](./kibana-plugin-core-server.savedobjectsrepository.deletefromnamespaces.md) | | Removes one or more namespaces from a given multi-namespace saved object. If no namespaces remain, the saved object is deleted entirely. This method and \[addToNamespaces\][SavedObjectsRepository.addToNamespaces()](./kibana-plugin-core-server.savedobjectsrepository.addtonamespaces.md) are the only ways to change which Spaces a multi-namespace saved object is shared to. | -| [find({ search, defaultSearchOperator, searchFields, rootSearchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespaces, type, filter, preference, })](./kibana-plugin-core-server.savedobjectsrepository.find.md) | | | +| [find(options)](./kibana-plugin-core-server.savedobjectsrepository.find.md) | | | | [get(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.get.md) | | Gets a single object | | [incrementCounter(type, id, counterFieldName, options)](./kibana-plugin-core-server.savedobjectsrepository.incrementcounter.md) | | Increases a counter field by one. Creates the document if one doesn't exist for the given id. | | [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsrepository.update.md) | | Updates an object | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.createemptyfindresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.createemptyfindresponse.md new file mode 100644 index 0000000000000..40e865cb02ce8 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.createemptyfindresponse.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) > [createEmptyFindResponse](./kibana-plugin-core-server.savedobjectsutils.createemptyfindresponse.md) + +## SavedObjectsUtils.createEmptyFindResponse property + +Creates an empty response for a find operation. This is only intended to be used by saved objects client wrappers. + +Signature: + +```typescript +static createEmptyFindResponse: ({ page, perPage, }: SavedObjectsFindOptions) => SavedObjectsFindResponse; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md index e365dfbcb5142..83831f65bd41a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md @@ -15,6 +15,7 @@ export declare class SavedObjectsUtils | Property | Modifiers | Type | Description | | --- | --- | --- | --- | +| [createEmptyFindResponse](./kibana-plugin-core-server.savedobjectsutils.createemptyfindresponse.md) | static | <T>({ page, perPage, }: SavedObjectsFindOptions) => SavedObjectsFindResponse<T> | Creates an empty response for a find operation. This is only intended to be used by saved objects client wrappers. | | [namespaceIdToString](./kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md) | static | (namespace?: string | undefined) => string | Converts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with the exception of the undefined namespace ID (which has a namespace string of 'default'). | | [namespaceStringToId](./kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md) | static | (namespace: string) => string | undefined | Converts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with the exception of the 'default' namespace string (which has a namespace ID of undefined). | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror._constructor_.md new file mode 100644 index 0000000000000..676f1a2c785f8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DuplicateIndexPatternError](./kibana-plugin-plugins-data-public.duplicateindexpatternerror.md) > [(constructor)](./kibana-plugin-plugins-data-public.duplicateindexpatternerror._constructor_.md) + +## DuplicateIndexPatternError.(constructor) + +Constructs a new instance of the `DuplicateIndexPatternError` class + +Signature: + +```typescript +constructor(message: string); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| message | string | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror.md new file mode 100644 index 0000000000000..7ed8f97976464 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.duplicateindexpatternerror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DuplicateIndexPatternError](./kibana-plugin-plugins-data-public.duplicateindexpatternerror.md) + +## DuplicateIndexPatternError class + +Signature: + +```typescript +export declare class DuplicateIndexPatternError extends Error +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(message)](./kibana-plugin-plugins-data-public.duplicateindexpatternerror._constructor_.md) | | Constructs a new instance of the DuplicateIndexPatternError class | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.md index fee34378339af..45cd088ee1203 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.md @@ -7,7 +7,7 @@ Signature: ```typescript -export interface IEsSearchRequest extends IKibanaSearchRequest +export interface IEsSearchRequest extends IKibanaSearchRequest ``` ## Properties @@ -15,5 +15,4 @@ export interface IEsSearchRequest extends IKibanaSearchRequest | Property | Type | Description | | --- | --- | --- | | [indexType](./kibana-plugin-plugins-data-public.iessearchrequest.indextype.md) | string | | -| [params](./kibana-plugin-plugins-data-public.iessearchrequest.params.md) | ISearchRequestParams | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.params.md deleted file mode 100644 index 24107faa28e8c..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchrequest.params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) > [params](./kibana-plugin-plugins-data-public.iessearchrequest.params.md) - -## IEsSearchRequest.params property - -Signature: - -```typescript -params?: ISearchRequestParams; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md deleted file mode 100644 index 00a56c6fe9c31..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) > [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md) - -## IEsSearchResponse.isPartial property - -Indicates whether the results returned are complete or partial - -Signature: - -```typescript -isPartial?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md deleted file mode 100644 index 56fb1a7519811..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) > [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md) - -## IEsSearchResponse.isRunning property - -Indicates whether async search is still in flight - -Signature: - -```typescript -isRunning?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md index 7c9a6aa702463..c8a372edbdb85 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md @@ -2,19 +2,10 @@ [Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) -## IEsSearchResponse interface +## IEsSearchResponse type Signature: ```typescript -export interface IEsSearchResponse extends IKibanaSearchResponse +export declare type IEsSearchResponse = IKibanaSearchResponse>; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md) | boolean | Indicates whether the results returned are complete or partial | -| [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md) | boolean | Indicates whether async search is still in flight | -| [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) | SearchResponse<Source> | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md deleted file mode 100644 index f4648143ebc2e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) > [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) - -## IEsSearchResponse.rawResponse property - -Signature: - -```typescript -rawResponse: SearchResponse; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md index 2c131c6da9937..60ac95bc21af2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md @@ -7,8 +7,5 @@ Signature: ```typescript -fieldFormatMap?: Record; +fieldFormatMap?: Record | undefined>; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md new file mode 100644 index 0000000000000..7466e4b9cf658 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) > [getFormatterForField](./kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md) + +## IIndexPattern.getFormatterForField property + +Signature: + +```typescript +getFormatterForField?: (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md index 1cb89822eb605..ba77e659f0834 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md @@ -14,8 +14,9 @@ export interface IIndexPattern | Property | Type | Description | | --- | --- | --- | -| [fieldFormatMap](./kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md) | Record<string, {
id: string;
params: unknown;
}> | | +| [fieldFormatMap](./kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md) | Record<string, SerializedFieldFormat<unknown> | undefined> | | | [fields](./kibana-plugin-plugins-data-public.iindexpattern.fields.md) | IFieldType[] | | +| [getFormatterForField](./kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md) | (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat | | | [id](./kibana-plugin-plugins-data-public.iindexpattern.id.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.iindexpattern.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-public.iindexpattern.title.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.tospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.tospec.md index fd20f2944c5be..0fe62f575a927 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.tospec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpatternfieldlist.tospec.md @@ -9,7 +9,7 @@ ```typescript toSpec(options?: { getFormatterForField?: IndexPattern['getFormatterForField']; - }): FieldSpec[]; + }): IndexPatternFieldMap; ``` ## Parameters @@ -20,5 +20,5 @@ toSpec(options?: { Returns: -`FieldSpec[]` +`IndexPatternFieldMap` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.debug.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.debug.md deleted file mode 100644 index cfb21a78557fd..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.debug.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) > [debug](./kibana-plugin-plugins-data-public.ikibanasearchrequest.debug.md) - -## IKibanaSearchRequest.debug property - -Optionally tell search strategies to output debug information. - -Signature: - -```typescript -debug?: boolean; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.md index 57e0fbe2c19a9..bba051037e29b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.md @@ -7,13 +7,13 @@ Signature: ```typescript -export interface IKibanaSearchRequest +export interface IKibanaSearchRequest ``` ## Properties | Property | Type | Description | | --- | --- | --- | -| [debug](./kibana-plugin-plugins-data-public.ikibanasearchrequest.debug.md) | boolean | Optionally tell search strategies to output debug information. | | [id](./kibana-plugin-plugins-data-public.ikibanasearchrequest.id.md) | string | An id can be used to uniquely identify this request. | +| [params](./kibana-plugin-plugins-data-public.ikibanasearchrequest.params.md) | Params | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.params.md new file mode 100644 index 0000000000000..b7e2006a66c14 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchrequest.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) > [params](./kibana-plugin-plugins-data-public.ikibanasearchrequest.params.md) + +## IKibanaSearchRequest.params property + +Signature: + +```typescript +params?: Params; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md new file mode 100644 index 0000000000000..702c774eb8818 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) > [isPartial](./kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md) + +## IKibanaSearchResponse.isPartial property + +Indicates whether the results returned are complete or partial + +Signature: + +```typescript +isPartial?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md new file mode 100644 index 0000000000000..1e625ccff26f9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) > [isRunning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md) + +## IKibanaSearchResponse.isRunning property + +Indicates whether search is still in flight + +Signature: + +```typescript +isRunning?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md index f7dfd1ddd2f49..159dc8f4ada18 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.md @@ -7,7 +7,7 @@ Signature: ```typescript -export interface IKibanaSearchResponse +export interface IKibanaSearchResponse ``` ## Properties @@ -15,6 +15,9 @@ export interface IKibanaSearchResponse | Property | Type | Description | | --- | --- | --- | | [id](./kibana-plugin-plugins-data-public.ikibanasearchresponse.id.md) | string | Some responses may contain a unique id to identify the request this response came from. | +| [isPartial](./kibana-plugin-plugins-data-public.ikibanasearchresponse.ispartial.md) | boolean | Indicates whether the results returned are complete or partial | +| [isRunning](./kibana-plugin-plugins-data-public.ikibanasearchresponse.isrunning.md) | boolean | Indicates whether search is still in flight | | [loaded](./kibana-plugin-plugins-data-public.ikibanasearchresponse.loaded.md) | number | If relevant to the search strategy, return a loaded number that represents how progress is indicated. | +| [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) | RawResponse | | | [total](./kibana-plugin-plugins-data-public.ikibanasearchresponse.total.md) | number | If relevant to the search strategy, return a total number that represents how progress is indicated. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md new file mode 100644 index 0000000000000..865c7d795801b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) > [rawResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.rawresponse.md) + +## IKibanaSearchResponse.rawResponse property + +Signature: + +```typescript +rawResponse: RawResponse; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md index a5bb15c963978..f81d03a28ec12 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._constructor_.md @@ -9,13 +9,12 @@ Constructs a new instance of the `IndexPattern` class Signature: ```typescript -constructor(id: string | undefined, { savedObjectsClient, apiClient, patternCache, fieldFormats, indexPatternsService, onNotification, onError, shortDotsEnable, metaFields, }: IndexPatternDeps); +constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| id | string | undefined | | -| { savedObjectsClient, apiClient, patternCache, fieldFormats, indexPatternsService, onNotification, onError, shortDotsEnable, metaFields, } | IndexPatternDeps | | +| { spec, fieldFormats, shortDotsEnable, metaFields, } | IndexPatternDeps | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._fetchfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._fetchfields.md deleted file mode 100644 index 8fff8baa71139..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern._fetchfields.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [\_fetchFields](./kibana-plugin-plugins-data-public.indexpattern._fetchfields.md) - -## IndexPattern.\_fetchFields() method - -Signature: - -```typescript -_fetchFields(): Promise; -``` -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md index 4bbbd83c65e10..99d2fc00a6b7b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md @@ -4,10 +4,12 @@ ## IndexPattern.addScriptedField() method +Add scripted field to field list + Signature: ```typescript -addScriptedField(name: string, script: string, fieldType: string | undefined, lang: string): Promise; +addScriptedField(name: string, script: string, fieldType?: string): Promise; ``` ## Parameters @@ -16,8 +18,7 @@ addScriptedField(name: string, script: string, fieldType: string | undefined, la | --- | --- | --- | | name | string | | | script | string | | -| fieldType | string | undefined | | -| lang | string | | +| fieldType | string | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.create.md deleted file mode 100644 index 5c122b835f59d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.create.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [create](./kibana-plugin-plugins-data-public.indexpattern.create.md) - -## IndexPattern.create() method - -Signature: - -```typescript -create(allowOverride?: boolean): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| allowOverride | boolean | | - -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md index b89b244d9826c..904d52fcd5751 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md @@ -7,5 +7,5 @@ Signature: ```typescript -fieldFormatMap: any; +fieldFormatMap: Record; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md index d4dca48c7cd7b..76bc41238526e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fields.md @@ -8,6 +8,6 @@ ```typescript fields: IIndexPatternFieldList & { - toSpec: () => FieldSpec[]; + toSpec: () => IndexPatternFieldMap; }; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md deleted file mode 100644 index 4d44b386a1db1..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [fieldsFetcher](./kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md) - -## IndexPattern.fieldsFetcher property - -Signature: - -```typescript -fieldsFetcher: any; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.flattenhit.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.flattenhit.md index db28d95197bb3..049c3e5e990f7 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.flattenhit.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.flattenhit.md @@ -7,5 +7,5 @@ Signature: ```typescript -flattenHit: any; +flattenHit: (hit: Record, deep?: boolean) => Record; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formatfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formatfield.md index 5a475d6161ac3..aadaddca6cc85 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formatfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formatfield.md @@ -7,5 +7,5 @@ Signature: ```typescript -formatField: any; +formatField: FormatFieldFn; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formathit.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formathit.md index ac515d374a93f..2be76bf1c1e05 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formathit.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.formathit.md @@ -7,5 +7,8 @@ Signature: ```typescript -formatHit: any; +formatHit: { + (hit: Record, type?: string): any; + formatField: FormatFieldFn; + }; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md new file mode 100644 index 0000000000000..2c5f30e4889ea --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getAsSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md) + +## IndexPattern.getAsSavedObjectBody() method + +Returns index pattern as saved object body for saving + +Signature: + +```typescript +getAsSavedObjectBody(): { + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }; +``` +Returns: + +`{ + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md index 7984f7aff1d2d..ba31d60b56892 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md @@ -4,17 +4,19 @@ ## IndexPattern.getFormatterForField() method +Provide a field, get its formatter + Signature: ```typescript -getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat; +getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| field | IndexPatternField | IndexPatternField['spec'] | | +| field | IndexPatternField | IndexPatternField['spec'] | IFieldType | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md new file mode 100644 index 0000000000000..349da63c13ca7 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md) + +## IndexPattern.getOriginalSavedObjectBody property + +Get last saved saved object fields + +Signature: + +```typescript +getOriginalSavedObjectBody: () => { + title?: string | undefined; + timeFieldName?: string | undefined; + intervalName?: string | undefined; + fields?: string | undefined; + sourceFilters?: string | undefined; + fieldFormatMap?: string | undefined; + typeMeta?: string | undefined; + type?: string | undefined; + }; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md index 121d32c7c40c8..4ce0144b73882 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md @@ -4,6 +4,8 @@ ## IndexPattern.getSourceFiltering() method +Get the source filtering configuration for that index. + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.init.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.init.md deleted file mode 100644 index 595992dc82b74..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.init.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [init](./kibana-plugin-plugins-data-public.indexpattern.init.md) - -## IndexPattern.init() method - -Signature: - -```typescript -init(): Promise; -``` -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.initfromspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.initfromspec.md deleted file mode 100644 index 764dd11638221..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.initfromspec.md +++ /dev/null @@ -1,22 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [initFromSpec](./kibana-plugin-plugins-data-public.indexpattern.initfromspec.md) - -## IndexPattern.initFromSpec() method - -Signature: - -```typescript -initFromSpec(spec: IndexPatternSpec): this; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| spec | IndexPatternSpec | | - -Returns: - -`this` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md deleted file mode 100644 index 27f99f418a078..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [isTimeBasedWildcard](./kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md) - -## IndexPattern.isTimeBasedWildcard() method - -Signature: - -```typescript -isTimeBasedWildcard(): boolean; -``` -Returns: - -`boolean` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.iswildcard.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.iswildcard.md deleted file mode 100644 index e5ea55ef1dd48..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.iswildcard.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [isWildcard](./kibana-plugin-plugins-data-public.indexpattern.iswildcard.md) - -## IndexPattern.isWildcard() method - -Signature: - -```typescript -isWildcard(): boolean; -``` -Returns: - -`boolean` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index 87ce1e258712a..c07041470d102 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -14,22 +14,22 @@ export declare class IndexPattern implements IIndexPattern | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(id, { savedObjectsClient, apiClient, patternCache, fieldFormats, indexPatternsService, onNotification, onError, shortDotsEnable, metaFields, })](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the IndexPattern class | +| [(constructor)({ spec, fieldFormats, shortDotsEnable, metaFields, })](./kibana-plugin-plugins-data-public.indexpattern._constructor_.md) | | Constructs a new instance of the IndexPattern class | ## Properties | Property | Modifiers | Type | Description | | --- | --- | --- | --- | -| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | any | | -| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => FieldSpec[];
} | | -| [fieldsFetcher](./kibana-plugin-plugins-data-public.indexpattern.fieldsfetcher.md) | | any | | -| [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | any | | -| [formatField](./kibana-plugin-plugins-data-public.indexpattern.formatfield.md) | | any | | -| [formatHit](./kibana-plugin-plugins-data-public.indexpattern.formathit.md) | | any | | +| [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | Record<string, any> | | +| [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | +| [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | +| [formatField](./kibana-plugin-plugins-data-public.indexpattern.formatfield.md) | | FormatFieldFn | | +| [formatHit](./kibana-plugin-plugins-data-public.indexpattern.formathit.md) | | {
(hit: Record<string, any>, type?: string): any;
formatField: FormatFieldFn;
} | | +| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.getoriginalsavedobjectbody.md) | | () => {
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;
fields?: string | undefined;
sourceFilters?: string | undefined;
fieldFormatMap?: string | undefined;
typeMeta?: string | undefined;
type?: string | undefined;
} | Get last saved saved object fields | | [id](./kibana-plugin-plugins-data-public.indexpattern.id.md) | | string | | | [intervalName](./kibana-plugin-plugins-data-public.indexpattern.intervalname.md) | | string | undefined | | | [metaFields](./kibana-plugin-plugins-data-public.indexpattern.metafields.md) | | string[] | | -| [originalBody](./kibana-plugin-plugins-data-public.indexpattern.originalbody.md) | | {
[key: string]: any;
} | | +| [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md) | | () => void | Reset last saved saved object fields. used after saving | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpattern.sourcefilters.md) | | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | string | undefined | | | [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | string | | @@ -41,26 +41,18 @@ export declare class IndexPattern implements IIndexPattern | Method | Modifiers | Description | | --- | --- | --- | -| [\_fetchFields()](./kibana-plugin-plugins-data-public.indexpattern._fetchfields.md) | | | -| [addScriptedField(name, script, fieldType, lang)](./kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md) | | | -| [create(allowOverride)](./kibana-plugin-plugins-data-public.indexpattern.create.md) | | | +| [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-public.indexpattern.getaggregationrestrictions.md) | | | +| [getAsSavedObjectBody()](./kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | | [getComputedFields()](./kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md) | | | | [getFieldByName(name)](./kibana-plugin-plugins-data-public.indexpattern.getfieldbyname.md) | | | -| [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | | +| [getFormatterForField(field)](./kibana-plugin-plugins-data-public.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | | [getNonScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getnonscriptedfields.md) | | | | [getScriptedFields()](./kibana-plugin-plugins-data-public.indexpattern.getscriptedfields.md) | | | -| [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | | +| [getSourceFiltering()](./kibana-plugin-plugins-data-public.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | | [getTimeField()](./kibana-plugin-plugins-data-public.indexpattern.gettimefield.md) | | | -| [init()](./kibana-plugin-plugins-data-public.indexpattern.init.md) | | | -| [initFromSpec(spec)](./kibana-plugin-plugins-data-public.indexpattern.initfromspec.md) | | | | [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | | -| [isTimeBasedWildcard()](./kibana-plugin-plugins-data-public.indexpattern.istimebasedwildcard.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | | -| [isWildcard()](./kibana-plugin-plugins-data-public.indexpattern.iswildcard.md) | | | -| [popularizeField(fieldName, unit)](./kibana-plugin-plugins-data-public.indexpattern.popularizefield.md) | | | -| [prepBody()](./kibana-plugin-plugins-data-public.indexpattern.prepbody.md) | | | -| [refreshFields()](./kibana-plugin-plugins-data-public.indexpattern.refreshfields.md) | | | -| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | | +| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [toSpec()](./kibana-plugin-plugins-data-public.indexpattern.tospec.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.originalbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.originalbody.md deleted file mode 100644 index 4bc3c76afbae9..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.originalbody.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [originalBody](./kibana-plugin-plugins-data-public.indexpattern.originalbody.md) - -## IndexPattern.originalBody property - -Signature: - -```typescript -originalBody: { - [key: string]: any; - }; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.popularizefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.popularizefield.md deleted file mode 100644 index eba5382158520..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.popularizefield.md +++ /dev/null @@ -1,23 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [popularizeField](./kibana-plugin-plugins-data-public.indexpattern.popularizefield.md) - -## IndexPattern.popularizeField() method - -Signature: - -```typescript -popularizeField(fieldName: string, unit?: number): Promise; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| fieldName | string | | -| unit | number | | - -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.prepbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.prepbody.md deleted file mode 100644 index 1d77b2a55860e..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.prepbody.md +++ /dev/null @@ -1,33 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [prepBody](./kibana-plugin-plugins-data-public.indexpattern.prepbody.md) - -## IndexPattern.prepBody() method - -Signature: - -```typescript -prepBody(): { - title: string; - timeFieldName: string | undefined; - intervalName: string | undefined; - sourceFilters: string | undefined; - fields: string | undefined; - fieldFormatMap: string | undefined; - type: string | undefined; - typeMeta: string | undefined; - }; -``` -Returns: - -`{ - title: string; - timeFieldName: string | undefined; - intervalName: string | undefined; - sourceFilters: string | undefined; - fields: string | undefined; - fieldFormatMap: string | undefined; - type: string | undefined; - typeMeta: string | undefined; - }` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.refreshfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.refreshfields.md deleted file mode 100644 index 271d0c45a4244..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.refreshfields.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [refreshFields](./kibana-plugin-plugins-data-public.indexpattern.refreshfields.md) - -## IndexPattern.refreshFields() method - -Signature: - -```typescript -refreshFields(): Promise; -``` -Returns: - -`Promise` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md index e902d9c42b082..aaaebdaccca5d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md @@ -4,6 +4,8 @@ ## IndexPattern.removeScriptedField() method +Remove scripted field from field list + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md new file mode 100644 index 0000000000000..6bbc13d8fd410 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-public.indexpattern.resetoriginalsavedobjectbody.md) + +## IndexPattern.resetOriginalSavedObjectBody property + +Reset last saved saved object fields. used after saving + +Signature: + +```typescript +resetOriginalSavedObjectBody: () => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md index eff2349f053ff..77a8ebb0b2d3f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md @@ -4,12 +4,6 @@ ## IndexPatternAttributes interface -> Warning: This API is now obsolete. -> -> - -Use data plugin interface instead - Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md index 6d62053726197..9b226266f0b5a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md @@ -4,6 +4,8 @@ ## IndexPatternField.conflictDescriptions property +Description of field type conflicts across different indices in the same index pattern + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md index 84c0a75fd206d..1b8e13a38c6d9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.count.md @@ -4,6 +4,8 @@ ## IndexPatternField.count property +Count is used for field popularity + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md index 0a8446d40e5ec..b81218eb08886 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.lang.md @@ -4,6 +4,8 @@ ## IndexPatternField.lang property +Script field language + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md index 215188ffa2607..4f49a9a8fc3ab 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -21,15 +21,15 @@ export declare class IndexPatternField implements IFieldType | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [aggregatable](./kibana-plugin-plugins-data-public.indexpatternfield.aggregatable.md) | | boolean | | -| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | Record<string, string[]> | undefined | | -| [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | number | | +| [conflictDescriptions](./kibana-plugin-plugins-data-public.indexpatternfield.conflictdescriptions.md) | | Record<string, string[]> | undefined | Description of field type conflicts across different indices in the same index pattern | +| [count](./kibana-plugin-plugins-data-public.indexpatternfield.count.md) | | number | Count is used for field popularity | | [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | string | | | [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | undefined | | | [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean | | -| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | undefined | | +| [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | undefined | Script field language | | [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string | | | [readFromDocValues](./kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md) | | boolean | | -| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | undefined | | +| [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | undefined | Script field code | | [scripted](./kibana-plugin-plugins-data-public.indexpatternfield.scripted.md) | | boolean | | | [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | boolean | | | [sortable](./kibana-plugin-plugins-data-public.indexpatternfield.sortable.md) | | boolean | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md index 27f9c797c92f2..7501e191d9363 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.script.md @@ -4,6 +4,8 @@ ## IndexPatternField.script property +Script field code + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md index 1d80c90991f55..711d6ad660450 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.tospec.md @@ -9,24 +9,7 @@ ```typescript toSpec({ getFormatterForField, }?: { getFormatterForField?: IndexPattern['getFormatterForField']; - }): { - count: number; - script: string | undefined; - lang: string | undefined; - conflictDescriptions: Record | undefined; - name: string; - type: string; - esTypes: string[] | undefined; - scripted: boolean; - searchable: boolean; - aggregatable: boolean; - readFromDocValues: boolean; - subType: import("../types").IFieldSubType | undefined; - format: { - id: any; - params: any; - } | undefined; - }; + }): FieldSpec; ``` ## Parameters @@ -37,22 +20,5 @@ toSpec({ getFormatterForField, }?: { Returns: -`{ - count: number; - script: string | undefined; - lang: string | undefined; - conflictDescriptions: Record | undefined; - name: string; - type: string; - esTypes: string[] | undefined; - scripted: boolean; - searchable: boolean; - aggregatable: boolean; - readFromDocValues: boolean; - subType: import("../types").IFieldSubType | undefined; - format: { - id: any; - params: any; - } | undefined; - }` +`FieldSpec` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fields.md new file mode 100644 index 0000000000000..386e080dbe6c2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.fields.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) + +## IndexPatternSpec.fields property + +Signature: + +```typescript +fields?: IndexPatternFieldMap; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md new file mode 100644 index 0000000000000..55eadbf36c660 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) + +## IndexPatternSpec.id property + +Signature: + +```typescript +id?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md new file mode 100644 index 0000000000000..98748661256da --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) + +## IndexPatternSpec.intervalName property + +Signature: + +```typescript +intervalName?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md new file mode 100644 index 0000000000000..74c4df126e1bf --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) + +## IndexPatternSpec interface + +Signature: + +```typescript +export interface IndexPatternSpec +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | IndexPatternFieldMap | | +| [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | | +| [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) | string | | +| [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md) | SourceFilter[] | | +| [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md) | string | | +| [title](./kibana-plugin-plugins-data-public.indexpatternspec.title.md) | string | | +| [type](./kibana-plugin-plugins-data-public.indexpatternspec.type.md) | string | | +| [typeMeta](./kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md) | TypeMeta | | +| [version](./kibana-plugin-plugins-data-public.indexpatternspec.version.md) | string | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md new file mode 100644 index 0000000000000..cda5285730135 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md) + +## IndexPatternSpec.sourceFilters property + +Signature: + +```typescript +sourceFilters?: SourceFilter[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md new file mode 100644 index 0000000000000..a527e3ac0658b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md) + +## IndexPatternSpec.timeFieldName property + +Signature: + +```typescript +timeFieldName?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.title.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.title.md new file mode 100644 index 0000000000000..4cc6d3c2524a7 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.title.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [title](./kibana-plugin-plugins-data-public.indexpatternspec.title.md) + +## IndexPatternSpec.title property + +Signature: + +```typescript +title?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.type.md new file mode 100644 index 0000000000000..d1c49be1b706f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [type](./kibana-plugin-plugins-data-public.indexpatternspec.type.md) + +## IndexPatternSpec.type property + +Signature: + +```typescript +type?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md new file mode 100644 index 0000000000000..9303047e905d3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [typeMeta](./kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md) + +## IndexPatternSpec.typeMeta property + +Signature: + +```typescript +typeMeta?: TypeMeta; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md new file mode 100644 index 0000000000000..43f7cf0226fb0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [version](./kibana-plugin-plugins-data-public.indexpatternspec.version.md) + +## IndexPatternSpec.version property + +Signature: + +```typescript +version?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice._constructor_.md new file mode 100644 index 0000000000000..ab397efb1fe0e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [(constructor)](./kibana-plugin-plugins-data-public.indexpatternsservice._constructor_.md) + +## IndexPatternsService.(constructor) + +Constructs a new instance of the `IndexPatternsService` class + +Signature: + +```typescript +constructor({ uiSettings, savedObjectsClient, apiClient, fieldFormats, onNotification, onError, onRedirectNoIndexPattern, }: IndexPatternsServiceDeps); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { uiSettings, savedObjectsClient, apiClient, fieldFormats, onNotification, onError, onRedirectNoIndexPattern, } | IndexPatternsServiceDeps | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md new file mode 100644 index 0000000000000..b371218325086 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [clearCache](./kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md) + +## IndexPatternsService.clearCache property + +Clear index pattern list cache + +Signature: + +```typescript +clearCache: (id?: string | undefined) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md new file mode 100644 index 0000000000000..d7152ba617cc6 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [create](./kibana-plugin-plugins-data-public.indexpatternsservice.create.md) + +## IndexPatternsService.create() method + +Create a new index pattern instance + +Signature: + +```typescript +create(spec: IndexPatternSpec, skipFetchFields?: boolean): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| spec | IndexPatternSpec | | +| skipFetchFields | boolean | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md new file mode 100644 index 0000000000000..eebfbb506fb77 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [createAndSave](./kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md) + +## IndexPatternsService.createAndSave() method + +Create a new index pattern and save it right away + +Signature: + +```typescript +createAndSave(spec: IndexPatternSpec, override?: boolean, skipFetchFields?: boolean): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| spec | IndexPatternSpec | | +| override | boolean | | +| skipFetchFields | boolean | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md new file mode 100644 index 0000000000000..8efb33c423b01 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [createSavedObject](./kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md) + +## IndexPatternsService.createSavedObject() method + +Save a new index pattern + +Signature: + +```typescript +createSavedObject(indexPattern: IndexPattern, override?: boolean): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| indexPattern | IndexPattern | | +| override | boolean | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.delete.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.delete.md new file mode 100644 index 0000000000000..aba31ab2c0d29 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.delete.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [delete](./kibana-plugin-plugins-data-public.indexpatternsservice.delete.md) + +## IndexPatternsService.delete() method + +Deletes an index pattern from .kibana index + +Signature: + +```typescript +delete(indexPatternId: string): Promise<{}>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| indexPatternId | string | | + +Returns: + +`Promise<{}>` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md new file mode 100644 index 0000000000000..3b6a8c7e4a04f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [ensureDefaultIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md) + +## IndexPatternsService.ensureDefaultIndexPattern property + +Signature: + +```typescript +ensureDefaultIndexPattern: EnsureDefaultIndexPattern; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md new file mode 100644 index 0000000000000..ed365fe03f980 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [fieldArrayToMap](./kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md) + +## IndexPatternsService.fieldArrayToMap property + +Converts field array to map + +Signature: + +```typescript +fieldArrayToMap: (fields: FieldSpec[]) => Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.get.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.get.md new file mode 100644 index 0000000000000..4aad6df6b413b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.get.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [get](./kibana-plugin-plugins-data-public.indexpatternsservice.get.md) + +## IndexPatternsService.get property + +Get an index pattern by id. Cache optimized + +Signature: + +```typescript +get: (id: string) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md new file mode 100644 index 0000000000000..ad2a167bd8c74 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getCache](./kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md) + +## IndexPatternsService.getCache property + +Signature: + +```typescript +getCache: () => Promise[] | null | undefined>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md new file mode 100644 index 0000000000000..01d4efeffe921 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md) + +## IndexPatternsService.getDefault property + +Get default index pattern + +Signature: + +```typescript +getDefault: () => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md new file mode 100644 index 0000000000000..c06c3c6f68492 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getFieldsForIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md) + +## IndexPatternsService.getFieldsForIndexPattern property + +Get field list by providing an index patttern (or spec) + +Signature: + +```typescript +getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md new file mode 100644 index 0000000000000..aec84866b9e58 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getFieldsForWildcard](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md) + +## IndexPatternsService.getFieldsForWildcard property + +Get field list by providing { pattern } + +Signature: + +```typescript +getFieldsForWildcard: (options?: GetFieldsOptions) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getids.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getids.md new file mode 100644 index 0000000000000..a012e0dc9d9c5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getids.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getIds](./kibana-plugin-plugins-data-public.indexpatternsservice.getids.md) + +## IndexPatternsService.getIds property + +Get list of index pattern ids + +Signature: + +```typescript +getIds: (refresh?: boolean) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md new file mode 100644 index 0000000000000..7d29ced66afa8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getIdsWithTitle](./kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md) + +## IndexPatternsService.getIdsWithTitle property + +Get list of index pattern ids with titles + +Signature: + +```typescript +getIdsWithTitle: (refresh?: boolean) => Promise>; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md new file mode 100644 index 0000000000000..04cc294a79dfc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [getTitles](./kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md) + +## IndexPatternsService.getTitles property + +Get list of index pattern titles + +Signature: + +```typescript +getTitles: (refresh?: boolean) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md new file mode 100644 index 0000000000000..34df8656e9175 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md @@ -0,0 +1,47 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) + +## IndexPatternsService class + +Signature: + +```typescript +export declare class IndexPatternsService +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)({ uiSettings, savedObjectsClient, apiClient, fieldFormats, onNotification, onError, onRedirectNoIndexPattern, })](./kibana-plugin-plugins-data-public.indexpatternsservice._constructor_.md) | | Constructs a new instance of the IndexPatternsService class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [clearCache](./kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md) | | (id?: string | undefined) => void | Clear index pattern list cache | +| [ensureDefaultIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md) | | EnsureDefaultIndexPattern | | +| [fieldArrayToMap](./kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md) | | (fields: FieldSpec[]) => Record<string, FieldSpec> | Converts field array to map | +| [get](./kibana-plugin-plugins-data-public.indexpatternsservice.get.md) | | (id: string) => Promise<IndexPattern> | Get an index pattern by id. Cache optimized | +| [getCache](./kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md) | | () => Promise<SavedObject<IndexPatternSavedObjectAttrs>[] | null | undefined> | | +| [getDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md) | | () => Promise<IndexPattern | null> | Get default index pattern | +| [getFieldsForIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforindexpattern.md) | | (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise<any> | Get field list by providing an index patttern (or spec) | +| [getFieldsForWildcard](./kibana-plugin-plugins-data-public.indexpatternsservice.getfieldsforwildcard.md) | | (options?: GetFieldsOptions) => Promise<any> | Get field list by providing { pattern } | +| [getIds](./kibana-plugin-plugins-data-public.indexpatternsservice.getids.md) | | (refresh?: boolean) => Promise<string[]> | Get list of index pattern ids | +| [getIdsWithTitle](./kibana-plugin-plugins-data-public.indexpatternsservice.getidswithtitle.md) | | (refresh?: boolean) => Promise<Array<{
id: string;
title: string;
}>> | Get list of index pattern ids with titles | +| [getTitles](./kibana-plugin-plugins-data-public.indexpatternsservice.gettitles.md) | | (refresh?: boolean) => Promise<string[]> | Get list of index pattern titles | +| [refreshFields](./kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md) | | (indexPattern: IndexPattern) => Promise<void> | Refresh field list for a given index pattern | +| [savedObjectToSpec](./kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md) | | (savedObject: SavedObject<IndexPatternAttributes>) => IndexPatternSpec | Converts index pattern saved object to index pattern spec | +| [setDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.setdefault.md) | | (id: string, force?: boolean) => Promise<void> | Optionally set default index pattern, unless force = true | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [create(spec, skipFetchFields)](./kibana-plugin-plugins-data-public.indexpatternsservice.create.md) | | Create a new index pattern instance | +| [createAndSave(spec, override, skipFetchFields)](./kibana-plugin-plugins-data-public.indexpatternsservice.createandsave.md) | | Create a new index pattern and save it right away | +| [createSavedObject(indexPattern, override)](./kibana-plugin-plugins-data-public.indexpatternsservice.createsavedobject.md) | | Save a new index pattern | +| [delete(indexPatternId)](./kibana-plugin-plugins-data-public.indexpatternsservice.delete.md) | | Deletes an index pattern from .kibana index | +| [updateSavedObject(indexPattern, saveAttempts, ignoreErrors)](./kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md) | | Save existing index pattern. Will attempt to merge differences if there are conflicts | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md new file mode 100644 index 0000000000000..b7c47efbb445a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [refreshFields](./kibana-plugin-plugins-data-public.indexpatternsservice.refreshfields.md) + +## IndexPatternsService.refreshFields property + +Refresh field list for a given index pattern + +Signature: + +```typescript +refreshFields: (indexPattern: IndexPattern) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md new file mode 100644 index 0000000000000..7bd40c9cafd42 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [savedObjectToSpec](./kibana-plugin-plugins-data-public.indexpatternsservice.savedobjecttospec.md) + +## IndexPatternsService.savedObjectToSpec property + +Converts index pattern saved object to index pattern spec + +Signature: + +```typescript +savedObjectToSpec: (savedObject: SavedObject) => IndexPatternSpec; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.setdefault.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.setdefault.md new file mode 100644 index 0000000000000..2bf8eaa03d1ae --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.setdefault.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [setDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.setdefault.md) + +## IndexPatternsService.setDefault property + +Optionally set default index pattern, unless force = true + +Signature: + +```typescript +setDefault: (id: string, force?: boolean) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md new file mode 100644 index 0000000000000..5fc16c70de7ed --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) > [updateSavedObject](./kibana-plugin-plugins-data-public.indexpatternsservice.updatesavedobject.md) + +## IndexPatternsService.updateSavedObject() method + +Save existing index pattern. Will attempt to merge differences if there are conflicts + +Signature: + +```typescript +updateSavedObject(indexPattern: IndexPattern, saveAttempts?: number, ignoreErrors?: boolean): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| indexPattern | IndexPattern | | +| saveAttempts | number | | +| ignoreErrors | boolean | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iscompleteresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iscompleteresponse.md new file mode 100644 index 0000000000000..e17e453ecb749 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iscompleteresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [isCompleteResponse](./kibana-plugin-plugins-data-public.iscompleteresponse.md) + +## isCompleteResponse variable + +Signature: + +```typescript +isCompleteResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md index 861b59e73ef04..025ca6681d39b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md @@ -7,5 +7,5 @@ Signature: ```typescript -export declare type ISearchGeneric = (request: SearchStrategyRequest, options?: ISearchOptions) => Observable; +export declare type ISearchGeneric = (request: SearchStrategyRequest, options?: ISearchOptions) => Observable; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md index cee213fc6e7e3..5defe4a647614 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -19,4 +19,5 @@ export interface ISearchStart | [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | | [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | +| [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: Error) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md new file mode 100644 index 0000000000000..fb14057d83d5c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.showerror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) + +## ISearchStart.showError property + +Signature: + +```typescript +showError: (e: Error) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iserrorresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iserrorresponse.md new file mode 100644 index 0000000000000..e4ac35f19e959 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iserrorresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [isErrorResponse](./kibana-plugin-plugins-data-public.iserrorresponse.md) + +## isErrorResponse variable + +Signature: + +```typescript +isErrorResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ispartialresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ispartialresponse.md new file mode 100644 index 0000000000000..4b707ceeacc89 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ispartialresponse.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [isPartialResponse](./kibana-plugin-plugins-data-public.ispartialresponse.md) + +## isPartialResponse variable + +Signature: + +```typescript +isPartialResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index f51549c81fb62..e5f56a1ec387f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -11,16 +11,19 @@ | [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) | | | [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) | | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | +| [DuplicateIndexPatternError](./kibana-plugin-plugins-data-public.duplicateindexpatternerror.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | | [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) | | | [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) | | | [IndexPatternSelect](./kibana-plugin-plugins-data-public.indexpatternselect.md) | | +| [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) | | | [OptionedParamType](./kibana-plugin-plugins-data-public.optionedparamtype.md) | | +| [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) | | | [Plugin](./kibana-plugin-plugins-data-public.plugin.md) | | -| [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) | Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. | | [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) | | | [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | \* | +| [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) | Request Failure - When an entire multi request fails | | [TimeHistory](./kibana-plugin-plugins-data-public.timehistory.md) | | ## Enumerations @@ -33,6 +36,7 @@ | [METRIC\_TYPES](./kibana-plugin-plugins-data-public.metric_types.md) | | | [QuerySuggestionTypes](./kibana-plugin-plugins-data-public.querysuggestiontypes.md) | | | [SortDirection](./kibana-plugin-plugins-data-public.sortdirection.md) | | +| [TimeoutErrorMode](./kibana-plugin-plugins-data-public.timeouterrormode.md) | | ## Functions @@ -57,17 +61,16 @@ | [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | | [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) | | -| [Filter](./kibana-plugin-plugins-data-public.filter.md) | | | [IDataPluginServices](./kibana-plugin-plugins-data-public.idatapluginservices.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | | -| [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | | | [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | | | [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) | | | [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) | | | [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) | | -| [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | Use data plugin interface instead | +| [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | | +| [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) | | | [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) | | | [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) | The setup contract exposed by the Search plugin exposes the search strategy extension point. | @@ -75,9 +78,9 @@ | [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | high level search service | | [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | -| [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryState](./kibana-plugin-plugins-data-public.querystate.md) | All query state service state | | [QueryStateChange](./kibana-plugin-plugins-data-public.querystatechange.md) | | +| [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) | | | [QuerySuggestionBasic](./kibana-plugin-plugins-data-public.querysuggestionbasic.md) | \* | | [QuerySuggestionField](./kibana-plugin-plugins-data-public.querysuggestionfield.md) | \* | | [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) | \* | @@ -90,7 +93,6 @@ | [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | search source fields | | [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* | -| [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | ## Variables @@ -115,8 +117,11 @@ | [getKbnTypeNames](./kibana-plugin-plugins-data-public.getkbntypenames.md) | Get the esTypes known by all kbnFieldTypes {Array} | | [indexPatterns](./kibana-plugin-plugins-data-public.indexpatterns.md) | | | [injectSearchSourceReferences](./kibana-plugin-plugins-data-public.injectsearchsourcereferences.md) | | +| [isCompleteResponse](./kibana-plugin-plugins-data-public.iscompleteresponse.md) | | +| [isErrorResponse](./kibana-plugin-plugins-data-public.iserrorresponse.md) | | | [isFilter](./kibana-plugin-plugins-data-public.isfilter.md) | | | [isFilters](./kibana-plugin-plugins-data-public.isfilters.md) | | +| [isPartialResponse](./kibana-plugin-plugins-data-public.ispartialresponse.md) | | | [isQuery](./kibana-plugin-plugins-data-public.isquery.md) | | | [isTimeRange](./kibana-plugin-plugins-data-public.istimerange.md) | | | [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md) | | @@ -145,8 +150,10 @@ | [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) | \* | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) | | | [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | | +| [Filter](./kibana-plugin-plugins-data-public.filter.md) | | | [IAggConfig](./kibana-plugin-plugins-data-public.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-public.iaggtype.md) | | +| [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) | | | [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-public.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-public.ifieldparamtype.md) | | @@ -162,6 +169,7 @@ | [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | | | [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | | | [PhrasesFilter](./kibana-plugin-plugins-data-public.phrasesfilter.md) | | +| [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | | | [QuerySuggestion](./kibana-plugin-plugins-data-public.querysuggestion.md) | \* | | [QuerySuggestionGetFn](./kibana-plugin-plugins-data-public.querysuggestiongetfn.md) | | @@ -173,4 +181,5 @@ | [TabbedAggRow](./kibana-plugin-plugins-data-public.tabbedaggrow.md) | \* | | [TimefilterContract](./kibana-plugin-plugins-data-public.timefiltercontract.md) | | | [TimeHistoryContract](./kibana-plugin-plugins-data-public.timehistorycontract.md) | | +| [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md new file mode 100644 index 0000000000000..f8966572afbb6 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [(constructor)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) + +## PainlessError.(constructor) + +Constructs a new instance of the `PainlessError` class + +Signature: + +```typescript +constructor(err: EsError, request: IKibanaSearchRequest); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| err | EsError | | +| request | IKibanaSearchRequest | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md new file mode 100644 index 0000000000000..a3b4c51c6c331 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [getErrorMessage](./kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md) + +## PainlessError.getErrorMessage() method + +Signature: + +```typescript +getErrorMessage(application: ApplicationStart): JSX.Element; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| application | ApplicationStart | | + +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md new file mode 100644 index 0000000000000..306211cd60259 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) + +## PainlessError class + +Signature: + +```typescript +export declare class PainlessError extends KbnError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(err, request)](./kibana-plugin-plugins-data-public.painlesserror._constructor_.md) | | Constructs a new instance of the PainlessError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) | | string | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getErrorMessage(application)](./kibana-plugin-plugins-data-public.painlesserror.geterrormessage.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md new file mode 100644 index 0000000000000..a7e6920b2ae21 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.painlesserror.painlessstack.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) > [painlessStack](./kibana-plugin-plugins-data-public.painlesserror.painlessstack.md) + +## PainlessError.painlessStack property + +Signature: + +```typescript +painlessStack?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md index e85747b8cc3d7..aa7c3bb5d4932 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md @@ -7,5 +7,5 @@ Signature: ```typescript -QueryStringInput: React.FC> +QueryStringInput: React.FC ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md new file mode 100644 index 0000000000000..5a41852001ac0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [bubbleSubmitEvent](./kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md) + +## QueryStringInputProps.bubbleSubmitEvent property + +Signature: + +```typescript +bubbleSubmitEvent?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md new file mode 100644 index 0000000000000..7fa3b76977183 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.classname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [className](./kibana-plugin-plugins-data-public.querystringinputprops.classname.md) + +## QueryStringInputProps.className property + +Signature: + +```typescript +className?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md new file mode 100644 index 0000000000000..edaedf49f4b10 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) + +## QueryStringInputProps.dataTestSubj property + +Signature: + +```typescript +dataTestSubj?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md new file mode 100644 index 0000000000000..cc4c6f606409e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) + +## QueryStringInputProps.disableAutoFocus property + +Signature: + +```typescript +disableAutoFocus?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md new file mode 100644 index 0000000000000..3783138696020 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) + +## QueryStringInputProps.indexPatterns property + +Signature: + +```typescript +indexPatterns: Array; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md new file mode 100644 index 0000000000000..a282ac3bc5049 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) + +## QueryStringInputProps.isInvalid property + +Signature: + +```typescript +isInvalid?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md new file mode 100644 index 0000000000000..d133a0930b53d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) + +## QueryStringInputProps.languageSwitcherPopoverAnchorPosition property + +Signature: + +```typescript +languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md new file mode 100644 index 0000000000000..d503980da7947 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.md @@ -0,0 +1,34 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) + +## QueryStringInputProps interface + +Signature: + +```typescript +export interface QueryStringInputProps +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [bubbleSubmitEvent](./kibana-plugin-plugins-data-public.querystringinputprops.bubblesubmitevent.md) | boolean | | +| [className](./kibana-plugin-plugins-data-public.querystringinputprops.classname.md) | string | | +| [dataTestSubj](./kibana-plugin-plugins-data-public.querystringinputprops.datatestsubj.md) | string | | +| [disableAutoFocus](./kibana-plugin-plugins-data-public.querystringinputprops.disableautofocus.md) | boolean | | +| [indexPatterns](./kibana-plugin-plugins-data-public.querystringinputprops.indexpatterns.md) | Array<IIndexPattern | string> | | +| [isInvalid](./kibana-plugin-plugins-data-public.querystringinputprops.isinvalid.md) | boolean | | +| [languageSwitcherPopoverAnchorPosition](./kibana-plugin-plugins-data-public.querystringinputprops.languageswitcherpopoveranchorposition.md) | PopoverAnchorPosition | | +| [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) | () => void | | +| [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) | (query: Query) => void | | +| [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) | (isFocused: boolean) => void | | +| [onSubmit](./kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md) | (query: Query) => void | | +| [persistedLog](./kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md) | PersistedLog | | +| [placeholder](./kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md) | string | | +| [prepend](./kibana-plugin-plugins-data-public.querystringinputprops.prepend.md) | any | | +| [query](./kibana-plugin-plugins-data-public.querystringinputprops.query.md) | Query | | +| [screenTitle](./kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md) | string | | +| [size](./kibana-plugin-plugins-data-public.querystringinputprops.size.md) | SuggestionsListSize | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md new file mode 100644 index 0000000000000..10f2ae2ea4f14 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onblur.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onBlur](./kibana-plugin-plugins-data-public.querystringinputprops.onblur.md) + +## QueryStringInputProps.onBlur property + +Signature: + +```typescript +onBlur?: () => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md new file mode 100644 index 0000000000000..fee44d7afd506 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchange.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onChange](./kibana-plugin-plugins-data-public.querystringinputprops.onchange.md) + +## QueryStringInputProps.onChange property + +Signature: + +```typescript +onChange?: (query: Query) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md new file mode 100644 index 0000000000000..0421ae9c8bac5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onChangeQueryInputFocus](./kibana-plugin-plugins-data-public.querystringinputprops.onchangequeryinputfocus.md) + +## QueryStringInputProps.onChangeQueryInputFocus property + +Signature: + +```typescript +onChangeQueryInputFocus?: (isFocused: boolean) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md new file mode 100644 index 0000000000000..951ec7419485f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [onSubmit](./kibana-plugin-plugins-data-public.querystringinputprops.onsubmit.md) + +## QueryStringInputProps.onSubmit property + +Signature: + +```typescript +onSubmit?: (query: Query) => void; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md new file mode 100644 index 0000000000000..d1a8efb364016 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [persistedLog](./kibana-plugin-plugins-data-public.querystringinputprops.persistedlog.md) + +## QueryStringInputProps.persistedLog property + +Signature: + +```typescript +persistedLog?: PersistedLog; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md new file mode 100644 index 0000000000000..31e41f4d55205 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [placeholder](./kibana-plugin-plugins-data-public.querystringinputprops.placeholder.md) + +## QueryStringInputProps.placeholder property + +Signature: + +```typescript +placeholder?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md new file mode 100644 index 0000000000000..7be882058d3fd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.prepend.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [prepend](./kibana-plugin-plugins-data-public.querystringinputprops.prepend.md) + +## QueryStringInputProps.prepend property + +Signature: + +```typescript +prepend?: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md new file mode 100644 index 0000000000000..f15f6d082332b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.query.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [query](./kibana-plugin-plugins-data-public.querystringinputprops.query.md) + +## QueryStringInputProps.query property + +Signature: + +```typescript +query: Query; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md new file mode 100644 index 0000000000000..0c80252d74571 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [screenTitle](./kibana-plugin-plugins-data-public.querystringinputprops.screentitle.md) + +## QueryStringInputProps.screenTitle property + +Signature: + +```typescript +screenTitle?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md new file mode 100644 index 0000000000000..6b0e53a23e07b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinputprops.size.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStringInputProps](./kibana-plugin-plugins-data-public.querystringinputprops.md) > [size](./kibana-plugin-plugins-data-public.querystringinputprops.size.md) + +## QueryStringInputProps.size property + +Signature: + +```typescript +size?: SuggestionsListSize; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md deleted file mode 100644 index 25e472817b46d..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) > [(constructor)](./kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md) - -## RequestTimeoutError.(constructor) - -Constructs a new instance of the `RequestTimeoutError` class - -Signature: - -```typescript -constructor(message?: string); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| message | string | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md deleted file mode 100644 index 84b2fc3fe0b17..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.requesttimeouterror.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) - -## RequestTimeoutError class - -Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. - -Signature: - -```typescript -export declare class RequestTimeoutError extends Error -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(message)](./kibana-plugin-plugins-data-public.requesttimeouterror._constructor_.md) | | Constructs a new instance of the RequestTimeoutError class | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md new file mode 100644 index 0000000000000..8ecd8b8c5ac22 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [getTimeoutMode](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) + +## SearchInterceptor.getTimeoutMode() method + +Signature: + +```typescript +protected getTimeoutMode(): TimeoutErrorMode; +``` +Returns: + +`TimeoutErrorMode` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md new file mode 100644 index 0000000000000..02db74b1a9e91 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [handleSearchError](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) + +## SearchInterceptor.handleSearchError() method + +Signature: + +```typescript +protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| e | any | | +| request | IKibanaSearchRequest | | +| timeoutSignal | AbortSignal | | +| appAbortSignal | AbortSignal | | + +Returns: + +`Error` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index 5cee345db6cd2..a02a6116d7ae0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -21,11 +21,13 @@ export declare class SearchInterceptor | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md) | | SearchInterceptorDeps | | -| [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) | | ((e: Error) => void) & import("lodash").Cancelable | | ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | +| [handleSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | +| [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md index 1752d183a8737..672ff5065c456 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.search.md @@ -9,17 +9,19 @@ Searches using the given `search` method. Overrides the `AbortSignal` with one t Signature: ```typescript -search(request: IEsSearchRequest, options?: ISearchOptions): Observable; +search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| request | IEsSearchRequest | | +| request | IKibanaSearchRequest | | | options | ISearchOptions | | Returns: -`Observable` +`Observable` + +`Observalbe` emitting the search response or an error. diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md new file mode 100644 index 0000000000000..92e851c783dd0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showerror.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [showError](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) + +## SearchInterceptor.showError() method + +Signature: + +```typescript +showError(e: Error): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| e | Error | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md deleted file mode 100644 index 91ecb2821acbf..0000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) - -## SearchInterceptor.showTimeoutError property - -Signature: - -```typescript -protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md new file mode 100644 index 0000000000000..1c6370c7d0356 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [(constructor)](./kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md) + +## SearchTimeoutError.(constructor) + +Constructs a new instance of the `SearchTimeoutError` class + +Signature: + +```typescript +constructor(err: Error, mode: TimeoutErrorMode); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| err | Error | | +| mode | TimeoutErrorMode | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md new file mode 100644 index 0000000000000..58ef953c9d7db --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [getErrorMessage](./kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md) + +## SearchTimeoutError.getErrorMessage() method + +Signature: + +```typescript +getErrorMessage(application: ApplicationStart): JSX.Element; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| application | ApplicationStart | | + +Returns: + +`JSX.Element` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md new file mode 100644 index 0000000000000..5c0bec04dcfbc --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) + +## SearchTimeoutError class + +Request Failure - When an entire multi request fails + +Signature: + +```typescript +export declare class SearchTimeoutError extends KbnError +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(err, mode)](./kibana-plugin-plugins-data-public.searchtimeouterror._constructor_.md) | | Constructs a new instance of the SearchTimeoutError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [mode](./kibana-plugin-plugins-data-public.searchtimeouterror.mode.md) | | TimeoutErrorMode | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [getErrorMessage(application)](./kibana-plugin-plugins-data-public.searchtimeouterror.geterrormessage.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md new file mode 100644 index 0000000000000..d534a73eca2ec --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchtimeouterror.mode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) > [mode](./kibana-plugin-plugins-data-public.searchtimeouterror.mode.md) + +## SearchTimeoutError.mode property + +Signature: + +```typescript +mode: TimeoutErrorMode; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md new file mode 100644 index 0000000000000..8ad63e2c1e9b4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.timeouterrormode.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [TimeoutErrorMode](./kibana-plugin-plugins-data-public.timeouterrormode.md) + +## TimeoutErrorMode enum + +Signature: + +```typescript +export declare enum TimeoutErrorMode +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| CHANGE | 2 | | +| CONTACT | 1 | | +| UPGRADE | 0 | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.aggregatable.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.aggregatable.md new file mode 100644 index 0000000000000..2889ee34ad77b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.aggregatable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [aggregatable](./kibana-plugin-plugins-data-server.fielddescriptor.aggregatable.md) + +## FieldDescriptor.aggregatable property + +Signature: + +```typescript +aggregatable: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.estypes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.estypes.md new file mode 100644 index 0000000000000..9caa374d8da48 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.estypes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [esTypes](./kibana-plugin-plugins-data-server.fielddescriptor.estypes.md) + +## FieldDescriptor.esTypes property + +Signature: + +```typescript +esTypes: string[]; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.md new file mode 100644 index 0000000000000..693de675da940 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) + +## FieldDescriptor interface + +Signature: + +```typescript +export interface FieldDescriptor +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aggregatable](./kibana-plugin-plugins-data-server.fielddescriptor.aggregatable.md) | boolean | | +| [esTypes](./kibana-plugin-plugins-data-server.fielddescriptor.estypes.md) | string[] | | +| [name](./kibana-plugin-plugins-data-server.fielddescriptor.name.md) | string | | +| [readFromDocValues](./kibana-plugin-plugins-data-server.fielddescriptor.readfromdocvalues.md) | boolean | | +| [searchable](./kibana-plugin-plugins-data-server.fielddescriptor.searchable.md) | boolean | | +| [subType](./kibana-plugin-plugins-data-server.fielddescriptor.subtype.md) | FieldSubType | | +| [type](./kibana-plugin-plugins-data-server.fielddescriptor.type.md) | string | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.name.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.name.md new file mode 100644 index 0000000000000..178880a34cd4d --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [name](./kibana-plugin-plugins-data-server.fielddescriptor.name.md) + +## FieldDescriptor.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.readfromdocvalues.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.readfromdocvalues.md new file mode 100644 index 0000000000000..b60dc5d0dfed0 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.readfromdocvalues.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [readFromDocValues](./kibana-plugin-plugins-data-server.fielddescriptor.readfromdocvalues.md) + +## FieldDescriptor.readFromDocValues property + +Signature: + +```typescript +readFromDocValues: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.searchable.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.searchable.md new file mode 100644 index 0000000000000..efc7b4219a355 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.searchable.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [searchable](./kibana-plugin-plugins-data-server.fielddescriptor.searchable.md) + +## FieldDescriptor.searchable property + +Signature: + +```typescript +searchable: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.subtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.subtype.md new file mode 100644 index 0000000000000..b08179f12f250 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.subtype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [subType](./kibana-plugin-plugins-data-server.fielddescriptor.subtype.md) + +## FieldDescriptor.subType property + +Signature: + +```typescript +subType?: FieldSubType; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.type.md new file mode 100644 index 0000000000000..7b0513a60c90e --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.fielddescriptor.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) > [type](./kibana-plugin-plugins-data-server.fielddescriptor.type.md) + +## FieldDescriptor.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md index 0dfa23eb64c1b..9141bcdd2e8d7 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md @@ -7,7 +7,7 @@ Signature: ```typescript -export interface IEsSearchRequest extends IKibanaSearchRequest +export interface IEsSearchRequest extends IKibanaSearchRequest ``` ## Properties @@ -15,5 +15,4 @@ export interface IEsSearchRequest extends IKibanaSearchRequest | Property | Type | Description | | --- | --- | --- | | [indexType](./kibana-plugin-plugins-data-server.iessearchrequest.indextype.md) | string | | -| [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md) | ISearchRequestParams | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md deleted file mode 100644 index d65281973c951..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) > [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md) - -## IEsSearchRequest.params property - -Signature: - -```typescript -params?: ISearchRequestParams; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md deleted file mode 100644 index fbddfc1cd9fc4..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md) - -## IEsSearchResponse.isPartial property - -Indicates whether the results returned are complete or partial - -Signature: - -```typescript -isPartial?: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md deleted file mode 100644 index 01f3982957d5c..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md) - -## IEsSearchResponse.isRunning property - -Indicates whether async search is still in flight - -Signature: - -```typescript -isRunning?: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md index 55c0399e90e2f..d333af1b278c2 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md @@ -2,19 +2,10 @@ [Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) -## IEsSearchResponse interface +## IEsSearchResponse type Signature: ```typescript -export interface IEsSearchResponse extends IKibanaSearchResponse +export declare type IEsSearchResponse = IKibanaSearchResponse>; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md) | boolean | Indicates whether the results returned are complete or partial | -| [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md) | boolean | Indicates whether async search is still in flight | -| [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md) | SearchResponse<Source> | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md deleted file mode 100644 index 9987debfa551c..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md) - -## IEsSearchResponse.rawResponse property - -Signature: - -```typescript -rawResponse: SearchResponse; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fieldformatmap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fieldformatmap.md deleted file mode 100644 index ab9e3171d7d7b..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fieldformatmap.md +++ /dev/null @@ -1,14 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [fieldFormatMap](./kibana-plugin-plugins-data-server.iindexpattern.fieldformatmap.md) - -## IIndexPattern.fieldFormatMap property - -Signature: - -```typescript -fieldFormatMap?: Record; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fields.md deleted file mode 100644 index fb6d046ff2174..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.fields.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [fields](./kibana-plugin-plugins-data-server.iindexpattern.fields.md) - -## IIndexPattern.fields property - -Signature: - -```typescript -fields: IFieldType[]; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md deleted file mode 100644 index a4d6abcf86a94..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md +++ /dev/null @@ -1,15 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [getTimeField](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md) - -## IIndexPattern.getTimeField() method - -Signature: - -```typescript -getTimeField?(): IFieldType | undefined; -``` -Returns: - -`IFieldType | undefined` - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.id.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.id.md deleted file mode 100644 index cac263df0f9aa..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [id](./kibana-plugin-plugins-data-server.iindexpattern.id.md) - -## IIndexPattern.id property - -Signature: - -```typescript -id?: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md deleted file mode 100644 index a79244a24acf5..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.md +++ /dev/null @@ -1,29 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) - -## IIndexPattern interface - -Signature: - -```typescript -export interface IIndexPattern -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [fieldFormatMap](./kibana-plugin-plugins-data-server.iindexpattern.fieldformatmap.md) | Record<string, {
id: string;
params: unknown;
}> | | -| [fields](./kibana-plugin-plugins-data-server.iindexpattern.fields.md) | IFieldType[] | | -| [id](./kibana-plugin-plugins-data-server.iindexpattern.id.md) | string | | -| [timeFieldName](./kibana-plugin-plugins-data-server.iindexpattern.timefieldname.md) | string | | -| [title](./kibana-plugin-plugins-data-server.iindexpattern.title.md) | string | | -| [type](./kibana-plugin-plugins-data-server.iindexpattern.type.md) | string | | - -## Methods - -| Method | Description | -| --- | --- | -| [getTimeField()](./kibana-plugin-plugins-data-server.iindexpattern.gettimefield.md) | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.timefieldname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.timefieldname.md deleted file mode 100644 index 14cf514477da4..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.timefieldname.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [timeFieldName](./kibana-plugin-plugins-data-server.iindexpattern.timefieldname.md) - -## IIndexPattern.timeFieldName property - -Signature: - -```typescript -timeFieldName?: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.title.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.title.md deleted file mode 100644 index 119963d7ff95d..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.title.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [title](./kibana-plugin-plugins-data-server.iindexpattern.title.md) - -## IIndexPattern.title property - -Signature: - -```typescript -title: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.type.md deleted file mode 100644 index 6b89b71664b23..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iindexpattern.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) > [type](./kibana-plugin-plugins-data-server.iindexpattern.type.md) - -## IIndexPattern.type property - -Signature: - -```typescript -type?: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern._constructor_.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern._constructor_.md new file mode 100644 index 0000000000000..22ee6f15933ea --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [(constructor)](./kibana-plugin-plugins-data-server.indexpattern._constructor_.md) + +## IndexPattern.(constructor) + +Constructs a new instance of the `IndexPattern` class + +Signature: + +```typescript +constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { spec, fieldFormats, shortDotsEnable, metaFields, } | IndexPatternDeps | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md new file mode 100644 index 0000000000000..a86fea3106225 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [addScriptedField](./kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md) + +## IndexPattern.addScriptedField() method + +Add scripted field to field list + +Signature: + +```typescript +addScriptedField(name: string, script: string, fieldType?: string): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | +| script | string | | +| fieldType | string | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md new file mode 100644 index 0000000000000..2f686bd313d58 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md) + +## IndexPattern.fieldFormatMap property + +Signature: + +```typescript +fieldFormatMap: Record; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fields.md new file mode 100644 index 0000000000000..5b22014486c02 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fields.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [fields](./kibana-plugin-plugins-data-server.indexpattern.fields.md) + +## IndexPattern.fields property + +Signature: + +```typescript +fields: IIndexPatternFieldList & { + toSpec: () => IndexPatternFieldMap; + }; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.flattenhit.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.flattenhit.md new file mode 100644 index 0000000000000..33c6dedc6dcd8 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.flattenhit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [flattenHit](./kibana-plugin-plugins-data-server.indexpattern.flattenhit.md) + +## IndexPattern.flattenHit property + +Signature: + +```typescript +flattenHit: (hit: Record, deep?: boolean) => Record; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formatfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formatfield.md new file mode 100644 index 0000000000000..07db8a0805b07 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formatfield.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [formatField](./kibana-plugin-plugins-data-server.indexpattern.formatfield.md) + +## IndexPattern.formatField property + +Signature: + +```typescript +formatField: FormatFieldFn; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formathit.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formathit.md new file mode 100644 index 0000000000000..75f282a8991fc --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.formathit.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [formatHit](./kibana-plugin-plugins-data-server.indexpattern.formathit.md) + +## IndexPattern.formatHit property + +Signature: + +```typescript +formatHit: { + (hit: Record, type?: string): any; + formatField: FormatFieldFn; + }; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md new file mode 100644 index 0000000000000..b655e779e4fa4 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getAggregationRestrictions](./kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md) + +## IndexPattern.getAggregationRestrictions() method + +Signature: + +```typescript +getAggregationRestrictions(): Record> | undefined; +``` +Returns: + +`Record> | undefined` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md new file mode 100644 index 0000000000000..f1bdb2f729414 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getAsSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md) + +## IndexPattern.getAsSavedObjectBody() method + +Returns index pattern as saved object body for saving + +Signature: + +```typescript +getAsSavedObjectBody(): { + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }; +``` +Returns: + +`{ + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md new file mode 100644 index 0000000000000..eab6ae9bf9033 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getComputedFields](./kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md) + +## IndexPattern.getComputedFields() method + +Signature: + +```typescript +getComputedFields(): { + storedFields: string[]; + scriptFields: any; + docvalueFields: { + field: any; + format: string; + }[]; + }; +``` +Returns: + +`{ + storedFields: string[]; + scriptFields: any; + docvalueFields: { + field: any; + format: string; + }[]; + }` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md new file mode 100644 index 0000000000000..712be3b72828a --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getFieldByName](./kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md) + +## IndexPattern.getFieldByName() method + +Signature: + +```typescript +getFieldByName(name: string): IndexPatternField | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`IndexPatternField | undefined` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md new file mode 100644 index 0000000000000..7dc2756009f4e --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getFormatterForField](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) + +## IndexPattern.getFormatterForField() method + +Provide a field, get its formatter + +Signature: + +```typescript +getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | IndexPatternField | IndexPatternField['spec'] | IFieldType | | + +Returns: + +`FieldFormat` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md new file mode 100644 index 0000000000000..89d79d9b750fa --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getNonScriptedFields](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) + +## IndexPattern.getNonScriptedFields() method + +Signature: + +```typescript +getNonScriptedFields(): IndexPatternField[]; +``` +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md new file mode 100644 index 0000000000000..324f9d0152ab5 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md) + +## IndexPattern.getOriginalSavedObjectBody property + +Get last saved saved object fields + +Signature: + +```typescript +getOriginalSavedObjectBody: () => { + title?: string | undefined; + timeFieldName?: string | undefined; + intervalName?: string | undefined; + fields?: string | undefined; + sourceFilters?: string | undefined; + fieldFormatMap?: string | undefined; + typeMeta?: string | undefined; + type?: string | undefined; + }; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md new file mode 100644 index 0000000000000..edfff8ec5efac --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getScriptedFields](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) + +## IndexPattern.getScriptedFields() method + +Signature: + +```typescript +getScriptedFields(): IndexPatternField[]; +``` +Returns: + +`IndexPatternField[]` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md new file mode 100644 index 0000000000000..240f9b4fb0aa2 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getSourceFiltering](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) + +## IndexPattern.getSourceFiltering() method + +Get the source filtering configuration for that index. + +Signature: + +```typescript +getSourceFiltering(): { + excludes: any[]; + }; +``` +Returns: + +`{ + excludes: any[]; + }` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.gettimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.gettimefield.md new file mode 100644 index 0000000000000..b5806f883fb9f --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.gettimefield.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [getTimeField](./kibana-plugin-plugins-data-server.indexpattern.gettimefield.md) + +## IndexPattern.getTimeField() method + +Signature: + +```typescript +getTimeField(): IndexPatternField | undefined; +``` +Returns: + +`IndexPatternField | undefined` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.id.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.id.md new file mode 100644 index 0000000000000..8fad82bd06705 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [id](./kibana-plugin-plugins-data-server.indexpattern.id.md) + +## IndexPattern.id property + +Signature: + +```typescript +id?: string; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md new file mode 100644 index 0000000000000..caaa6929235f8 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [intervalName](./kibana-plugin-plugins-data-server.indexpattern.intervalname.md) + +## IndexPattern.intervalName property + +Signature: + +```typescript +intervalName: string | undefined; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimebased.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimebased.md new file mode 100644 index 0000000000000..790744979942d --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimebased.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [isTimeBased](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) + +## IndexPattern.isTimeBased() method + +Signature: + +```typescript +isTimeBased(): boolean; +``` +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md new file mode 100644 index 0000000000000..22fb60eba4f6e --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [isTimeNanosBased](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) + +## IndexPattern.isTimeNanosBased() method + +Signature: + +```typescript +isTimeNanosBased(): boolean; +``` +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md new file mode 100644 index 0000000000000..603864234d34b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -0,0 +1,58 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) + +## IndexPattern class + +Signature: + +```typescript +export declare class IndexPattern implements IIndexPattern +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)({ spec, fieldFormats, shortDotsEnable, metaFields, })](./kibana-plugin-plugins-data-server.indexpattern._constructor_.md) | | Constructs a new instance of the IndexPattern class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md) | | Record<string, any> | | +| [fields](./kibana-plugin-plugins-data-server.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | +| [flattenHit](./kibana-plugin-plugins-data-server.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | +| [formatField](./kibana-plugin-plugins-data-server.indexpattern.formatfield.md) | | FormatFieldFn | | +| [formatHit](./kibana-plugin-plugins-data-server.indexpattern.formathit.md) | | {
(hit: Record<string, any>, type?: string): any;
formatField: FormatFieldFn;
} | | +| [getOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.getoriginalsavedobjectbody.md) | | () => {
title?: string | undefined;
timeFieldName?: string | undefined;
intervalName?: string | undefined;
fields?: string | undefined;
sourceFilters?: string | undefined;
fieldFormatMap?: string | undefined;
typeMeta?: string | undefined;
type?: string | undefined;
} | Get last saved saved object fields | +| [id](./kibana-plugin-plugins-data-server.indexpattern.id.md) | | string | | +| [intervalName](./kibana-plugin-plugins-data-server.indexpattern.intervalname.md) | | string | undefined | | +| [metaFields](./kibana-plugin-plugins-data-server.indexpattern.metafields.md) | | string[] | | +| [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md) | | () => void | Reset last saved saved object fields. used after saving | +| [sourceFilters](./kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md) | | SourceFilter[] | | +| [timeFieldName](./kibana-plugin-plugins-data-server.indexpattern.timefieldname.md) | | string | undefined | | +| [title](./kibana-plugin-plugins-data-server.indexpattern.title.md) | | string | | +| [type](./kibana-plugin-plugins-data-server.indexpattern.type.md) | | string | undefined | | +| [typeMeta](./kibana-plugin-plugins-data-server.indexpattern.typemeta.md) | | TypeMeta | | +| [version](./kibana-plugin-plugins-data-server.indexpattern.version.md) | | string | undefined | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md) | | Add scripted field to field list | +| [getAggregationRestrictions()](./kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md) | | | +| [getAsSavedObjectBody()](./kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | +| [getComputedFields()](./kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md) | | | +| [getFieldByName(name)](./kibana-plugin-plugins-data-server.indexpattern.getfieldbyname.md) | | | +| [getFormatterForField(field)](./kibana-plugin-plugins-data-server.indexpattern.getformatterforfield.md) | | Provide a field, get its formatter | +| [getNonScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getnonscriptedfields.md) | | | +| [getScriptedFields()](./kibana-plugin-plugins-data-server.indexpattern.getscriptedfields.md) | | | +| [getSourceFiltering()](./kibana-plugin-plugins-data-server.indexpattern.getsourcefiltering.md) | | Get the source filtering configuration for that index. | +| [getTimeField()](./kibana-plugin-plugins-data-server.indexpattern.gettimefield.md) | | | +| [isTimeBased()](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) | | | +| [isTimeNanosBased()](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) | | | +| [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | +| [toSpec()](./kibana-plugin-plugins-data-server.indexpattern.tospec.md) | | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.metafields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.metafields.md new file mode 100644 index 0000000000000..a2c7c806d6057 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.metafields.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [metaFields](./kibana-plugin-plugins-data-server.indexpattern.metafields.md) + +## IndexPattern.metaFields property + +Signature: + +```typescript +metaFields: string[]; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md new file mode 100644 index 0000000000000..3162a7f42dd12 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [removeScriptedField](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) + +## IndexPattern.removeScriptedField() method + +Remove scripted field from field list + +Signature: + +```typescript +removeScriptedField(fieldName: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fieldName | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md new file mode 100644 index 0000000000000..18ec7070bd577 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [resetOriginalSavedObjectBody](./kibana-plugin-plugins-data-server.indexpattern.resetoriginalsavedobjectbody.md) + +## IndexPattern.resetOriginalSavedObjectBody property + +Reset last saved saved object fields. used after saving + +Signature: + +```typescript +resetOriginalSavedObjectBody: () => void; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md new file mode 100644 index 0000000000000..d359bef2f30a9 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [sourceFilters](./kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md) + +## IndexPattern.sourceFilters property + +Signature: + +```typescript +sourceFilters?: SourceFilter[]; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.timefieldname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.timefieldname.md new file mode 100644 index 0000000000000..35740afa4e3dc --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.timefieldname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [timeFieldName](./kibana-plugin-plugins-data-server.indexpattern.timefieldname.md) + +## IndexPattern.timeFieldName property + +Signature: + +```typescript +timeFieldName: string | undefined; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.title.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.title.md new file mode 100644 index 0000000000000..4cebde989aebd --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.title.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [title](./kibana-plugin-plugins-data-server.indexpattern.title.md) + +## IndexPattern.title property + +Signature: + +```typescript +title: string; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md new file mode 100644 index 0000000000000..5d76b8f00853b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [toSpec](./kibana-plugin-plugins-data-server.indexpattern.tospec.md) + +## IndexPattern.toSpec() method + +Signature: + +```typescript +toSpec(): IndexPatternSpec; +``` +Returns: + +`IndexPatternSpec` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md new file mode 100644 index 0000000000000..01154ab5444d1 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [type](./kibana-plugin-plugins-data-server.indexpattern.type.md) + +## IndexPattern.type property + +Signature: + +```typescript +type: string | undefined; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md new file mode 100644 index 0000000000000..b16bcec404d97 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [typeMeta](./kibana-plugin-plugins-data-server.indexpattern.typemeta.md) + +## IndexPattern.typeMeta property + +Signature: + +```typescript +typeMeta?: TypeMeta; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md new file mode 100644 index 0000000000000..e4297d8389111 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [version](./kibana-plugin-plugins-data-server.indexpattern.version.md) + +## IndexPattern.version property + +Signature: + +```typescript +version: string | undefined; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md index 4a5b61f5c179b..40b029da00469 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md @@ -4,12 +4,6 @@ ## IndexPatternAttributes interface -> Warning: This API is now obsolete. -> -> - -Use data plugin interface instead - Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.aggregatable.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.aggregatable.md deleted file mode 100644 index 92994b851ec85..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.aggregatable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [aggregatable](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.aggregatable.md) - -## IndexPatternFieldDescriptor.aggregatable property - -Signature: - -```typescript -aggregatable: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.estypes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.estypes.md deleted file mode 100644 index f24ba9a48d85e..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.estypes.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [esTypes](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.estypes.md) - -## IndexPatternFieldDescriptor.esTypes property - -Signature: - -```typescript -esTypes: string[]; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md deleted file mode 100644 index d84d0cba06ac6..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) - -## IndexPatternFieldDescriptor interface - -Signature: - -```typescript -export interface FieldDescriptor -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggregatable](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.aggregatable.md) | boolean | | -| [esTypes](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.estypes.md) | string[] | | -| [name](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.name.md) | string | | -| [readFromDocValues](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.readfromdocvalues.md) | boolean | | -| [searchable](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.searchable.md) | boolean | | -| [subType](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.subtype.md) | FieldSubType | | -| [type](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.type.md) | string | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.name.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.name.md deleted file mode 100644 index 16ea60c5b8ae2..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [name](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.name.md) - -## IndexPatternFieldDescriptor.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.readfromdocvalues.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.readfromdocvalues.md deleted file mode 100644 index fc8667196c879..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.readfromdocvalues.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [readFromDocValues](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.readfromdocvalues.md) - -## IndexPatternFieldDescriptor.readFromDocValues property - -Signature: - -```typescript -readFromDocValues: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.searchable.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.searchable.md deleted file mode 100644 index 7d159c65b40bd..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.searchable.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [searchable](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.searchable.md) - -## IndexPatternFieldDescriptor.searchable property - -Signature: - -```typescript -searchable: boolean; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.subtype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.subtype.md deleted file mode 100644 index 7053eaf08138c..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.subtype.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [subType](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.subtype.md) - -## IndexPatternFieldDescriptor.subType property - -Signature: - -```typescript -subType?: FieldSubType; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.type.md deleted file mode 100644 index bb571d1bee14a..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternfielddescriptor.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) > [type](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.type.md) - -## IndexPatternFieldDescriptor.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md new file mode 100644 index 0000000000000..aa78c055f4f5c --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) + +## IndexPatternsService class + +Signature: + +```typescript +export declare class IndexPatternsService implements Plugin +``` + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [setup(core)](./kibana-plugin-plugins-data-server.indexpatternsservice.setup.md) | | | +| [start(core, { fieldFormats, logger })](./kibana-plugin-plugins-data-server.indexpatternsservice.start.md) | | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.setup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.setup.md new file mode 100644 index 0000000000000..a354fbc2a477b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.setup.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) > [setup](./kibana-plugin-plugins-data-server.indexpatternsservice.setup.md) + +## IndexPatternsService.setup() method + +Signature: + +```typescript +setup(core: CoreSetup): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreSetup | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md new file mode 100644 index 0000000000000..d35dc3aa11000 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternsservice.start.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) > [start](./kibana-plugin-plugins-data-server.indexpatternsservice.start.md) + +## IndexPatternsService.start() method + +Signature: + +```typescript +start(core: CoreStart, { fieldFormats, logger }: IndexPatternsServiceStartDeps): { + indexPatternsServiceFactory: (kibanaRequest: KibanaRequest) => Promise; + }; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreStart | | +| { fieldFormats, logger } | IndexPatternsServiceStartDeps | | + +Returns: + +`{ + indexPatternsServiceFactory: (kibanaRequest: KibanaRequest) => Promise; + }` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md index ac2ae13372f7a..3e27140e8bc08 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md @@ -15,6 +15,6 @@ export interface ISearchSetup | Property | Type | Description | | --- | --- | --- | | [aggs](./kibana-plugin-plugins-data-server.isearchsetup.aggs.md) | AggsSetup | | -| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | <SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse>(name: string, strategy: ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse>) => void | Extension point exposed for other plugins to register their own search strategies. | +| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | <SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse>(name: string, strategy: ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse>) => void | Extension point exposed for other plugins to register their own search strategies. | | [usage](./kibana-plugin-plugins-data-server.isearchsetup.usage.md) | SearchUsage | Used internally for telemetry | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md index f20c6f4911062..81571d343495c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md @@ -9,5 +9,5 @@ Extension point exposed for other plugins to register their own search strategie Signature: ```typescript -registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void; +registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md index 577532d22b3d3..9c47ea1a166d5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md @@ -7,7 +7,7 @@ Signature: ```typescript -export interface ISearchStart +export interface ISearchStart ``` ## Properties @@ -16,5 +16,6 @@ export interface ISearchStartAggsStart | | | [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | (name: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse> | Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. | -| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | (context: RequestHandlerContext, request: IEsSearchRequest, options: ISearchOptions) => Promise<IEsSearchResponse> | | +| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise<SearchStrategyResponse> | | +| [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) | {
asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;
} | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md index 33ca818afc769..fdcd4d6768db5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md @@ -7,5 +7,5 @@ Signature: ```typescript -search: (context: RequestHandlerContext, request: IEsSearchRequest, options: ISearchOptions) => Promise; +search: (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.searchsource.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.searchsource.md new file mode 100644 index 0000000000000..66a43fe29c43b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.searchsource.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) > [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) + +## ISearchStart.searchSource property + +Signature: + +```typescript +searchSource: { + asScoped: (request: KibanaRequest) => Promise; + }; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md index dc076455ab272..3d2caf417f3cb 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md @@ -9,7 +9,7 @@ Search strategy interface contains a search method that takes in a request and r Signature: ```typescript -export interface ISearchStrategy +export interface ISearchStrategy ``` ## Properties diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 3c477e17503f4..f1eecd6e49b02 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -9,7 +9,9 @@ | Class | Description | | --- | --- | | [AggParamType](./kibana-plugin-plugins-data-server.aggparamtype.md) | | +| [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) | | | [IndexPatternsFetcher](./kibana-plugin-plugins-data-server.indexpatternsfetcher.md) | | +| [IndexPatternsService](./kibana-plugin-plugins-data-server.indexpatternsservice.md) | | | [OptionedParamType](./kibana-plugin-plugins-data-server.optionedparamtype.md) | | | [Plugin](./kibana-plugin-plugins-data-server.plugin.md) | | @@ -41,14 +43,12 @@ | --- | --- | | [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | | | [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | +| [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | -| [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | -| [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) | | -| [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Use data plugin interface instead | -| [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) | | +| [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) | | | [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | | | [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | | @@ -91,6 +91,7 @@ | [Filter](./kibana-plugin-plugins-data-server.filter.md) | | | [IAggConfig](./kibana-plugin-plugins-data-server.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-server.iaggtype.md) | | +| [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [IFieldParamType](./kibana-plugin-plugins-data-server.ifieldparamtype.md) | | | [IMetricAggType](./kibana-plugin-plugins-data-server.imetricaggtype.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index 455c5ecdd8195..e44cb5c657747 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -8,13 +8,13 @@ ```typescript start(core: CoreStart): { - search: ISearchStart>; fieldFormats: { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; }; + search: ISearchStart>; }; ``` @@ -27,12 +27,12 @@ start(core: CoreStart): { Returns: `{ - search: ISearchStart>; fieldFormats: { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; }; + search: ISearchStart>; }` diff --git a/docs/development/plugins/expressions/public/index.md b/docs/development/plugins/expressions/public/index.md new file mode 100644 index 0000000000000..ade7a9e90b517 --- /dev/null +++ b/docs/development/plugins/expressions/public/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md new file mode 100644 index 0000000000000..f905a1028d217 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md) + +## AnyExpressionFunctionDefinition type + +Type to capture every possible expression function definition. + +Signature: + +```typescript +export declare type AnyExpressionFunctionDefinition = ExpressionFunctionDefinition, any>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md new file mode 100644 index 0000000000000..c213de4341a6a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md) + +## AnyExpressionTypeDefinition type + +Signature: + +```typescript +export declare type AnyExpressionTypeDefinition = ExpressionTypeDefinition; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.argumenttype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.argumenttype.md new file mode 100644 index 0000000000000..bf80b863fda90 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.argumenttype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ArgumentType](./kibana-plugin-plugins-expressions-public.argumenttype.md) + +## ArgumentType type + +This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each `arg` in the specification. + +Signature: + +```typescript +export declare type ArgumentType = SingleArgumentType | MultipleArgumentType | UnresolvedSingleArgumentType | UnresolvedMultipleArgumentType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpression.md new file mode 100644 index 0000000000000..e1d522588aae8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [buildExpression](./kibana-plugin-plugins-expressions-public.buildexpression.md) + +## buildExpression() function + +Makes it easy to progressively build, update, and traverse an expression AST. You can either start with an empty AST, or provide an expression string, AST, or array of expression function builders to use as initial state. + +Signature: + +```typescript +export declare function buildExpression(initialState?: ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string): ExpressionAstExpressionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initialState | ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string | | + +Returns: + +`ExpressionAstExpressionBuilder` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpressionfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpressionfunction.md new file mode 100644 index 0000000000000..79deb7157130e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.buildexpressionfunction.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [buildExpressionFunction](./kibana-plugin-plugins-expressions-public.buildexpressionfunction.md) + +## buildExpressionFunction() function + +Manages an AST for a single expression function. The return value can be provided to `buildExpression` to add this function to an expression. + +Note that to preserve type safety and ensure no args are missing, all required arguments for the specified function must be provided up front. If desired, they can be changed or removed later. + +Signature: + +```typescript +export declare function buildExpressionFunction(fnName: InferFunctionDefinition['name'], +initialArgs: { + [K in keyof FunctionArgs]: FunctionArgs[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[]; +}): ExpressionAstFunctionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fnName | InferFunctionDefinition<FnDef>['name'] | | +| initialArgs | {
[K in keyof FunctionArgs<FnDef>]: FunctionArgs<FnDef>[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[];
} | | + +Returns: + +`ExpressionAstFunctionBuilder` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.columns.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.columns.md new file mode 100644 index 0000000000000..d24c4f4dfb176 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.columns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Datatable](./kibana-plugin-plugins-expressions-public.datatable.md) > [columns](./kibana-plugin-plugins-expressions-public.datatable.columns.md) + +## Datatable.columns property + +Signature: + +```typescript +columns: DatatableColumn[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.md new file mode 100644 index 0000000000000..f2daf656dfa73 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Datatable](./kibana-plugin-plugins-expressions-public.datatable.md) + +## Datatable interface + +A `Datatable` in Canvas is a unique structure that represents tabulated data. + +Signature: + +```typescript +export interface Datatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [columns](./kibana-plugin-plugins-expressions-public.datatable.columns.md) | DatatableColumn[] | | +| [rows](./kibana-plugin-plugins-expressions-public.datatable.rows.md) | DatatableRow[] | | +| [type](./kibana-plugin-plugins-expressions-public.datatable.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.rows.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.rows.md new file mode 100644 index 0000000000000..0d52e446b09fd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Datatable](./kibana-plugin-plugins-expressions-public.datatable.md) > [rows](./kibana-plugin-plugins-expressions-public.datatable.rows.md) + +## Datatable.rows property + +Signature: + +```typescript +rows: DatatableRow[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.type.md new file mode 100644 index 0000000000000..e0ee6fd5d8372 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Datatable](./kibana-plugin-plugins-expressions-public.datatable.md) > [type](./kibana-plugin-plugins-expressions-public.datatable.type.md) + +## Datatable.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.id.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.id.md new file mode 100644 index 0000000000000..d9b98e6cf939e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-public.datatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-public.datatablecolumn.id.md) + +## DatatableColumn.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.md new file mode 100644 index 0000000000000..d67a5d9b36b12 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-public.datatablecolumn.md) + +## DatatableColumn interface + +This type represents the shape of a column in a `Datatable`. + +Signature: + +```typescript +export interface DatatableColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-expressions-public.datatablecolumn.id.md) | string | | +| [meta](./kibana-plugin-plugins-expressions-public.datatablecolumn.meta.md) | DatatableColumnMeta | | +| [name](./kibana-plugin-plugins-expressions-public.datatablecolumn.name.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.meta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.meta.md new file mode 100644 index 0000000000000..a5414dde86f97 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.meta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-public.datatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-public.datatablecolumn.meta.md) + +## DatatableColumn.meta property + +Signature: + +```typescript +meta: DatatableColumnMeta; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.name.md new file mode 100644 index 0000000000000..74c3883e7a172 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumn.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-public.datatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-public.datatablecolumn.name.md) + +## DatatableColumn.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md new file mode 100644 index 0000000000000..a06ab351e62c3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) + +## DatatableColumnType type + +This type represents the `type` of any `DatatableColumn` in a `Datatable`. + +Signature: + +```typescript +export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablerow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablerow.md new file mode 100644 index 0000000000000..87cc15d0d4091 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablerow.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [DatatableRow](./kibana-plugin-plugins-expressions-public.datatablerow.md) + +## DatatableRow type + +This type represents a row in a `Datatable`. + +Signature: + +```typescript +export declare type DatatableRow = Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution._constructor_.md new file mode 100644 index 0000000000000..1d0c9f99169db --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.execution._constructor_.md) + +## Execution.(constructor) + +Constructs a new instance of the `Execution` class + +Signature: + +```typescript +constructor(params: ExecutionParams); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| params | ExecutionParams<ExtraContext> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cancel.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cancel.md new file mode 100644 index 0000000000000..e87cea30dd5b6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cancel.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [cancel](./kibana-plugin-plugins-expressions-public.execution.cancel.md) + +## Execution.cancel() method + +Stop execution of expression. + +Signature: + +```typescript +cancel(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cast.md new file mode 100644 index 0000000000000..632849af7c82b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.cast.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [cast](./kibana-plugin-plugins-expressions-public.execution.cast.md) + +## Execution.cast() method + +Signature: + +```typescript +cast(value: any, toTypeNames?: string[]): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| value | any | | +| toTypeNames | string[] | | + +Returns: + +`any` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.context.md new file mode 100644 index 0000000000000..732fe94d65617 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.context.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [context](./kibana-plugin-plugins-expressions-public.execution.context.md) + +## Execution.context property + +Execution context - object that allows to do side-effects. Context is passed to every function. + +Signature: + +```typescript +readonly context: ExecutionContext & ExtraContext; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.contract.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.contract.md new file mode 100644 index 0000000000000..fa03297ea22a7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.contract.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [contract](./kibana-plugin-plugins-expressions-public.execution.contract.md) + +## Execution.contract property + +Contract is a public representation of `Execution` instances. Contract we can return to other plugins for their consumption. + +Signature: + +```typescript +readonly contract: ExecutionContract; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.expression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.expression.md new file mode 100644 index 0000000000000..a30cc89e8b649 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [expression](./kibana-plugin-plugins-expressions-public.execution.expression.md) + +## Execution.expression property + +Signature: + +```typescript +readonly expression: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.input.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.input.md new file mode 100644 index 0000000000000..553a463a2b931 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.input.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [input](./kibana-plugin-plugins-expressions-public.execution.input.md) + +## Execution.input property + +Initial input of the execution. + +N.B. It is initialized to `null` rather than `undefined` for legacy reasons, because in legacy interpreter it was set to `null` by default. + +Signature: + +```typescript +input: Input; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md new file mode 100644 index 0000000000000..728015011f7d9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [inspectorAdapters](./kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md) + +## Execution.inspectorAdapters property + +Signature: + +```typescript +get inspectorAdapters(): InspectorAdapters; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.interpret.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.interpret.md new file mode 100644 index 0000000000000..31f38b7069812 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.interpret.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [interpret](./kibana-plugin-plugins-expressions-public.execution.interpret.md) + +## Execution.interpret() method + +Signature: + +```typescript +interpret(ast: ExpressionAstNode, input: T, options?: ExpressionExecOptions): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstNode | | +| input | T | | +| options | ExpressionExecOptions | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokechain.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokechain.md new file mode 100644 index 0000000000000..5078baf2ca526 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokechain.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [invokeChain](./kibana-plugin-plugins-expressions-public.execution.invokechain.md) + +## Execution.invokeChain() method + +Signature: + +```typescript +invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| chainArr | ExpressionAstFunction[] | | +| input | unknown | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokefunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokefunction.md new file mode 100644 index 0000000000000..e90cee8b626d6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.invokefunction.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [invokeFunction](./kibana-plugin-plugins-expressions-public.execution.invokefunction.md) + +## Execution.invokeFunction() method + +Signature: + +```typescript +invokeFunction(fn: ExpressionFunction, input: unknown, args: Record): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fn | ExpressionFunction | | +| input | unknown | | +| args | Record<string, unknown> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md new file mode 100644 index 0000000000000..4d227e6ab85b8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md @@ -0,0 +1,43 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) + +## Execution class + +Signature: + +```typescript +export declare class Execution = Record, Input = unknown, Output = unknown, InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object ? ExtraContext['inspectorAdapters'] : DefaultInspectorAdapters> +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(params)](./kibana-plugin-plugins-expressions-public.execution._constructor_.md) | | Constructs a new instance of the Execution class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-public.execution.context.md) | | ExecutionContext<Input, InspectorAdapters> & ExtraContext | Execution context - object that allows to do side-effects. Context is passed to every function. | +| [contract](./kibana-plugin-plugins-expressions-public.execution.contract.md) | | ExecutionContract<ExtraContext, Input, Output, InspectorAdapters> | Contract is a public representation of Execution instances. Contract we can return to other plugins for their consumption. | +| [expression](./kibana-plugin-plugins-expressions-public.execution.expression.md) | | string | | +| [input](./kibana-plugin-plugins-expressions-public.execution.input.md) | | Input | Initial input of the execution.N.B. It is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | +| [inspectorAdapters](./kibana-plugin-plugins-expressions-public.execution.inspectoradapters.md) | | InspectorAdapters | | +| [params](./kibana-plugin-plugins-expressions-public.execution.params.md) | | ExecutionParams<ExtraContext> | | +| [result](./kibana-plugin-plugins-expressions-public.execution.result.md) | | Promise<Output | ExpressionValueError> | | +| [state](./kibana-plugin-plugins-expressions-public.execution.state.md) | | ExecutionContainer<Output | ExpressionValueError> | Dynamic state of the execution. | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [cancel()](./kibana-plugin-plugins-expressions-public.execution.cancel.md) | | Stop execution of expression. | +| [cast(value, toTypeNames)](./kibana-plugin-plugins-expressions-public.execution.cast.md) | | | +| [interpret(ast, input, options)](./kibana-plugin-plugins-expressions-public.execution.interpret.md) | | | +| [invokeChain(chainArr, input)](./kibana-plugin-plugins-expressions-public.execution.invokechain.md) | | | +| [invokeFunction(fn, input, args)](./kibana-plugin-plugins-expressions-public.execution.invokefunction.md) | | | +| [resolveArgs(fnDef, input, argAsts)](./kibana-plugin-plugins-expressions-public.execution.resolveargs.md) | | | +| [start(input)](./kibana-plugin-plugins-expressions-public.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.params.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.params.md new file mode 100644 index 0000000000000..cd90bf6adab47 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [params](./kibana-plugin-plugins-expressions-public.execution.params.md) + +## Execution.params property + +Signature: + +```typescript +readonly params: ExecutionParams; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.resolveargs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.resolveargs.md new file mode 100644 index 0000000000000..ab67dff604a86 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.resolveargs.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [resolveArgs](./kibana-plugin-plugins-expressions-public.execution.resolveargs.md) + +## Execution.resolveArgs() method + +Signature: + +```typescript +resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fnDef | ExpressionFunction | | +| input | unknown | | +| argAsts | any | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.result.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.result.md new file mode 100644 index 0000000000000..e0167a3a378fe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.result.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [result](./kibana-plugin-plugins-expressions-public.execution.result.md) + +## Execution.result property + +Signature: + +```typescript +get result(): Promise; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md new file mode 100644 index 0000000000000..c6edc43d423dc --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [start](./kibana-plugin-plugins-expressions-public.execution.start.md) + +## Execution.start() method + +Call this method to start execution. + +N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons, because in legacy interpreter it was set to `null` by default. + +Signature: + +```typescript +start(input?: Input): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | Input | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.state.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.state.md new file mode 100644 index 0000000000000..ca8b57b760f29 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.state.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Execution](./kibana-plugin-plugins-expressions-public.execution.md) > [state](./kibana-plugin-plugins-expressions-public.execution.state.md) + +## Execution.state property + +Dynamic state of the execution. + +Signature: + +```typescript +readonly state: ExecutionContainer; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontainer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontainer.md new file mode 100644 index 0000000000000..5cea6c4bc4b8f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContainer](./kibana-plugin-plugins-expressions-public.executioncontainer.md) + +## ExecutionContainer type + +Signature: + +```typescript +export declare type ExecutionContainer = StateContainer, ExecutionPureTransitions>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.abortsignal.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.abortsignal.md new file mode 100644 index 0000000000000..caedf4344dc35 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.abortsignal.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [abortSignal](./kibana-plugin-plugins-expressions-public.executioncontext.abortsignal.md) + +## ExecutionContext.abortSignal property + +Adds ability to abort current execution. + +Signature: + +```typescript +abortSignal: AbortSignal; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getinitialinput.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getinitialinput.md new file mode 100644 index 0000000000000..460b1622c6fbd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getinitialinput.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [getInitialInput](./kibana-plugin-plugins-expressions-public.executioncontext.getinitialinput.md) + +## ExecutionContext.getInitialInput property + +Get initial input with which execution started. + +Signature: + +```typescript +getInitialInput: () => Input; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md new file mode 100644 index 0000000000000..dffce4a091718 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [getSavedObject](./kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md) + +## ExecutionContext.getSavedObject property + +Allows to fetch saved objects from ElasticSearch. In browser `getSavedObject` function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. + +Signature: + +```typescript +getSavedObject?: (type: string, id: string) => Promise>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.inspectoradapters.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.inspectoradapters.md new file mode 100644 index 0000000000000..6f0db6af5616e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.inspectoradapters.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [inspectorAdapters](./kibana-plugin-plugins-expressions-public.executioncontext.inspectoradapters.md) + +## ExecutionContext.inspectorAdapters property + +Adapters for `inspector` plugin. + +Signature: + +```typescript +inspectorAdapters: InspectorAdapters; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md new file mode 100644 index 0000000000000..786e94455c600 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) + +## ExecutionContext interface + +`ExecutionContext` is an object available to all functions during a single execution; it provides various methods to perform side-effects. + +Signature: + +```typescript +export interface ExecutionContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [abortSignal](./kibana-plugin-plugins-expressions-public.executioncontext.abortsignal.md) | AbortSignal | Adds ability to abort current execution. | +| [getInitialInput](./kibana-plugin-plugins-expressions-public.executioncontext.getinitialinput.md) | () => Input | Get initial input with which execution started. | +| [getSavedObject](./kibana-plugin-plugins-expressions-public.executioncontext.getsavedobject.md) | <T extends SavedObjectAttributes = SavedObjectAttributes>(type: string, id: string) => Promise<SavedObject<T>> | Allows to fetch saved objects from ElasticSearch. In browser getSavedObject function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. | +| [inspectorAdapters](./kibana-plugin-plugins-expressions-public.executioncontext.inspectoradapters.md) | InspectorAdapters | Adapters for inspector plugin. | +| [search](./kibana-plugin-plugins-expressions-public.executioncontext.search.md) | ExecutionContextSearch | Search context in which expression should operate. | +| [types](./kibana-plugin-plugins-expressions-public.executioncontext.types.md) | Record<string, ExpressionType> | A map of available expression types. | +| [variables](./kibana-plugin-plugins-expressions-public.executioncontext.variables.md) | Record<string, unknown> | Context variables that can be consumed using var and var_set functions. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.search.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.search.md new file mode 100644 index 0000000000000..05501a475cbd4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.search.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [search](./kibana-plugin-plugins-expressions-public.executioncontext.search.md) + +## ExecutionContext.search property + +Search context in which expression should operate. + +Signature: + +```typescript +search?: ExecutionContextSearch; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.types.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.types.md new file mode 100644 index 0000000000000..0bddaf8455635 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.types.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [types](./kibana-plugin-plugins-expressions-public.executioncontext.types.md) + +## ExecutionContext.types property + +A map of available expression types. + +Signature: + +```typescript +types: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.variables.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.variables.md new file mode 100644 index 0000000000000..3f8a87152f9fe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontext.variables.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) > [variables](./kibana-plugin-plugins-expressions-public.executioncontext.variables.md) + +## ExecutionContext.variables property + +Context variables that can be consumed using `var` and `var_set` functions. + +Signature: + +```typescript +variables: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract._constructor_.md new file mode 100644 index 0000000000000..89a99ef2f8ef8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.executioncontract._constructor_.md) + +## ExecutionContract.(constructor) + +Constructs a new instance of the `ExecutionContract` class + +Signature: + +```typescript +constructor(execution: Execution); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| execution | Execution<ExtraContext, Input, Output, InspectorAdapters> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.cancel.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.cancel.md new file mode 100644 index 0000000000000..7ddfb824288d1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.cancel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [cancel](./kibana-plugin-plugins-expressions-public.executioncontract.cancel.md) + +## ExecutionContract.cancel property + +Cancel the execution of the expression. This will set abort signal (available in execution context) to aborted state, letting expression functions to stop their execution. + +Signature: + +```typescript +cancel: () => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.execution.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.execution.md new file mode 100644 index 0000000000000..f7911250488f2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.execution.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [execution](./kibana-plugin-plugins-expressions-public.executioncontract.execution.md) + +## ExecutionContract.execution property + +Signature: + +```typescript +protected readonly execution: Execution; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getast.md new file mode 100644 index 0000000000000..d873614d779a9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getast.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [getAst](./kibana-plugin-plugins-expressions-public.executioncontract.getast.md) + +## ExecutionContract.getAst property + +Get AST used to execute the expression. + +Signature: + +```typescript +getAst: () => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getdata.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getdata.md new file mode 100644 index 0000000000000..dcd96cf5767bf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getdata.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [getData](./kibana-plugin-plugins-expressions-public.executioncontract.getdata.md) + +## ExecutionContract.getData property + +Returns the final output of expression, if any error happens still wraps that error into `ExpressionValueError` type and returns that. This function never throws. + +Signature: + +```typescript +getData: () => Promise; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md new file mode 100644 index 0000000000000..41dbe72fa69b2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [getExpression](./kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md) + +## ExecutionContract.getExpression property + +Get string representation of the expression. Returns the original string if execution was started from a string. If execution was started from an AST this method returns a string generated from AST. + +Signature: + +```typescript +getExpression: () => string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.inspect.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.inspect.md new file mode 100644 index 0000000000000..d5202b02b0dfd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.inspect.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [inspect](./kibana-plugin-plugins-expressions-public.executioncontract.inspect.md) + +## ExecutionContract.inspect property + +Get Inspector adapters provided to all functions of expression through execution context. + +Signature: + +```typescript +inspect: () => InspectorAdapters; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.ispending.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.ispending.md new file mode 100644 index 0000000000000..409c31b3fbc2c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.ispending.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) > [isPending](./kibana-plugin-plugins-expressions-public.executioncontract.ispending.md) + +## ExecutionContract.isPending property + +Signature: + +```typescript +get isPending(): boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.md new file mode 100644 index 0000000000000..d05620eace208 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executioncontract.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) + +## ExecutionContract class + +`ExecutionContract` is a wrapper around `Execution` class. It provides the same functionality but does not expose Expressions plugin internals. + +Signature: + +```typescript +export declare class ExecutionContract = Record, Input = unknown, Output = unknown, InspectorAdapters = unknown> +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(execution)](./kibana-plugin-plugins-expressions-public.executioncontract._constructor_.md) | | Constructs a new instance of the ExecutionContract class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [cancel](./kibana-plugin-plugins-expressions-public.executioncontract.cancel.md) | | () => void | Cancel the execution of the expression. This will set abort signal (available in execution context) to aborted state, letting expression functions to stop their execution. | +| [execution](./kibana-plugin-plugins-expressions-public.executioncontract.execution.md) | | Execution<ExtraContext, Input, Output, InspectorAdapters> | | +| [getAst](./kibana-plugin-plugins-expressions-public.executioncontract.getast.md) | | () => ExpressionAstExpression | Get AST used to execute the expression. | +| [getData](./kibana-plugin-plugins-expressions-public.executioncontract.getdata.md) | | () => Promise<Output | ExpressionValueError> | Returns the final output of expression, if any error happens still wraps that error into ExpressionValueError type and returns that. This function never throws. | +| [getExpression](./kibana-plugin-plugins-expressions-public.executioncontract.getexpression.md) | | () => string | Get string representation of the expression. Returns the original string if execution was started from a string. If execution was started from an AST this method returns a string generated from AST. | +| [inspect](./kibana-plugin-plugins-expressions-public.executioncontract.inspect.md) | | () => InspectorAdapters | Get Inspector adapters provided to all functions of expression through execution context. | +| [isPending](./kibana-plugin-plugins-expressions-public.executioncontract.ispending.md) | | boolean | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.ast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.ast.md new file mode 100644 index 0000000000000..63487bc4c753e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) > [ast](./kibana-plugin-plugins-expressions-public.executionparams.ast.md) + +## ExecutionParams.ast property + +Signature: + +```typescript +ast?: ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.context.md new file mode 100644 index 0000000000000..b6013162ef2ae --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) > [context](./kibana-plugin-plugins-expressions-public.executionparams.context.md) + +## ExecutionParams.context property + +Signature: + +```typescript +context?: ExtraContext; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.debug.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.debug.md new file mode 100644 index 0000000000000..61ec72465f55e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.debug.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) > [debug](./kibana-plugin-plugins-expressions-public.executionparams.debug.md) + +## ExecutionParams.debug property + +Whether to execute expression in \*debug mode\*. In \*debug mode\* inputs and outputs as well as all resolved arguments and time it took to execute each function are saved and are available for introspection. + +Signature: + +```typescript +debug?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.executor.md new file mode 100644 index 0000000000000..ec070842692fe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.executor.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) > [executor](./kibana-plugin-plugins-expressions-public.executionparams.executor.md) + +## ExecutionParams.executor property + +Signature: + +```typescript +executor: Executor; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.expression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.expression.md new file mode 100644 index 0000000000000..f79728bacd336 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) > [expression](./kibana-plugin-plugins-expressions-public.executionparams.expression.md) + +## ExecutionParams.expression property + +Signature: + +```typescript +expression?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.md new file mode 100644 index 0000000000000..e39dc231fbf96 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionparams.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) + +## ExecutionParams interface + +Signature: + +```typescript +export interface ExecutionParams = Record> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-public.executionparams.ast.md) | ExpressionAstExpression | | +| [context](./kibana-plugin-plugins-expressions-public.executionparams.context.md) | ExtraContext | | +| [debug](./kibana-plugin-plugins-expressions-public.executionparams.debug.md) | boolean | Whether to execute expression in \*debug mode\*. In \*debug mode\* inputs and outputs as well as all resolved arguments and time it took to execute each function are saved and are available for introspection. | +| [executor](./kibana-plugin-plugins-expressions-public.executionparams.executor.md) | Executor<any> | | +| [expression](./kibana-plugin-plugins-expressions-public.executionparams.expression.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.ast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.ast.md new file mode 100644 index 0000000000000..bd77c959bde63 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) > [ast](./kibana-plugin-plugins-expressions-public.executionstate.ast.md) + +## ExecutionState.ast property + +Signature: + +```typescript +ast: ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.error.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.error.md new file mode 100644 index 0000000000000..3ec804b3f0f2e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.error.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) > [error](./kibana-plugin-plugins-expressions-public.executionstate.error.md) + +## ExecutionState.error property + +Error happened during the execution. + +Signature: + +```typescript +error?: Error; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.md new file mode 100644 index 0000000000000..a7848a65fb94b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) + +## ExecutionState interface + +Signature: + +```typescript +export interface ExecutionState extends ExecutorState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-public.executionstate.ast.md) | ExpressionAstExpression | | +| [error](./kibana-plugin-plugins-expressions-public.executionstate.error.md) | Error | Error happened during the execution. | +| [result](./kibana-plugin-plugins-expressions-public.executionstate.result.md) | Output | Result of the expression execution. | +| [state](./kibana-plugin-plugins-expressions-public.executionstate.state.md) | 'not-started' | 'pending' | 'result' | 'error' | Tracks state of execution.- not-started - before .start() method was called. - pending - immediately after .start() method is called. - result - when expression execution completed. - error - when execution failed with error. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.result.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.result.md new file mode 100644 index 0000000000000..571f95211b8bf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.result.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) > [result](./kibana-plugin-plugins-expressions-public.executionstate.result.md) + +## ExecutionState.result property + +Result of the expression execution. + +Signature: + +```typescript +result?: Output; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.state.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.state.md new file mode 100644 index 0000000000000..9b6403590e60b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executionstate.state.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) > [state](./kibana-plugin-plugins-expressions-public.executionstate.state.md) + +## ExecutionState.state property + +Tracks state of execution. + +- `not-started` - before .start() method was called. - `pending` - immediately after .start() method is called. - `result` - when expression execution completed. - `error` - when execution failed with error. + +Signature: + +```typescript +state: 'not-started' | 'pending' | 'result' | 'error'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor._constructor_.md new file mode 100644 index 0000000000000..2d776c9536c82 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.executor._constructor_.md) + +## Executor.(constructor) + +Constructs a new instance of the `Executor` class + +Signature: + +```typescript +constructor(state?: ExecutorState); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| state | ExecutorState<Context> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.context.md new file mode 100644 index 0000000000000..9a35931bbb26b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [context](./kibana-plugin-plugins-expressions-public.executor.context.md) + +## Executor.context property + +Signature: + +```typescript +get context(): Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createexecution.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createexecution.md new file mode 100644 index 0000000000000..e6765064d4a27 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createexecution.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [createExecution](./kibana-plugin-plugins-expressions-public.executor.createexecution.md) + +## Executor.createExecution() method + +Signature: + +```typescript +createExecution = Record, Input = unknown, Output = unknown>(ast: string | ExpressionAstExpression, context?: ExtraContext, { debug }?: ExpressionExecOptions): Execution; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | string | ExpressionAstExpression | | +| context | ExtraContext | | +| { debug } | ExpressionExecOptions | | + +Returns: + +`Execution` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createwithdefaults.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createwithdefaults.md new file mode 100644 index 0000000000000..a058d1c9f830e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.createwithdefaults.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [createWithDefaults](./kibana-plugin-plugins-expressions-public.executor.createwithdefaults.md) + +## Executor.createWithDefaults() method + +Signature: + +```typescript +static createWithDefaults = Record>(state?: ExecutorState): Executor; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| state | ExecutorState<Ctx> | | + +Returns: + +`Executor` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extendcontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extendcontext.md new file mode 100644 index 0000000000000..a08fcc839110d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.extendcontext.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [extendContext](./kibana-plugin-plugins-expressions-public.executor.extendcontext.md) + +## Executor.extendContext() method + +Signature: + +```typescript +extendContext(extraContext: Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| extraContext | Record<string, unknown> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.fork.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.fork.md new file mode 100644 index 0000000000000..65aa7978a5910 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.fork.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [fork](./kibana-plugin-plugins-expressions-public.executor.fork.md) + +## Executor.fork() method + +Signature: + +```typescript +fork(): Executor; +``` +Returns: + +`Executor` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.functions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.functions.md new file mode 100644 index 0000000000000..3c55c246c91f8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.functions.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [functions](./kibana-plugin-plugins-expressions-public.executor.functions.md) + +## Executor.functions property + +> Warning: This API is now obsolete. +> +> + +Signature: + +```typescript +readonly functions: FunctionsRegistry; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunction.md new file mode 100644 index 0000000000000..11d04edc9c97d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunction.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getFunction](./kibana-plugin-plugins-expressions-public.executor.getfunction.md) + +## Executor.getFunction() method + +Signature: + +```typescript +getFunction(name: string): ExpressionFunction | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`ExpressionFunction | undefined` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunctions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunctions.md new file mode 100644 index 0000000000000..1098c867e4c86 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.getfunctions.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getFunctions](./kibana-plugin-plugins-expressions-public.executor.getfunctions.md) + +## Executor.getFunctions() method + +Signature: + +```typescript +getFunctions(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettype.md new file mode 100644 index 0000000000000..a0dc6deb21d2c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getType](./kibana-plugin-plugins-expressions-public.executor.gettype.md) + +## Executor.getType() method + +Signature: + +```typescript +getType(name: string): ExpressionType | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`ExpressionType | undefined` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettypes.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettypes.md new file mode 100644 index 0000000000000..a3c72b135cd31 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.gettypes.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [getTypes](./kibana-plugin-plugins-expressions-public.executor.gettypes.md) + +## Executor.getTypes() method + +Signature: + +```typescript +getTypes(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md new file mode 100644 index 0000000000000..b71c8c79c068f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.md @@ -0,0 +1,43 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) + +## Executor class + +Signature: + +```typescript +export declare class Executor = Record> +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(state)](./kibana-plugin-plugins-expressions-public.executor._constructor_.md) | | Constructs a new instance of the Executor class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-public.executor.context.md) | | Record<string, unknown> | | +| [functions](./kibana-plugin-plugins-expressions-public.executor.functions.md) | | FunctionsRegistry | | +| [state](./kibana-plugin-plugins-expressions-public.executor.state.md) | | ExecutorContainer<Context> | | +| [types](./kibana-plugin-plugins-expressions-public.executor.types.md) | | TypesRegistry | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [createExecution(ast, context, { debug })](./kibana-plugin-plugins-expressions-public.executor.createexecution.md) | | | +| [createWithDefaults(state)](./kibana-plugin-plugins-expressions-public.executor.createwithdefaults.md) | static | | +| [extendContext(extraContext)](./kibana-plugin-plugins-expressions-public.executor.extendcontext.md) | | | +| [fork()](./kibana-plugin-plugins-expressions-public.executor.fork.md) | | | +| [getFunction(name)](./kibana-plugin-plugins-expressions-public.executor.getfunction.md) | | | +| [getFunctions()](./kibana-plugin-plugins-expressions-public.executor.getfunctions.md) | | | +| [getType(name)](./kibana-plugin-plugins-expressions-public.executor.gettype.md) | | | +| [getTypes()](./kibana-plugin-plugins-expressions-public.executor.gettypes.md) | | | +| [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-public.executor.registerfunction.md) | | | +| [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-public.executor.registertype.md) | | | +| [run(ast, input, context)](./kibana-plugin-plugins-expressions-public.executor.run.md) | | Execute expression and return result. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registerfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registerfunction.md new file mode 100644 index 0000000000000..b4217fa492a20 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registerfunction.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [registerFunction](./kibana-plugin-plugins-expressions-public.executor.registerfunction.md) + +## Executor.registerFunction() method + +Signature: + +```typescript +registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registertype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registertype.md new file mode 100644 index 0000000000000..f56e5ffcfb9ee --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.registertype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [registerType](./kibana-plugin-plugins-expressions-public.executor.registertype.md) + +## Executor.registerType() method + +Signature: + +```typescript +registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| typeDefinition | AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.run.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.run.md new file mode 100644 index 0000000000000..e06ce733acd43 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.run.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [run](./kibana-plugin-plugins-expressions-public.executor.run.md) + +## Executor.run() method + +Execute expression and return result. + +Signature: + +```typescript +run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | string | ExpressionAstExpression | | +| input | Input | | +| context | ExtraContext | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.state.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.state.md new file mode 100644 index 0000000000000..e9b7006980ceb --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.state.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [state](./kibana-plugin-plugins-expressions-public.executor.state.md) + +## Executor.state property + +Signature: + +```typescript +readonly state: ExecutorContainer; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.types.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.types.md new file mode 100644 index 0000000000000..1ab9a5c4621be --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executor.types.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Executor](./kibana-plugin-plugins-expressions-public.executor.md) > [types](./kibana-plugin-plugins-expressions-public.executor.types.md) + +## Executor.types property + +> Warning: This API is now obsolete. +> +> + +Signature: + +```typescript +readonly types: TypesRegistry; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorcontainer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorcontainer.md new file mode 100644 index 0000000000000..f48b001593f94 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorcontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutorContainer](./kibana-plugin-plugins-expressions-public.executorcontainer.md) + +## ExecutorContainer type + +Signature: + +```typescript +export declare type ExecutorContainer = Record> = StateContainer, ExecutorPureTransitions, ExecutorPureSelectors>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.context.md new file mode 100644 index 0000000000000..d52074b0eecdd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) > [context](./kibana-plugin-plugins-expressions-public.executorstate.context.md) + +## ExecutorState.context property + +Signature: + +```typescript +context: Context; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.functions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.functions.md new file mode 100644 index 0000000000000..034caf27aaef7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.functions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) > [functions](./kibana-plugin-plugins-expressions-public.executorstate.functions.md) + +## ExecutorState.functions property + +Signature: + +```typescript +functions: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.md new file mode 100644 index 0000000000000..e120631285887 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) + +## ExecutorState interface + +Signature: + +```typescript +export interface ExecutorState = Record> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-public.executorstate.context.md) | Context | | +| [functions](./kibana-plugin-plugins-expressions-public.executorstate.functions.md) | Record<string, ExpressionFunction> | | +| [types](./kibana-plugin-plugins-expressions-public.executorstate.types.md) | Record<string, ExpressionType> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.types.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.types.md new file mode 100644 index 0000000000000..00cf80c271684 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.executorstate.types.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) > [types](./kibana-plugin-plugins-expressions-public.executorstate.types.md) + +## ExecutorState.types property + +Signature: + +```typescript +types: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastargument.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastargument.md new file mode 100644 index 0000000000000..559cec0e841ac --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastargument.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstArgument](./kibana-plugin-plugins-expressions-public.expressionastargument.md) + +## ExpressionAstArgument type + +Signature: + +```typescript +export declare type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md new file mode 100644 index 0000000000000..b50ac83036ffe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) > [chain](./kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md) + +## ExpressionAstExpression.chain property + +Signature: + +```typescript +chain: ExpressionAstFunction[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md new file mode 100644 index 0000000000000..537659c51dce8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) + +## ExpressionAstExpression interface + +Signature: + +```typescript +export interface ExpressionAstExpression +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [chain](./kibana-plugin-plugins-expressions-public.expressionastexpression.chain.md) | ExpressionAstFunction[] | | +| [type](./kibana-plugin-plugins-expressions-public.expressionastexpression.type.md) | 'expression' | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md new file mode 100644 index 0000000000000..34a86e235a911 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpression.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastexpression.type.md) + +## ExpressionAstExpression.type property + +Signature: + +```typescript +type: 'expression'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.findfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.findfunction.md new file mode 100644 index 0000000000000..d31f04ad5bf77 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.findfunction.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) > [findFunction](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.findfunction.md) + +## ExpressionAstExpressionBuilder.findFunction property + +Recursively searches expression for all ocurrences of the function, including in subexpressions. + +Useful when performing migrations on a specific function, as you can iterate over the array of references and update all functions at once. + +Signature: + +```typescript +findFunction: (fnName: InferFunctionDefinition['name']) => Array> | []; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.functions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.functions.md new file mode 100644 index 0000000000000..ceaa4c89fb237 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.functions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) > [functions](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.functions.md) + +## ExpressionAstExpressionBuilder.functions property + +Array of each of the `buildExpressionFunction()` instances in this expression. Use this to remove or reorder functions in the expression. + +Signature: + +```typescript +functions: ExpressionAstFunctionBuilder[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md new file mode 100644 index 0000000000000..079e0b3dd8ac1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) + +## ExpressionAstExpressionBuilder interface + +Signature: + +```typescript +export interface ExpressionAstExpressionBuilder +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [findFunction](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.findfunction.md) | <FnDef extends AnyExpressionFunctionDefinition = AnyExpressionFunctionDefinition>(fnName: InferFunctionDefinition<FnDef>['name']) => Array<ExpressionAstFunctionBuilder<FnDef>> | [] | Recursively searches expression for all ocurrences of the function, including in subexpressions.Useful when performing migrations on a specific function, as you can iterate over the array of references and update all functions at once. | +| [functions](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.functions.md) | ExpressionAstFunctionBuilder[] | Array of each of the buildExpressionFunction() instances in this expression. Use this to remove or reorder functions in the expression. | +| [toAst](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.toast.md) | () => ExpressionAstExpression | Converts expression to an AST. ExpressionAstExpression | +| [toString](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.tostring.md) | () => string | Converts expression to an expression string. string | +| [type](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.type.md) | 'expression_builder' | Used to identify expression builder objects. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.toast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.toast.md new file mode 100644 index 0000000000000..e0b10033f6f3a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.toast.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) > [toAst](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.toast.md) + +## ExpressionAstExpressionBuilder.toAst property + +Converts expression to an AST. + + `ExpressionAstExpression` + +Signature: + +```typescript +toAst: () => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.tostring.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.tostring.md new file mode 100644 index 0000000000000..6a9a25256c0a3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.tostring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) > [toString](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.tostring.md) + +## ExpressionAstExpressionBuilder.toString property + +Converts expression to an expression string. + + `string` + +Signature: + +```typescript +toString: () => string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.type.md new file mode 100644 index 0000000000000..2aa8d5089aa29 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.type.md) + +## ExpressionAstExpressionBuilder.type property + +Used to identify expression builder objects. + +Signature: + +```typescript +type: 'expression_builder'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md new file mode 100644 index 0000000000000..72b44e8319542 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md) + +## ExpressionAstFunction.arguments property + +Signature: + +```typescript +arguments: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md new file mode 100644 index 0000000000000..36101a110979a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [debug](./kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md) + +## ExpressionAstFunction.debug property + +Debug information added to each function when expression is executed in \*debug mode\*. + +Signature: + +```typescript +debug?: ExpressionAstFunctionDebug; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md new file mode 100644 index 0000000000000..1840fff4b625f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.function.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [function](./kibana-plugin-plugins-expressions-public.expressionastfunction.function.md) + +## ExpressionAstFunction.function property + +Signature: + +```typescript +function: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md new file mode 100644 index 0000000000000..1004e58759806 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) + +## ExpressionAstFunction interface + +Signature: + +```typescript +export interface ExpressionAstFunction +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunction.arguments.md) | Record<string, ExpressionAstArgument[]> | | +| [debug](./kibana-plugin-plugins-expressions-public.expressionastfunction.debug.md) | ExpressionAstFunctionDebug | Debug information added to each function when expression is executed in \*debug mode\*. | +| [function](./kibana-plugin-plugins-expressions-public.expressionastfunction.function.md) | string | | +| [type](./kibana-plugin-plugins-expressions-public.expressionastfunction.type.md) | 'function' | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md new file mode 100644 index 0000000000000..f7f8786430191 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunction.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastfunction.type.md) + +## ExpressionAstFunction.type property + +Signature: + +```typescript +type: 'function'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.addargument.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.addargument.md new file mode 100644 index 0000000000000..da7f0ebc826c1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.addargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [addArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.addargument.md) + +## ExpressionAstFunctionBuilder.addArgument property + +Adds an additional argument to the function. For multi-args, this should be called once for each new arg. Note that TS will not enforce whether multi-args are available, so only use this to update an existing arg if you are certain it is a multi-arg. + +Signature: + +```typescript +addArgument: >(name: A, value: FunctionArgs[A] | ExpressionAstExpressionBuilder) => this; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.arguments.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.arguments.md new file mode 100644 index 0000000000000..4a95d20d6c983 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.arguments.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.arguments.md) + +## ExpressionAstFunctionBuilder.arguments property + +Object of all args currently added to the function. This is structured similarly to `ExpressionAstFunction['arguments']`, however any subexpressions are returned as expression builder instances instead of expression ASTs. + +Signature: + +```typescript +arguments: FunctionBuilderArguments; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.getargument.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.getargument.md new file mode 100644 index 0000000000000..0df9c80c632b1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.getargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [getArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.getargument.md) + +## ExpressionAstFunctionBuilder.getArgument property + +Retrieves an existing argument by name. Useful when you want to retrieve the current array of args and add something to it before calling `replaceArgument`. Any subexpression arguments will be returned as expression builder instances. + +Signature: + +```typescript +getArgument: >(name: A) => Array[A] | ExpressionAstExpressionBuilder> | undefined; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md new file mode 100644 index 0000000000000..b05504af28d9b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) + +## ExpressionAstFunctionBuilder interface + +Signature: + +```typescript +export interface ExpressionAstFunctionBuilder +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.addargument.md) | <A extends FunctionArgName<FnDef>>(name: A, value: FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder) => this | Adds an additional argument to the function. For multi-args, this should be called once for each new arg. Note that TS will not enforce whether multi-args are available, so only use this to update an existing arg if you are certain it is a multi-arg. | +| [arguments](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.arguments.md) | FunctionBuilderArguments<FnDef> | Object of all args currently added to the function. This is structured similarly to ExpressionAstFunction['arguments'], however any subexpressions are returned as expression builder instances instead of expression ASTs. | +| [getArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.getargument.md) | <A extends FunctionArgName<FnDef>>(name: A) => Array<FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder> | undefined | Retrieves an existing argument by name. Useful when you want to retrieve the current array of args and add something to it before calling replaceArgument. Any subexpression arguments will be returned as expression builder instances. | +| [name](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.name.md) | InferFunctionDefinition<FnDef>['name'] | Name of this expression function. | +| [removeArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.removeargument.md) | <A extends OptionalKeys<FunctionArgs<FnDef>>>(name: A) => this | Removes an (optional) argument from the function.TypeScript will enforce that you only remove optional arguments. For manipulating required args, use replaceArgument. | +| [replaceArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.replaceargument.md) | <A extends FunctionArgName<FnDef>>(name: A, value: Array<FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder>) => this | Overwrites an existing argument with a new value. In order to support multi-args, the value given must always be an array. | +| [toAst](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.toast.md) | () => ExpressionAstFunction | Converts function to an AST. ExpressionAstFunction | +| [toString](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.tostring.md) | () => string | Converts function to an expression string. string | +| [type](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.type.md) | 'expression_function_builder' | Used to identify expression function builder objects. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.name.md new file mode 100644 index 0000000000000..5bcf965426dbd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [name](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.name.md) + +## ExpressionAstFunctionBuilder.name property + +Name of this expression function. + +Signature: + +```typescript +name: InferFunctionDefinition['name']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.removeargument.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.removeargument.md new file mode 100644 index 0000000000000..1883618c96d53 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.removeargument.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [removeArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.removeargument.md) + +## ExpressionAstFunctionBuilder.removeArgument property + +Removes an (optional) argument from the function. + +TypeScript will enforce that you only remove optional arguments. For manipulating required args, use `replaceArgument`. + +Signature: + +```typescript +removeArgument: >>(name: A) => this; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.replaceargument.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.replaceargument.md new file mode 100644 index 0000000000000..81709f6e94f0a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.replaceargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [replaceArgument](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.replaceargument.md) + +## ExpressionAstFunctionBuilder.replaceArgument property + +Overwrites an existing argument with a new value. In order to support multi-args, the value given must always be an array. + +Signature: + +```typescript +replaceArgument: >(name: A, value: Array[A] | ExpressionAstExpressionBuilder>) => this; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.toast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.toast.md new file mode 100644 index 0000000000000..bf79726c881ae --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.toast.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [toAst](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.toast.md) + +## ExpressionAstFunctionBuilder.toAst property + +Converts function to an AST. + + `ExpressionAstFunction` + +Signature: + +```typescript +toAst: () => ExpressionAstFunction; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.tostring.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.tostring.md new file mode 100644 index 0000000000000..5c8d0c806d372 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.tostring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [toString](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.tostring.md) + +## ExpressionAstFunctionBuilder.toString property + +Converts function to an expression string. + + `string` + +Signature: + +```typescript +toString: () => string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.type.md new file mode 100644 index 0000000000000..b88876b14f367 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) > [type](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.type.md) + +## ExpressionAstFunctionBuilder.type property + +Used to identify expression function builder objects. + +Signature: + +```typescript +type: 'expression_function_builder'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastnode.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastnode.md new file mode 100644 index 0000000000000..4e05b6a18374c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionastnode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionAstNode](./kibana-plugin-plugins-expressions-public.expressionastnode.md) + +## ExpressionAstNode type + +Signature: + +```typescript +export declare type ExpressionAstNode = ExpressionAstExpression | ExpressionAstFunction | ExpressionAstArgument; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.interpreter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.interpreter.md new file mode 100644 index 0000000000000..6741634379dc1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.interpreter.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionExecutor](./kibana-plugin-plugins-expressions-public.expressionexecutor.md) > [interpreter](./kibana-plugin-plugins-expressions-public.expressionexecutor.interpreter.md) + +## ExpressionExecutor.interpreter property + +Signature: + +```typescript +interpreter: ExpressionInterpreter; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.md new file mode 100644 index 0000000000000..f0c457af52d22 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionexecutor.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionExecutor](./kibana-plugin-plugins-expressions-public.expressionexecutor.md) + +## ExpressionExecutor interface + +> Warning: This API is now obsolete. +> +> This type if remainder from legacy platform, will be deleted going further. +> + +Signature: + +```typescript +export interface ExpressionExecutor +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [interpreter](./kibana-plugin-plugins-expressions-public.expressionexecutor.interpreter.md) | ExpressionInterpreter | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction._constructor_.md new file mode 100644 index 0000000000000..9c711b47c89d0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionfunction._constructor_.md) + +## ExpressionFunction.(constructor) + +Constructs a new instance of the `ExpressionFunction` class + +Signature: + +```typescript +constructor(functionDefinition: AnyExpressionFunctionDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md new file mode 100644 index 0000000000000..7a65878cd5a2d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [accepts](./kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md) + +## ExpressionFunction.accepts property + +Signature: + +```typescript +accepts: (type: string) => boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md new file mode 100644 index 0000000000000..550620386a892 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [aliases](./kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md) + +## ExpressionFunction.aliases property + +Aliases that can be used instead of `name`. + +Signature: + +```typescript +aliases: string[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.args.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.args.md new file mode 100644 index 0000000000000..e14c08b8b7079 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.args.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [args](./kibana-plugin-plugins-expressions-public.expressionfunction.args.md) + +## ExpressionFunction.args property + +Specification of expression function parameters. + +Signature: + +```typescript +args: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.fn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.fn.md new file mode 100644 index 0000000000000..d94d9af9bf0f9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.fn.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [fn](./kibana-plugin-plugins-expressions-public.expressionfunction.fn.md) + +## ExpressionFunction.fn property + +Function to run function (context, args) + +Signature: + +```typescript +fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.help.md new file mode 100644 index 0000000000000..bbf70e11192eb --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [help](./kibana-plugin-plugins-expressions-public.expressionfunction.help.md) + +## ExpressionFunction.help property + +A short help text. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md new file mode 100644 index 0000000000000..865c856746062 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md) + +## ExpressionFunction.inputTypes property + +Type of inputs that this function supports. + +Signature: + +```typescript +inputTypes: string[] | undefined; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md new file mode 100644 index 0000000000000..5ca67e40c93ec --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) + +## ExpressionFunction class + +Signature: + +```typescript +export declare class ExpressionFunction +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(functionDefinition)](./kibana-plugin-plugins-expressions-public.expressionfunction._constructor_.md) | | Constructs a new instance of the ExpressionFunction class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [accepts](./kibana-plugin-plugins-expressions-public.expressionfunction.accepts.md) | | (type: string) => boolean | | +| [aliases](./kibana-plugin-plugins-expressions-public.expressionfunction.aliases.md) | | string[] | Aliases that can be used instead of name. | +| [args](./kibana-plugin-plugins-expressions-public.expressionfunction.args.md) | | Record<string, ExpressionFunctionParameter> | Specification of expression function parameters. | +| [fn](./kibana-plugin-plugins-expressions-public.expressionfunction.fn.md) | | (input: ExpressionValue, params: Record<string, any>, handlers: object) => ExpressionValue | Function to run function (context, args) | +| [help](./kibana-plugin-plugins-expressions-public.expressionfunction.help.md) | | string | A short help text. | +| [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunction.inputtypes.md) | | string[] | undefined | Type of inputs that this function supports. | +| [name](./kibana-plugin-plugins-expressions-public.expressionfunction.name.md) | | string | Name of function | +| [type](./kibana-plugin-plugins-expressions-public.expressionfunction.type.md) | | string | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.name.md new file mode 100644 index 0000000000000..2858089ea67de --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [name](./kibana-plugin-plugins-expressions-public.expressionfunction.name.md) + +## ExpressionFunction.name property + +Name of function + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.type.md new file mode 100644 index 0000000000000..7a7bc129a1719 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunction.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) > [type](./kibana-plugin-plugins-expressions-public.expressionfunction.type.md) + +## ExpressionFunction.type property + +Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.aliases.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.aliases.md new file mode 100644 index 0000000000000..bca3600b6d416 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.aliases.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [aliases](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.aliases.md) + +## ExpressionFunctionDefinition.aliases property + + What is this? + +Signature: + +```typescript +aliases?: string[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md new file mode 100644 index 0000000000000..65ead35adf0d6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [args](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md) + +## ExpressionFunctionDefinition.args property + +Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. + +Signature: + +```typescript +args: { + [key in keyof Arguments]: ArgumentType; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md new file mode 100644 index 0000000000000..34bbfc7976007 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md) + +## ExpressionFunctionDefinition.context property + +> Warning: This API is now obsolete. +> +> Use `inputTypes` instead. +> + +Signature: + +```typescript +context?: { + types: AnyExpressionFunctionDefinition['inputTypes']; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.fn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.fn.md new file mode 100644 index 0000000000000..a2180c0cee665 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.fn.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [fn](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.fn.md) + +## ExpressionFunctionDefinition.fn() method + +The actual implementation of the function. + +Signature: + +```typescript +fn(input: Input, args: Arguments, context: Context): Output; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | Input | | +| args | Arguments | | +| context | Context | | + +Returns: + +`Output` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md new file mode 100644 index 0000000000000..ad99bb3a14a0b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [help](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md) + +## ExpressionFunctionDefinition.help property + +Help text displayed in the Expression editor. This text should be internationalized. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md new file mode 100644 index 0000000000000..06c15dba514c2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md) + +## ExpressionFunctionDefinition.inputTypes property + +List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. + +Signature: + +```typescript +inputTypes?: Array>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md new file mode 100644 index 0000000000000..bc801542f81ac --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) + +## ExpressionFunctionDefinition interface + +`ExpressionFunctionDefinition` is the interface plugins have to implement to register a function in `expressions` plugin. + +Signature: + +```typescript +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aliases](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.aliases.md) | string[] | What is this? | +| [args](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.args.md) | {
[key in keyof Arguments]: ArgumentType<Arguments[key]>;
} | Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. | +| [context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.context.md) | {
types: AnyExpressionFunctionDefinition['inputTypes'];
} | | +| [help](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.help.md) | string | Help text displayed in the Expression editor. This text should be internationalized. | +| [inputTypes](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.inputtypes.md) | Array<TypeToString<Input>> | List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. | +| [name](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md) | Name | The name of the function, as will be used in expression. | +| [type](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.type.md) | TypeToString<UnwrapPromiseOrReturn<Output>> | Name of type of value this function outputs. | + +## Methods + +| Method | Description | +| --- | --- | +| [fn(input, args, context)](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.fn.md) | The actual implementation of the function. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md new file mode 100644 index 0000000000000..1c74a25851c96 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [name](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.name.md) + +## ExpressionFunctionDefinition.name property + +The name of the function, as will be used in expression. + +Signature: + +```typescript +name: Name; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.type.md new file mode 100644 index 0000000000000..4831f24a418bc --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) > [type](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.type.md) + +## ExpressionFunctionDefinition.type property + +Name of type of value this function outputs. + +Signature: + +```typescript +type?: TypeToString>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md new file mode 100644 index 0000000000000..3b3b5520ab3ab --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [clog](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md) + +## ExpressionFunctionDefinitions.clog property + +Signature: + +```typescript +clog: ExpressionFunctionClog; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md new file mode 100644 index 0000000000000..06674eeaf9d7a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [font](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md) + +## ExpressionFunctionDefinitions.font property + +Signature: + +```typescript +font: ExpressionFunctionFont; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md new file mode 100644 index 0000000000000..abe8e0ae161ad --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [kibana](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md) + +## ExpressionFunctionDefinitions.kibana property + +Signature: + +```typescript +kibana: ExpressionFunctionKibana; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md new file mode 100644 index 0000000000000..4b58fd84e160d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [kibana\_context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md) + +## ExpressionFunctionDefinitions.kibana\_context property + +Signature: + +```typescript +kibana_context: ExpressionFunctionKibanaContext; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md new file mode 100644 index 0000000000000..914c5d6ebe2f6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) + +## ExpressionFunctionDefinitions interface + +A mapping of `ExpressionFunctionDefinition`s for functions which the Expressions services provides out-of-the-box. Any new functions registered by the Expressions plugin should have their types added here. + +Signature: + +```typescript +export interface ExpressionFunctionDefinitions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [clog](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.clog.md) | ExpressionFunctionClog | | +| [font](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | +| [kibana\_context](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana_context.md) | ExpressionFunctionKibanaContext | | +| [kibana](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.kibana.md) | ExpressionFunctionKibana | | +| [theme](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md) | ExpressionFunctionTheme | | +| [var\_set](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md) | ExpressionFunctionVarSet | | +| [var](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md) | ExpressionFunctionVar | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md new file mode 100644 index 0000000000000..766aee8f80809 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [theme](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md) + +## ExpressionFunctionDefinitions.theme property + +Signature: + +```typescript +theme: ExpressionFunctionTheme; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md new file mode 100644 index 0000000000000..4c3f4bb98a51e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [var](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md) + +## ExpressionFunctionDefinitions.var property + +Signature: + +```typescript +var: ExpressionFunctionVar; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md new file mode 100644 index 0000000000000..a45d58242e4f3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [var\_set](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md) + +## ExpressionFunctionDefinitions.var\_set property + +Signature: + +```typescript +var_set: ExpressionFunctionVarSet; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md new file mode 100644 index 0000000000000..8ccf48ba28527 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md) + +## ExpressionFunctionKibana type + +Signature: + +```typescript +export declare type ExpressionFunctionKibana = ExpressionFunctionDefinition<'kibana', ExpressionValueSearchContext | null, object, ExpressionValueSearchContext>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter._constructor_.md new file mode 100644 index 0000000000000..476ae51dd50f7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter._constructor_.md) + +## ExpressionFunctionParameter.(constructor) + +Constructs a new instance of the `ExpressionFunctionParameter` class + +Signature: + +```typescript +constructor(name: string, arg: ArgumentType); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | +| arg | ArgumentType<any> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.accepts.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.accepts.md new file mode 100644 index 0000000000000..13b658d86855e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.accepts.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [accepts](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.accepts.md) + +## ExpressionFunctionParameter.accepts() method + +Signature: + +```typescript +accepts(type: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.aliases.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.aliases.md new file mode 100644 index 0000000000000..03d6daac044b8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.aliases.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [aliases](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.aliases.md) + +## ExpressionFunctionParameter.aliases property + +Signature: + +```typescript +aliases: string[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.default.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.default.md new file mode 100644 index 0000000000000..20cb697c182ae --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.default.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [default](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.default.md) + +## ExpressionFunctionParameter.default property + +Signature: + +```typescript +default: any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.help.md new file mode 100644 index 0000000000000..102715264d5a9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [help](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.help.md) + +## ExpressionFunctionParameter.help property + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md new file mode 100644 index 0000000000000..eb99255b09328 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md @@ -0,0 +1,38 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) + +## ExpressionFunctionParameter class + +Signature: + +```typescript +export declare class ExpressionFunctionParameter +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(name, arg)](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter._constructor_.md) | | Constructs a new instance of the ExpressionFunctionParameter class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [aliases](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.aliases.md) | | string[] | | +| [default](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.default.md) | | any | | +| [help](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.help.md) | | string | | +| [multi](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.multi.md) | | boolean | | +| [name](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.name.md) | | string | | +| [options](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.options.md) | | any[] | | +| [required](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.required.md) | | boolean | | +| [resolve](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.resolve.md) | | boolean | | +| [types](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.types.md) | | string[] | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [accepts(type)](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.accepts.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.multi.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.multi.md new file mode 100644 index 0000000000000..cc0bfbaac05a1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.multi.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [multi](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.multi.md) + +## ExpressionFunctionParameter.multi property + +Signature: + +```typescript +multi: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.name.md new file mode 100644 index 0000000000000..6a7d120a169dc --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [name](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.name.md) + +## ExpressionFunctionParameter.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.options.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.options.md new file mode 100644 index 0000000000000..c1596becd2f5b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.options.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [options](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.options.md) + +## ExpressionFunctionParameter.options property + +Signature: + +```typescript +options: any[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.required.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.required.md new file mode 100644 index 0000000000000..b4c494704edd7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.required.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [required](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.required.md) + +## ExpressionFunctionParameter.required property + +Signature: + +```typescript +required: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.resolve.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.resolve.md new file mode 100644 index 0000000000000..a5689aa2d4226 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.resolve.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [resolve](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.resolve.md) + +## ExpressionFunctionParameter.resolve property + +Signature: + +```typescript +resolve: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.types.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.types.md new file mode 100644 index 0000000000000..63d73001b7285 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctionparameter.types.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) > [types](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.types.md) + +## ExpressionFunctionParameter.types property + +Signature: + +```typescript +types: string[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.dataurl.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.dataurl.md new file mode 100644 index 0000000000000..b6b34720a7dd8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.dataurl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-public.expressionimage.md) > [dataurl](./kibana-plugin-plugins-expressions-public.expressionimage.dataurl.md) + +## ExpressionImage.dataurl property + +Signature: + +```typescript +dataurl: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.md new file mode 100644 index 0000000000000..430273cca7edd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-public.expressionimage.md) + +## ExpressionImage interface + +Signature: + +```typescript +export interface ExpressionImage +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [dataurl](./kibana-plugin-plugins-expressions-public.expressionimage.dataurl.md) | string | | +| [mode](./kibana-plugin-plugins-expressions-public.expressionimage.mode.md) | string | | +| [type](./kibana-plugin-plugins-expressions-public.expressionimage.type.md) | 'image' | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.mode.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.mode.md new file mode 100644 index 0000000000000..f56a58ee71e98 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.mode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-public.expressionimage.md) > [mode](./kibana-plugin-plugins-expressions-public.expressionimage.mode.md) + +## ExpressionImage.mode property + +Signature: + +```typescript +mode: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.type.md new file mode 100644 index 0000000000000..e3b6e135233ef --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionimage.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-public.expressionimage.md) > [type](./kibana-plugin-plugins-expressions-public.expressionimage.type.md) + +## ExpressionImage.type property + +Signature: + +```typescript +type: 'image'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md new file mode 100644 index 0000000000000..9d5f7609ee6cd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [displayName](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md) + +## ExpressionRenderDefinition.displayName property + +A user friendly name of the renderer as will be displayed to user in UI. + +Signature: + +```typescript +displayName: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.help.md new file mode 100644 index 0000000000000..ca67f18c0591f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [help](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.help.md) + +## ExpressionRenderDefinition.help property + +Help text as will be displayed to user. A sentence or few about what this element does. + +Signature: + +```typescript +help?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md new file mode 100644 index 0000000000000..3c3322914cebe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) + +## ExpressionRenderDefinition interface + +Signature: + +```typescript +export interface ExpressionRenderDefinition +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [displayName](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md) | string | A user friendly name of the renderer as will be displayed to user in UI. | +| [help](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.help.md) | string | Help text as will be displayed to user. A sentence or few about what this element does. | +| [name](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.name.md) | string | Technical name of the renderer, used as ID to identify renderer in expression renderer registry. This must match the name of the expression function that is used to create the type: render object. | +| [render](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.render.md) | (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise<void> | The function called to render the output data of an expression. | +| [reuseDomNode](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.reusedomnode.md) | boolean | Tell the renderer if the dom node should be reused, it's recreated each time by default. | +| [validate](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.validate.md) | () => undefined | Error | Used to validate the data before calling the render function. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.name.md new file mode 100644 index 0000000000000..25b782549fe7b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [name](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.name.md) + +## ExpressionRenderDefinition.name property + +Technical name of the renderer, used as ID to identify renderer in expression renderer registry. This must match the name of the expression function that is used to create the `type: render` object. + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.render.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.render.md new file mode 100644 index 0000000000000..d476ae15d4237 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.render.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [render](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.render.md) + +## ExpressionRenderDefinition.render property + +The function called to render the output data of an expression. + +Signature: + +```typescript +render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.reusedomnode.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.reusedomnode.md new file mode 100644 index 0000000000000..515cb2c1c078d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.reusedomnode.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [reuseDomNode](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.reusedomnode.md) + +## ExpressionRenderDefinition.reuseDomNode property + +Tell the renderer if the dom node should be reused, it's recreated each time by default. + +Signature: + +```typescript +reuseDomNode: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.validate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.validate.md new file mode 100644 index 0000000000000..616a0dcc0a94f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.validate.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) > [validate](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.validate.md) + +## ExpressionRenderDefinition.validate property + +Used to validate the data before calling the render function. + +Signature: + +```typescript +validate?: () => undefined | Error; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer._constructor_.md new file mode 100644 index 0000000000000..de74ee631fcf1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionrenderer._constructor_.md) + +## ExpressionRenderer.(constructor) + +Constructs a new instance of the `ExpressionRenderer` class + +Signature: + +```typescript +constructor(config: ExpressionRenderDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| config | ExpressionRenderDefinition<Config> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.displayname.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.displayname.md new file mode 100644 index 0000000000000..710bcc60a47e7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.displayname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [displayName](./kibana-plugin-plugins-expressions-public.expressionrenderer.displayname.md) + +## ExpressionRenderer.displayName property + +Signature: + +```typescript +readonly displayName: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.help.md new file mode 100644 index 0000000000000..f5b3f248e71fe --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [help](./kibana-plugin-plugins-expressions-public.expressionrenderer.help.md) + +## ExpressionRenderer.help property + +Signature: + +```typescript +readonly help: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.md new file mode 100644 index 0000000000000..017d88c0cda69 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) + +## ExpressionRenderer class + +Signature: + +```typescript +export declare class ExpressionRenderer +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(config)](./kibana-plugin-plugins-expressions-public.expressionrenderer._constructor_.md) | | Constructs a new instance of the ExpressionRenderer class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [displayName](./kibana-plugin-plugins-expressions-public.expressionrenderer.displayname.md) | | string | | +| [help](./kibana-plugin-plugins-expressions-public.expressionrenderer.help.md) | | string | | +| [name](./kibana-plugin-plugins-expressions-public.expressionrenderer.name.md) | | string | | +| [render](./kibana-plugin-plugins-expressions-public.expressionrenderer.render.md) | | ExpressionRenderDefinition<Config>['render'] | | +| [reuseDomNode](./kibana-plugin-plugins-expressions-public.expressionrenderer.reusedomnode.md) | | boolean | | +| [validate](./kibana-plugin-plugins-expressions-public.expressionrenderer.validate.md) | | () => void | Error | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.name.md new file mode 100644 index 0000000000000..2ed6677cf6ec4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [name](./kibana-plugin-plugins-expressions-public.expressionrenderer.name.md) + +## ExpressionRenderer.name property + +Signature: + +```typescript +readonly name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.render.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.render.md new file mode 100644 index 0000000000000..2491cb31d7659 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.render.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [render](./kibana-plugin-plugins-expressions-public.expressionrenderer.render.md) + +## ExpressionRenderer.render property + +Signature: + +```typescript +readonly render: ExpressionRenderDefinition['render']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.reusedomnode.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.reusedomnode.md new file mode 100644 index 0000000000000..b5c3a89cc3ed1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.reusedomnode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [reuseDomNode](./kibana-plugin-plugins-expressions-public.expressionrenderer.reusedomnode.md) + +## ExpressionRenderer.reuseDomNode property + +Signature: + +```typescript +readonly reuseDomNode: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.validate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.validate.md new file mode 100644 index 0000000000000..7c1a7ac65809f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderer.validate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) > [validate](./kibana-plugin-plugins-expressions-public.expressionrenderer.validate.md) + +## ExpressionRenderer.validate property + +Signature: + +```typescript +readonly validate: () => void | Error; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md new file mode 100644 index 0000000000000..c49a74abe57f3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererComponent](./kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md) + +## ExpressionRendererComponent type + +Signature: + +```typescript +export declare type ExpressionRendererComponent = React.FC; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.data.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.data.md new file mode 100644 index 0000000000000..537a3f278863d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.data.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererEvent](./kibana-plugin-plugins-expressions-public.expressionrendererevent.md) > [data](./kibana-plugin-plugins-expressions-public.expressionrendererevent.data.md) + +## ExpressionRendererEvent.data property + +Signature: + +```typescript +data: any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.md new file mode 100644 index 0000000000000..952d2f92496c3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererEvent](./kibana-plugin-plugins-expressions-public.expressionrendererevent.md) + +## ExpressionRendererEvent interface + +Signature: + +```typescript +export interface ExpressionRendererEvent +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [data](./kibana-plugin-plugins-expressions-public.expressionrendererevent.data.md) | any | | +| [name](./kibana-plugin-plugins-expressions-public.expressionrendererevent.name.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.name.md new file mode 100644 index 0000000000000..bbff92108358a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererevent.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererEvent](./kibana-plugin-plugins-expressions-public.expressionrendererevent.md) > [name](./kibana-plugin-plugins-expressions-public.expressionrendererevent.name.md) + +## ExpressionRendererEvent.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.get.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.get.md new file mode 100644 index 0000000000000..cff44001f0a1f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) > [get](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.get.md) + +## ExpressionRendererRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionRenderer | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionRenderer | null` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.md new file mode 100644 index 0000000000000..e53f2a7970723 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) + +## ExpressionRendererRegistry class + +Signature: + +```typescript +export declare class ExpressionRendererRegistry implements IRegistry +``` + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.get.md) | | | +| [register(definition)](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.register.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.register.md new file mode 100644 index 0000000000000..13cabb0410861 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) > [register](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.register.md) + +## ExpressionRendererRegistry.register() method + +Signature: + +```typescript +register(definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| definition | AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.toarray.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.toarray.md new file mode 100644 index 0000000000000..b29fd46265d16 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) > [toArray](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.toarray.md) + +## ExpressionRendererRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionRenderer[]; +``` +Returns: + +`ExpressionRenderer[]` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.tojs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.tojs.md new file mode 100644 index 0000000000000..930ef7f8d89d2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) > [toJS](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.tojs.md) + +## ExpressionRendererRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.md new file mode 100644 index 0000000000000..3b3c1644adbef --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderError](./kibana-plugin-plugins-expressions-public.expressionrendererror.md) + +## ExpressionRenderError interface + +Signature: + +```typescript +export interface ExpressionRenderError extends Error +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./kibana-plugin-plugins-expressions-public.expressionrendererror.type.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.type.md new file mode 100644 index 0000000000000..b1939299a9d37 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrendererror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderError](./kibana-plugin-plugins-expressions-public.expressionrendererror.md) > [type](./kibana-plugin-plugins-expressions-public.expressionrendererror.type.md) + +## ExpressionRenderError.type property + +Signature: + +```typescript +type?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md new file mode 100644 index 0000000000000..fb6ba7ee2621c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) + +## ExpressionRenderHandler.(constructor) + +Constructs a new instance of the `ExpressionRenderHandler` class + +Signature: + +```typescript +constructor(element: HTMLElement, { onRenderError }?: Partial); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| element | HTMLElement | | +| { onRenderError } | Partial<ExpressionRenderHandlerParams> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.destroy.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.destroy.md new file mode 100644 index 0000000000000..df949324b3b45 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.destroy.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [destroy](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.destroy.md) + +## ExpressionRenderHandler.destroy property + +Signature: + +```typescript +destroy: () => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.events_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.events_.md new file mode 100644 index 0000000000000..c462724a4fdd9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.events_.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [events$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.events_.md) + +## ExpressionRenderHandler.events$ property + +Signature: + +```typescript +events$: Observable; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.getelement.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.getelement.md new file mode 100644 index 0000000000000..42262938502d8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.getelement.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [getElement](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.getelement.md) + +## ExpressionRenderHandler.getElement property + +Signature: + +```typescript +getElement: () => HTMLElement; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.handlerendererror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.handlerendererror.md new file mode 100644 index 0000000000000..6a70cac98ef8a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.handlerendererror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [handleRenderError](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.handlerendererror.md) + +## ExpressionRenderHandler.handleRenderError property + +Signature: + +```typescript +handleRenderError: (error: ExpressionRenderError) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md new file mode 100644 index 0000000000000..7f7d5792ba684 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) + +## ExpressionRenderHandler class + +Signature: + +```typescript +export declare class ExpressionRenderHandler +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(element, { onRenderError })](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) | | Constructs a new instance of the ExpressionRenderHandler class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [destroy](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.destroy.md) | | () => void | | +| [events$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.events_.md) | | Observable<ExpressionRendererEvent> | | +| [getElement](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.getelement.md) | | () => HTMLElement | | +| [handleRenderError](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.handlerendererror.md) | | (error: ExpressionRenderError) => void | | +| [render](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.render.md) | | (data: any, uiState?: any) => Promise<void> | | +| [render$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.render_.md) | | Observable<number> | | +| [update$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.update_.md) | | Observable<UpdateValue | null> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render.md new file mode 100644 index 0000000000000..dec17d60ffd14 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [render](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.render.md) + +## ExpressionRenderHandler.render property + +Signature: + +```typescript +render: (data: any, uiState?: any) => Promise; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render_.md new file mode 100644 index 0000000000000..631dcbfcf89c1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.render_.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [render$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.render_.md) + +## ExpressionRenderHandler.render$ property + +Signature: + +```typescript +render$: Observable; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.update_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.update_.md new file mode 100644 index 0000000000000..527e64f8e4815 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.update_.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) > [update$](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.update_.md) + +## ExpressionRenderHandler.update$ property + +Signature: + +```typescript +update$: Observable; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin._constructor_.md new file mode 100644 index 0000000000000..f49ae9b8166e7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionspublicplugin._constructor_.md) + +## ExpressionsPublicPlugin.(constructor) + +Constructs a new instance of the `ExpressionsPublicPlugin` class + +Signature: + +```typescript +constructor(initializerContext: PluginInitializerContext); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.md new file mode 100644 index 0000000000000..dc8c961ceecc4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) + +## ExpressionsPublicPlugin class + +Signature: + +```typescript +export declare class ExpressionsPublicPlugin implements Plugin +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(initializerContext)](./kibana-plugin-plugins-expressions-public.expressionspublicplugin._constructor_.md) | | Constructs a new instance of the ExpressionsPublicPlugin class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [setup(core)](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.setup.md) | | | +| [start(core)](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.start.md) | | | +| [stop()](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.stop.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.setup.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.setup.md new file mode 100644 index 0000000000000..11f72a737aa44 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.setup.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) > [setup](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.setup.md) + +## ExpressionsPublicPlugin.setup() method + +Signature: + +```typescript +setup(core: CoreSetup): ExpressionsSetup; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreSetup | | + +Returns: + +`ExpressionsSetup` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.start.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.start.md new file mode 100644 index 0000000000000..75599e2575809 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.start.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) > [start](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.start.md) + +## ExpressionsPublicPlugin.start() method + +Signature: + +```typescript +start(core: CoreStart): ExpressionsStart; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreStart | | + +Returns: + +`ExpressionsStart` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.stop.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.stop.md new file mode 100644 index 0000000000000..2de33ef166b96 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionspublicplugin.stop.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) > [stop](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.stop.md) + +## ExpressionsPublicPlugin.stop() method + +Signature: + +```typescript +stop(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice._constructor_.md new file mode 100644 index 0000000000000..695adad8cbeaf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressionsservice._constructor_.md) + +## ExpressionsService.(constructor) + +Constructs a new instance of the `ExpressionsService` class + +Signature: + +```typescript +constructor({ executor, renderers, }?: ExpressionServiceParams); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { executor, renderers, } | ExpressionServiceParams | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.execute.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.execute.md new file mode 100644 index 0000000000000..e4ab0aa32516c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.execute.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [execute](./kibana-plugin-plugins-expressions-public.expressionsservice.execute.md) + +## ExpressionsService.execute property + +Signature: + +```typescript +readonly execute: ExpressionsServiceStart['execute']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.executor.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.executor.md new file mode 100644 index 0000000000000..f206a0a5c4bb3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.executor.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [executor](./kibana-plugin-plugins-expressions-public.expressionsservice.executor.md) + +## ExpressionsService.executor property + +Signature: + +```typescript +readonly executor: Executor; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.fork.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.fork.md new file mode 100644 index 0000000000000..5273f8d79f5cf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.fork.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [fork](./kibana-plugin-plugins-expressions-public.expressionsservice.fork.md) + +## ExpressionsService.fork property + +Signature: + +```typescript +readonly fork: () => ExpressionsService; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md new file mode 100644 index 0000000000000..7d79a1e407a46 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md) + +## ExpressionsService.getFunction property + +Signature: + +```typescript +readonly getFunction: ExpressionsServiceStart['getFunction']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md new file mode 100644 index 0000000000000..6e1b1ca3e1c6d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getFunctions](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md) + +## ExpressionsService.getFunctions property + +Returns POJO map of all registered expression functions, where keys are names of the functions and values are `ExpressionFunction` instances. + +Signature: + +```typescript +readonly getFunctions: () => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md new file mode 100644 index 0000000000000..5821654cf8ec5 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md) + +## ExpressionsService.getRenderer property + +Signature: + +```typescript +readonly getRenderer: ExpressionsServiceStart['getRenderer']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md new file mode 100644 index 0000000000000..3258717759c90 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getRenderers](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md) + +## ExpressionsService.getRenderers property + +Returns POJO map of all registered expression renderers, where keys are names of the renderers and values are `ExpressionRenderer` instances. + +Signature: + +```typescript +readonly getRenderers: () => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md new file mode 100644 index 0000000000000..e8c451ab88e9f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getType](./kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md) + +## ExpressionsService.getType property + +Signature: + +```typescript +readonly getType: ExpressionsServiceStart['getType']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md new file mode 100644 index 0000000000000..844f581240d45 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) + +## ExpressionsService.getTypes property + +Returns POJO map of all registered expression types, where keys are names of the types and values are `ExpressionType` instances. + +Signature: + +```typescript +readonly getTypes: () => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md new file mode 100644 index 0000000000000..fa93435bffc38 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.md @@ -0,0 +1,72 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) + +## ExpressionsService class + +`ExpressionsService` class is used for multiple purposes: + +1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. `ExpressionsService` defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. `ExpressionsService` creates a bound version of all exported contract functions. 5. Functions are bound the way there are: + +\`\`\`ts registerFunction = (...args: Parameters<Executor\['registerFunction'\]> ): ReturnType<Executor\['registerFunction'\]> => this.executor.registerFunction(...args); \`\`\` + +so that JSDoc appears in developers IDE when they use those `plugins.expressions.registerFunction(`. + +Signature: + +```typescript +export declare class ExpressionsService +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)({ executor, renderers, })](./kibana-plugin-plugins-expressions-public.expressionsservice._constructor_.md) | | Constructs a new instance of the ExpressionsService class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [execute](./kibana-plugin-plugins-expressions-public.expressionsservice.execute.md) | | ExpressionsServiceStart['execute'] | | +| [executor](./kibana-plugin-plugins-expressions-public.expressionsservice.executor.md) | | Executor | | +| [fork](./kibana-plugin-plugins-expressions-public.expressionsservice.fork.md) | | () => ExpressionsService | | +| [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunction.md) | | ExpressionsServiceStart['getFunction'] | | +| [getFunctions](./kibana-plugin-plugins-expressions-public.expressionsservice.getfunctions.md) | | () => ReturnType<Executor['getFunctions']> | Returns POJO map of all registered expression functions, where keys are names of the functions and values are ExpressionFunction instances. | +| [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderer.md) | | ExpressionsServiceStart['getRenderer'] | | +| [getRenderers](./kibana-plugin-plugins-expressions-public.expressionsservice.getrenderers.md) | | () => ReturnType<ExpressionRendererRegistry['toJS']> | Returns POJO map of all registered expression renderers, where keys are names of the renderers and values are ExpressionRenderer instances. | +| [getType](./kibana-plugin-plugins-expressions-public.expressionsservice.gettype.md) | | ExpressionsServiceStart['getType'] | | +| [getTypes](./kibana-plugin-plugins-expressions-public.expressionsservice.gettypes.md) | | () => ReturnType<Executor['getTypes']> | Returns POJO map of all registered expression types, where keys are names of the types and values are ExpressionType instances. | +| [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) | | (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void | Register an expression function, which will be possible to execute as part of the expression pipeline.Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is. +```ts +expressions.registerFunction({ + name: 'sleep', + args: { + time: { + aliases: ['_'], + help: 'Time in milliseconds for how long to sleep', + types: ['number'], + }, + }, + help: '', + fn: async (input, args, context) => { + await new Promise(r => setTimeout(r, args.time)); + return input; + }, +} + +``` +The actual function is defined in the fn key. The function can be \*async\*. It receives three arguments: (1) input is the output of the previous function or the initial input of the expression if the function is first in chain; (2) args are function arguments as defined in expression string, that can be edited by user (e.g in case of Canvas); (3) context is a shared object passed to all functions that can be used for side-effects. | +| [registerRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.registerrenderer.md) | | (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void | | +| [registerType](./kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md) | | (typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)) => void | | +| [renderers](./kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md) | | ExpressionRendererRegistry | | +| [run](./kibana-plugin-plugins-expressions-public.expressionsservice.run.md) | | ExpressionsServiceStart['run'] | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [setup()](./kibana-plugin-plugins-expressions-public.expressionsservice.setup.md) | | Returns Kibana Platform \*setup\* life-cycle contract. Useful to return the same contract on server-side and browser-side. | +| [start()](./kibana-plugin-plugins-expressions-public.expressionsservice.start.md) | | Returns Kibana Platform \*start\* life-cycle contract. Useful to return the same contract on server-side and browser-side. | +| [stop()](./kibana-plugin-plugins-expressions-public.expressionsservice.stop.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md new file mode 100644 index 0000000000000..0653e68bb4837 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [registerFunction](./kibana-plugin-plugins-expressions-public.expressionsservice.registerfunction.md) + +## ExpressionsService.registerFunction property + +Register an expression function, which will be possible to execute as part of the expression pipeline. + +Below we register a function which simply sleeps for given number of milliseconds to delay the execution and outputs its input as-is. + +```ts +expressions.registerFunction({ + name: 'sleep', + args: { + time: { + aliases: ['_'], + help: 'Time in milliseconds for how long to sleep', + types: ['number'], + }, + }, + help: '', + fn: async (input, args, context) => { + await new Promise(r => setTimeout(r, args.time)); + return input; + }, +} + +``` +The actual function is defined in the `fn` key. The function can be \*async\*. It receives three arguments: (1) `input` is the output of the previous function or the initial input of the expression if the function is first in chain; (2) `args` are function arguments as defined in expression string, that can be edited by user (e.g in case of Canvas); (3) `context` is a shared object passed to all functions that can be used for side-effects. + +Signature: + +```typescript +readonly registerFunction: (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerrenderer.md new file mode 100644 index 0000000000000..7aff36e7fd817 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registerrenderer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [registerRenderer](./kibana-plugin-plugins-expressions-public.expressionsservice.registerrenderer.md) + +## ExpressionsService.registerRenderer property + +Signature: + +```typescript +readonly registerRenderer: (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md new file mode 100644 index 0000000000000..e6e71e5e7e7e9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [registerType](./kibana-plugin-plugins-expressions-public.expressionsservice.registertype.md) + +## ExpressionsService.registerType property + +Signature: + +```typescript +readonly registerType: (typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md new file mode 100644 index 0000000000000..e43e9a21050ea --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [renderers](./kibana-plugin-plugins-expressions-public.expressionsservice.renderers.md) + +## ExpressionsService.renderers property + +Signature: + +```typescript +readonly renderers: ExpressionRendererRegistry; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.run.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.run.md new file mode 100644 index 0000000000000..47469167f6360 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.run.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [run](./kibana-plugin-plugins-expressions-public.expressionsservice.run.md) + +## ExpressionsService.run property + +Signature: + +```typescript +readonly run: ExpressionsServiceStart['run']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.setup.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.setup.md new file mode 100644 index 0000000000000..a51f3f073d518 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.setup.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [setup](./kibana-plugin-plugins-expressions-public.expressionsservice.setup.md) + +## ExpressionsService.setup() method + +Returns Kibana Platform \*setup\* life-cycle contract. Useful to return the same contract on server-side and browser-side. + +Signature: + +```typescript +setup(): ExpressionsServiceSetup; +``` +Returns: + +`ExpressionsServiceSetup` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.start.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.start.md new file mode 100644 index 0000000000000..766d703a0729d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.start.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [start](./kibana-plugin-plugins-expressions-public.expressionsservice.start.md) + +## ExpressionsService.start() method + +Returns Kibana Platform \*start\* life-cycle contract. Useful to return the same contract on server-side and browser-side. + +Signature: + +```typescript +start(): ExpressionsServiceStart; +``` +Returns: + +`ExpressionsServiceStart` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.stop.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.stop.md new file mode 100644 index 0000000000000..a32bb4a8bb009 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservice.stop.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) > [stop](./kibana-plugin-plugins-expressions-public.expressionsservice.stop.md) + +## ExpressionsService.stop() method + +Signature: + +```typescript +stop(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicesetup.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicesetup.md new file mode 100644 index 0000000000000..4cf3fb9b53978 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicesetup.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceSetup](./kibana-plugin-plugins-expressions-public.expressionsservicesetup.md) + +## ExpressionsServiceSetup type + +The public contract that `ExpressionsService` provides to other plugins in Kibana Platform in \*setup\* life-cycle. + +Signature: + +```typescript +export declare type ExpressionsServiceSetup = Pick; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md new file mode 100644 index 0000000000000..b8211a6bff27c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [execute](./kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md) + +## ExpressionsServiceStart.execute property + +Starts expression execution and immediately returns `ExecutionContract` instance that tracks the progress of the execution and can be used to interact with the execution. + +Signature: + +```typescript +execute: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md new file mode 100644 index 0000000000000..dd18daceb9539 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [fork](./kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md) + +## ExpressionsServiceStart.fork property + +Create a new instance of `ExpressionsService`. The new instance inherits all state of the original `ExpressionsService`, including all expression types, expression functions and context. Also, all new types and functions registered in the original services AFTER the forking event will be available in the forked instance. However, all new types and functions registered in the forked instances will NOT be available to the original service. + +Signature: + +```typescript +fork: () => ExpressionsService; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md new file mode 100644 index 0000000000000..d1a9bbce2a27e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md) + +## ExpressionsServiceStart.getFunction property + +Get a registered `ExpressionFunction` by its name, which was registered using the `registerFunction` method. The returned `ExpressionFunction` instance is an internal representation of the function in Expressions service - do not mutate that object. + +Signature: + +```typescript +getFunction: (name: string) => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md new file mode 100644 index 0000000000000..ef98fd633cb0c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md) + +## ExpressionsServiceStart.getRenderer property + +Get a registered `ExpressionRenderer` by its name, which was registered using the `registerRenderer` method. The returned `ExpressionRenderer` instance is an internal representation of the renderer in Expressions service - do not mutate that object. + +Signature: + +```typescript +getRenderer: (name: string) => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md new file mode 100644 index 0000000000000..e9ec1733513ba --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [getType](./kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md) + +## ExpressionsServiceStart.getType property + +Get a registered `ExpressionType` by its name, which was registered using the `registerType` method. The returned `ExpressionType` instance is an internal representation of the type in Expressions service - do not mutate that object. + +Signature: + +```typescript +getType: (name: string) => ReturnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md new file mode 100644 index 0000000000000..34bf16c121326 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) + +## ExpressionsServiceStart interface + +The public contract that `ExpressionsService` provides to other plugins in Kibana Platform in \*start\* life-cycle. + +Signature: + +```typescript +export interface ExpressionsServiceStart +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [execute](./kibana-plugin-plugins-expressions-public.expressionsservicestart.execute.md) | <Input = unknown, Output = unknown, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract<ExtraContext, Input, Output> | Starts expression execution and immediately returns ExecutionContract instance that tracks the progress of the execution and can be used to interact with the execution. | +| [fork](./kibana-plugin-plugins-expressions-public.expressionsservicestart.fork.md) | () => ExpressionsService | Create a new instance of ExpressionsService. The new instance inherits all state of the original ExpressionsService, including all expression types, expression functions and context. Also, all new types and functions registered in the original services AFTER the forking event will be available in the forked instance. However, all new types and functions registered in the forked instances will NOT be available to the original service. | +| [getFunction](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getfunction.md) | (name: string) => ReturnType<Executor['getFunction']> | Get a registered ExpressionFunction by its name, which was registered using the registerFunction method. The returned ExpressionFunction instance is an internal representation of the function in Expressions service - do not mutate that object. | +| [getRenderer](./kibana-plugin-plugins-expressions-public.expressionsservicestart.getrenderer.md) | (name: string) => ReturnType<ExpressionRendererRegistry['get']> | Get a registered ExpressionRenderer by its name, which was registered using the registerRenderer method. The returned ExpressionRenderer instance is an internal representation of the renderer in Expressions service - do not mutate that object. | +| [getType](./kibana-plugin-plugins-expressions-public.expressionsservicestart.gettype.md) | (name: string) => ReturnType<Executor['getType']> | Get a registered ExpressionType by its name, which was registered using the registerType method. The returned ExpressionType instance is an internal representation of the type in Expressions service - do not mutate that object. | +| [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) | <Input, Output, ExtraContext extends Record<string, unknown> = Record<string, unknown>>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise<Output> | Executes expression string or a parsed expression AST and immediately returns the result.Below example will execute sleep 100 | clog expression with 123 initial input to the first function. +```ts +expressions.run('sleep 100 | clog', 123); + +``` +- sleep 100 will delay execution by 100 milliseconds and pass the 123 input as its output. - clog will print to console 123 and pass it as its output. - The final result of the execution will be 123.Optionally, you can pass an object as the third argument which will be used to extend the ExecutionContext&mdash;an object passed to each function as the third argument, that allows functions to perform side-effects. +```ts +expressions.run('...', null, { elasticsearchClient }); + +``` + | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md new file mode 100644 index 0000000000000..578c583624ad0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) > [run](./kibana-plugin-plugins-expressions-public.expressionsservicestart.run.md) + +## ExpressionsServiceStart.run property + +Executes expression string or a parsed expression AST and immediately returns the result. + +Below example will execute `sleep 100 | clog` expression with `123` initial input to the first function. + +```ts +expressions.run('sleep 100 | clog', 123); + +``` +- `sleep 100` will delay execution by 100 milliseconds and pass the `123` input as its output. - `clog` will print to console `123` and pass it as its output. - The final result of the execution will be `123`. + +Optionally, you can pass an object as the third argument which will be used to extend the `ExecutionContext`&mdash;an object passed to each function as the third argument, that allows functions to perform side-effects. + +```ts +expressions.run('...', null, { elasticsearchClient }); + +``` + +Signature: + +```typescript +run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionssetup.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionssetup.md new file mode 100644 index 0000000000000..01a894ae8fba6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionssetup.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsSetup](./kibana-plugin-plugins-expressions-public.expressionssetup.md) + +## ExpressionsSetup type + +Expressions public setup contract, extends [ExpressionsServiceSetup](./kibana-plugin-plugins-expressions-public.expressionsservicesetup.md) + +Signature: + +```typescript +export declare type ExpressionsSetup = ExpressionsServiceSetup; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionloader.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionloader.md new file mode 100644 index 0000000000000..b7226b12b0d2b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionloader.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) > [ExpressionLoader](./kibana-plugin-plugins-expressions-public.expressionsstart.expressionloader.md) + +## ExpressionsStart.ExpressionLoader property + +Signature: + +```typescript +ExpressionLoader: typeof ExpressionLoader; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionrenderhandler.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionrenderhandler.md new file mode 100644 index 0000000000000..a78bb6f154c46 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.expressionrenderhandler.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) > [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionsstart.expressionrenderhandler.md) + +## ExpressionsStart.ExpressionRenderHandler property + +Signature: + +```typescript +ExpressionRenderHandler: typeof ExpressionRenderHandler; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.loader.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.loader.md new file mode 100644 index 0000000000000..109d8e8bcab66 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.loader.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) > [loader](./kibana-plugin-plugins-expressions-public.expressionsstart.loader.md) + +## ExpressionsStart.loader property + +Signature: + +```typescript +loader: IExpressionLoader; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.md new file mode 100644 index 0000000000000..ac4004590b5a6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) + +## ExpressionsStart interface + +Expressions public start contrect, extends + +Signature: + +```typescript +export interface ExpressionsStart extends ExpressionsServiceStart +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ExpressionLoader](./kibana-plugin-plugins-expressions-public.expressionsstart.expressionloader.md) | typeof ExpressionLoader | | +| [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionsstart.expressionrenderhandler.md) | typeof ExpressionRenderHandler | | +| [loader](./kibana-plugin-plugins-expressions-public.expressionsstart.loader.md) | IExpressionLoader | | +| [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionsstart.reactexpressionrenderer.md) | typeof ReactExpressionRenderer | | +| [render](./kibana-plugin-plugins-expressions-public.expressionsstart.render.md) | typeof render | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.reactexpressionrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.reactexpressionrenderer.md new file mode 100644 index 0000000000000..bbd7253a747c4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.reactexpressionrenderer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) > [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionsstart.reactexpressionrenderer.md) + +## ExpressionsStart.ReactExpressionRenderer property + +Signature: + +```typescript +ReactExpressionRenderer: typeof ReactExpressionRenderer; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.render.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.render.md new file mode 100644 index 0000000000000..fcf279206119e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.render.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) > [render](./kibana-plugin-plugins-expressions-public.expressionsstart.render.md) + +## ExpressionsStart.render property + +Signature: + +```typescript +render: typeof render; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype._constructor_.md new file mode 100644 index 0000000000000..2302be5643722 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.expressiontype._constructor_.md) + +## ExpressionType.(constructor) + +Constructs a new instance of the `ExpressionType` class + +Signature: + +```typescript +constructor(definition: AnyExpressionTypeDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| definition | AnyExpressionTypeDefinition | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.castsfrom.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.castsfrom.md new file mode 100644 index 0000000000000..e238db1b45086 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.castsfrom.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [castsFrom](./kibana-plugin-plugins-expressions-public.expressiontype.castsfrom.md) + +## ExpressionType.castsFrom property + +Signature: + +```typescript +castsFrom: (value: ExpressionValue) => boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.caststo.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.caststo.md new file mode 100644 index 0000000000000..36e03e6f3d53f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.caststo.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [castsTo](./kibana-plugin-plugins-expressions-public.expressiontype.caststo.md) + +## ExpressionType.castsTo property + +Signature: + +```typescript +castsTo: (value: ExpressionValue) => boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.create.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.create.md new file mode 100644 index 0000000000000..e2da70b50b0d4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.create.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [create](./kibana-plugin-plugins-expressions-public.expressiontype.create.md) + +## ExpressionType.create property + +Signature: + +```typescript +create: unknown; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.deserialize.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.deserialize.md new file mode 100644 index 0000000000000..d47056817358c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.deserialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [deserialize](./kibana-plugin-plugins-expressions-public.expressiontype.deserialize.md) + +## ExpressionType.deserialize property + +Signature: + +```typescript +deserialize?: (serialized: any) => ExpressionValue; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.from.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.from.md new file mode 100644 index 0000000000000..51a36f614fbbf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.from.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [from](./kibana-plugin-plugins-expressions-public.expressiontype.from.md) + +## ExpressionType.from property + +Signature: + +```typescript +from: (value: ExpressionValue, types: Record) => any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.getfromfn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.getfromfn.md new file mode 100644 index 0000000000000..10d7bb4331916 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.getfromfn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [getFromFn](./kibana-plugin-plugins-expressions-public.expressiontype.getfromfn.md) + +## ExpressionType.getFromFn property + +Signature: + +```typescript +getFromFn: (typeName: string) => undefined | ExpressionValueConverter; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.gettofn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.gettofn.md new file mode 100644 index 0000000000000..25b71163e5709 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.gettofn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [getToFn](./kibana-plugin-plugins-expressions-public.expressiontype.gettofn.md) + +## ExpressionType.getToFn property + +Signature: + +```typescript +getToFn: (typeName: string) => undefined | ExpressionValueConverter; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.help.md new file mode 100644 index 0000000000000..e27e1dea2a872 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [help](./kibana-plugin-plugins-expressions-public.expressiontype.help.md) + +## ExpressionType.help property + +A short help text. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.md new file mode 100644 index 0000000000000..acb72b796cf1d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) + +## ExpressionType class + +Signature: + +```typescript +export declare class ExpressionType +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(definition)](./kibana-plugin-plugins-expressions-public.expressiontype._constructor_.md) | | Constructs a new instance of the ExpressionType class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [castsFrom](./kibana-plugin-plugins-expressions-public.expressiontype.castsfrom.md) | | (value: ExpressionValue) => boolean | | +| [castsTo](./kibana-plugin-plugins-expressions-public.expressiontype.caststo.md) | | (value: ExpressionValue) => boolean | | +| [create](./kibana-plugin-plugins-expressions-public.expressiontype.create.md) | | unknown | | +| [deserialize](./kibana-plugin-plugins-expressions-public.expressiontype.deserialize.md) | | (serialized: any) => ExpressionValue | | +| [from](./kibana-plugin-plugins-expressions-public.expressiontype.from.md) | | (value: ExpressionValue, types: Record<string, ExpressionType>) => any | | +| [getFromFn](./kibana-plugin-plugins-expressions-public.expressiontype.getfromfn.md) | | (typeName: string) => undefined | ExpressionValueConverter<ExpressionValue, ExpressionValue> | | +| [getToFn](./kibana-plugin-plugins-expressions-public.expressiontype.gettofn.md) | | (typeName: string) => undefined | ExpressionValueConverter<ExpressionValue, ExpressionValue> | | +| [help](./kibana-plugin-plugins-expressions-public.expressiontype.help.md) | | string | A short help text. | +| [name](./kibana-plugin-plugins-expressions-public.expressiontype.name.md) | | string | | +| [serialize](./kibana-plugin-plugins-expressions-public.expressiontype.serialize.md) | | (value: ExpressionValue) => any | Optional serialization (used when passing context around client/server). | +| [to](./kibana-plugin-plugins-expressions-public.expressiontype.to.md) | | (value: ExpressionValue, toTypeName: string, types: Record<string, ExpressionType>) => any | | +| [validate](./kibana-plugin-plugins-expressions-public.expressiontype.validate.md) | | (type: any) => void | Error | Type validation, useful for checking function output. | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.name.md new file mode 100644 index 0000000000000..8d14f6e4f6bd8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [name](./kibana-plugin-plugins-expressions-public.expressiontype.name.md) + +## ExpressionType.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.serialize.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.serialize.md new file mode 100644 index 0000000000000..cb4821b97e022 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.serialize.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [serialize](./kibana-plugin-plugins-expressions-public.expressiontype.serialize.md) + +## ExpressionType.serialize property + +Optional serialization (used when passing context around client/server). + +Signature: + +```typescript +serialize?: (value: ExpressionValue) => any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.to.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.to.md new file mode 100644 index 0000000000000..8045c5df638b0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.to.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [to](./kibana-plugin-plugins-expressions-public.expressiontype.to.md) + +## ExpressionType.to property + +Signature: + +```typescript +to: (value: ExpressionValue, toTypeName: string, types: Record) => any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.validate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.validate.md new file mode 100644 index 0000000000000..7214467b2b444 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontype.validate.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) > [validate](./kibana-plugin-plugins-expressions-public.expressiontype.validate.md) + +## ExpressionType.validate property + +Type validation, useful for checking function output. + +Signature: + +```typescript +validate: (type: any) => void | Error; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.deserialize.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.deserialize.md new file mode 100644 index 0000000000000..75dac1e991f65 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.deserialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [deserialize](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.deserialize.md) + +## ExpressionTypeDefinition.deserialize property + +Signature: + +```typescript +deserialize?: (type: SerializedType) => Value; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.from.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.from.md new file mode 100644 index 0000000000000..ac8920066eda7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.from.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [from](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.from.md) + +## ExpressionTypeDefinition.from property + +Signature: + +```typescript +from?: { + [type: string]: ExpressionValueConverter; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.help.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.help.md new file mode 100644 index 0000000000000..ad5e5eb38fa72 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [help](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.help.md) + +## ExpressionTypeDefinition.help property + +Signature: + +```typescript +help?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.md new file mode 100644 index 0000000000000..8c183e9a6de80 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) + +## ExpressionTypeDefinition interface + +A generic type which represents a custom Expression Type Definition that's registered to the Interpreter. + +Signature: + +```typescript +export interface ExpressionTypeDefinition +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [deserialize](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.deserialize.md) | (type: SerializedType) => Value | | +| [from](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.from.md) | {
[type: string]: ExpressionValueConverter<any, Value>;
} | | +| [help](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.help.md) | string | | +| [name](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.name.md) | Name | | +| [serialize](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.serialize.md) | (type: Value) => SerializedType | | +| [to](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.to.md) | {
[type: string]: ExpressionValueConverter<Value, any>;
} | | +| [validate](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.validate.md) | (type: any) => void | Error | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.name.md new file mode 100644 index 0000000000000..eb79d01040373 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [name](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.name.md) + +## ExpressionTypeDefinition.name property + +Signature: + +```typescript +name: Name; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.serialize.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.serialize.md new file mode 100644 index 0000000000000..5881ddbe5a6c4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.serialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [serialize](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.serialize.md) + +## ExpressionTypeDefinition.serialize property + +Signature: + +```typescript +serialize?: (type: Value) => SerializedType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.to.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.to.md new file mode 100644 index 0000000000000..282cdcdfb342d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.to.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [to](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.to.md) + +## ExpressionTypeDefinition.to property + +Signature: + +```typescript +to?: { + [type: string]: ExpressionValueConverter; + }; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.validate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.validate.md new file mode 100644 index 0000000000000..67d5e832c6284 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypedefinition.validate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) > [validate](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.validate.md) + +## ExpressionTypeDefinition.validate property + +Signature: + +```typescript +validate?: (type: any) => void | Error; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.css.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.css.md new file mode 100644 index 0000000000000..ca8e881ef7e46 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.css.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-public.expressiontypestyle.md) > [css](./kibana-plugin-plugins-expressions-public.expressiontypestyle.css.md) + +## ExpressionTypeStyle.css property + +Signature: + +```typescript +css: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.md new file mode 100644 index 0000000000000..4e1cc86699f2d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-public.expressiontypestyle.md) + +## ExpressionTypeStyle interface + +An object that represents style information, typically CSS. + +Signature: + +```typescript +export interface ExpressionTypeStyle +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [css](./kibana-plugin-plugins-expressions-public.expressiontypestyle.css.md) | string | | +| [spec](./kibana-plugin-plugins-expressions-public.expressiontypestyle.spec.md) | CSSStyle | | +| [type](./kibana-plugin-plugins-expressions-public.expressiontypestyle.type.md) | 'style' | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.spec.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.spec.md new file mode 100644 index 0000000000000..e732893366a36 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.spec.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-public.expressiontypestyle.md) > [spec](./kibana-plugin-plugins-expressions-public.expressiontypestyle.spec.md) + +## ExpressionTypeStyle.spec property + +Signature: + +```typescript +spec: CSSStyle; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.type.md new file mode 100644 index 0000000000000..01dd9b0da1072 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressiontypestyle.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-public.expressiontypestyle.md) > [type](./kibana-plugin-plugins-expressions-public.expressiontypestyle.type.md) + +## ExpressionTypeStyle.type property + +Signature: + +```typescript +type: 'style'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalue.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalue.md new file mode 100644 index 0000000000000..53ab339df902a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalue.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValue](./kibana-plugin-plugins-expressions-public.expressionvalue.md) + +## ExpressionValue type + +Signature: + +```typescript +export declare type ExpressionValue = ExpressionValueUnboxed | ExpressionValueBoxed; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueboxed.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueboxed.md new file mode 100644 index 0000000000000..6d8f060d4f91f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueboxed.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueBoxed](./kibana-plugin-plugins-expressions-public.expressionvalueboxed.md) + +## ExpressionValueBoxed type + +Signature: + +```typescript +export declare type ExpressionValueBoxed = { + type: Type; +} & Value; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueconverter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueconverter.md new file mode 100644 index 0000000000000..95e69645b53ee --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueconverter.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueConverter](./kibana-plugin-plugins-expressions-public.expressionvalueconverter.md) + +## ExpressionValueConverter type + +Signature: + +```typescript +export declare type ExpressionValueConverter = (input: I, availableTypes: Record) => O; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md new file mode 100644 index 0000000000000..4a714fe62424f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueerror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueError](./kibana-plugin-plugins-expressions-public.expressionvalueerror.md) + +## ExpressionValueError type + +Signature: + +```typescript +export declare type ExpressionValueError = ExpressionValueBoxed<'error', { + error: { + message: string; + type?: string; + name?: string; + stack?: string; + original?: Error; + }; + info?: unknown; +}>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluefilter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluefilter.md new file mode 100644 index 0000000000000..07c1bfe9a96d6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluefilter.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueFilter](./kibana-plugin-plugins-expressions-public.expressionvaluefilter.md) + +## ExpressionValueFilter type + +Represents an object that is a Filter. + +Signature: + +```typescript +export declare type ExpressionValueFilter = ExpressionValueBoxed<'filter', { + filterType?: string; + value?: string; + column?: string; + and: ExpressionValueFilter[]; + to?: string; + from?: string; + query?: string | null; +}>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluenum.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluenum.md new file mode 100644 index 0000000000000..fc92777ffd5b6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluenum.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueNum](./kibana-plugin-plugins-expressions-public.expressionvaluenum.md) + +## ExpressionValueNum type + +Signature: + +```typescript +export declare type ExpressionValueNum = ExpressionValueBoxed<'num', { + value: number; +}>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluerender.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluerender.md new file mode 100644 index 0000000000000..be9e7f859daec --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluerender.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueRender](./kibana-plugin-plugins-expressions-public.expressionvaluerender.md) + +## ExpressionValueRender type + +Represents an object that is intended to be rendered. + +Signature: + +```typescript +export declare type ExpressionValueRender = ExpressionValueBoxed; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluesearchcontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluesearchcontext.md new file mode 100644 index 0000000000000..bf64dfe4c86f7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvaluesearchcontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueSearchContext](./kibana-plugin-plugins-expressions-public.expressionvaluesearchcontext.md) + +## ExpressionValueSearchContext type + +Signature: + +```typescript +export declare type ExpressionValueSearchContext = ExpressionValueBoxed<'kibana_context', ExecutionContextSearch>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueunboxed.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueunboxed.md new file mode 100644 index 0000000000000..fbc37fe667d5e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionvalueunboxed.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionValueUnboxed](./kibana-plugin-plugins-expressions-public.expressionvalueunboxed.md) + +## ExpressionValueUnboxed type + +Signature: + +```typescript +export declare type ExpressionValueUnboxed = any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.label.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.label.md new file mode 100644 index 0000000000000..87294ce59feb6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.label.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Font](./kibana-plugin-plugins-expressions-public.font.md) > [label](./kibana-plugin-plugins-expressions-public.font.label.md) + +## Font.label property + +Signature: + +```typescript +label: FontLabel; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.md new file mode 100644 index 0000000000000..ef63d28fe6fba --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Font](./kibana-plugin-plugins-expressions-public.font.md) + +## Font interface + +An interface representing a font in Canvas, with a textual label and the CSS `font-value`. + +Signature: + +```typescript +export interface Font +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [label](./kibana-plugin-plugins-expressions-public.font.label.md) | FontLabel | | +| [value](./kibana-plugin-plugins-expressions-public.font.value.md) | FontValue | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.value.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.value.md new file mode 100644 index 0000000000000..cada244174785 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.font.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Font](./kibana-plugin-plugins-expressions-public.font.md) > [value](./kibana-plugin-plugins-expressions-public.font.value.md) + +## Font.value property + +Signature: + +```typescript +value: FontValue; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontlabel.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontlabel.md new file mode 100644 index 0000000000000..5af3427730ad1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontlabel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FontLabel](./kibana-plugin-plugins-expressions-public.fontlabel.md) + +## FontLabel type + +This type contains a unions of all supported font labels, or the the name of the font the user would see in a UI. + +Signature: + +```typescript +export declare type FontLabel = typeof fonts[number]['label']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontstyle.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontstyle.md new file mode 100644 index 0000000000000..9f70d91c7ac9b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontstyle.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FontStyle](./kibana-plugin-plugins-expressions-public.fontstyle.md) + +## FontStyle enum + +Enum of supported CSS `font-style` properties. + +Signature: + +```typescript +export declare enum FontStyle +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ITALIC | "italic" | | +| NORMAL | "normal" | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontvalue.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontvalue.md new file mode 100644 index 0000000000000..f03c9b61cb733 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontvalue.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FontValue](./kibana-plugin-plugins-expressions-public.fontvalue.md) + +## FontValue type + +This type contains a union of all supported font values, equivalent to the CSS `font-value` property. + +Signature: + +```typescript +export declare type FontValue = typeof fonts[number]['value']; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontweight.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontweight.md new file mode 100644 index 0000000000000..43388a3de11cc --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.fontweight.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FontWeight](./kibana-plugin-plugins-expressions-public.fontweight.md) + +## FontWeight enum + +Enum of supported CSS `font-weight` properties. + +Signature: + +```typescript +export declare enum FontWeight +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BOLD | "bold" | | +| BOLDER | "bolder" | | +| EIGHT | "800" | | +| FIVE | "500" | | +| FOUR | "400" | | +| LIGHTER | "lighter" | | +| NINE | "900" | | +| NORMAL | "normal" | | +| ONE | "100" | | +| SEVEN | "700" | | +| SIX | "600" | | +| THREE | "300" | | +| TWO | "200" | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.format.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.format.md new file mode 100644 index 0000000000000..27a9690e6fb0d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.format.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [format](./kibana-plugin-plugins-expressions-public.format.md) + +## format() function + +Signature: + +```typescript +export declare function format(ast: T, type: T extends ExpressionAstExpression ? 'expression' : 'argument'): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | T | | +| type | T extends ExpressionAstExpression ? 'expression' : 'argument' | | + +Returns: + +`string` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.formatexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.formatexpression.md new file mode 100644 index 0000000000000..425aa9c6171fc --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.formatexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [formatExpression](./kibana-plugin-plugins-expressions-public.formatexpression.md) + +## formatExpression() function + +Given expression pipeline AST, returns formatted string. + +Signature: + +```typescript +export declare function formatExpression(ast: ExpressionAstExpression): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | + +Returns: + +`string` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry._constructor_.md new file mode 100644 index 0000000000000..2ab299e3d32f4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.functionsregistry._constructor_.md) + +## FunctionsRegistry.(constructor) + +Constructs a new instance of the `FunctionsRegistry` class + +Signature: + +```typescript +constructor(executor: Executor); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| executor | Executor<any> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.get.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.get.md new file mode 100644 index 0000000000000..3ed2807028299 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) > [get](./kibana-plugin-plugins-expressions-public.functionsregistry.get.md) + +## FunctionsRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionFunction | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionFunction | null` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.md new file mode 100644 index 0000000000000..b32623934ee92 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) + +## FunctionsRegistry class + +Signature: + +```typescript +export declare class FunctionsRegistry implements IRegistry +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(executor)](./kibana-plugin-plugins-expressions-public.functionsregistry._constructor_.md) | | Constructs a new instance of the FunctionsRegistry class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-public.functionsregistry.get.md) | | | +| [register(functionDefinition)](./kibana-plugin-plugins-expressions-public.functionsregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-public.functionsregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-public.functionsregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.register.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.register.md new file mode 100644 index 0000000000000..32f7f389e8958 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) > [register](./kibana-plugin-plugins-expressions-public.functionsregistry.register.md) + +## FunctionsRegistry.register() method + +Signature: + +```typescript +register(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.toarray.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.toarray.md new file mode 100644 index 0000000000000..5bc482097a175 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) > [toArray](./kibana-plugin-plugins-expressions-public.functionsregistry.toarray.md) + +## FunctionsRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionFunction[]; +``` +Returns: + +`ExpressionFunction[]` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.tojs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.tojs.md new file mode 100644 index 0000000000000..d6790fb8f726e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.functionsregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) > [toJS](./kibana-plugin-plugins-expressions-public.functionsregistry.tojs.md) + +## FunctionsRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md new file mode 100644 index 0000000000000..40dcf07667b1b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [context](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md) + +## IExpressionLoaderParams.context property + +Signature: + +```typescript +context?: ExpressionValue; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md new file mode 100644 index 0000000000000..00ff3d498eb5c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [customFunctions](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md) + +## IExpressionLoaderParams.customFunctions property + +Signature: + +```typescript +customFunctions?: []; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md new file mode 100644 index 0000000000000..72b82e2d41b05 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [customRenderers](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md) + +## IExpressionLoaderParams.customRenderers property + +Signature: + +```typescript +customRenderers?: []; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md new file mode 100644 index 0000000000000..62483016d3aee --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [disableCaching](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md) + +## IExpressionLoaderParams.disableCaching property + +Signature: + +```typescript +disableCaching?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md new file mode 100644 index 0000000000000..52f2a6e56d133 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [inspectorAdapters](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md) + +## IExpressionLoaderParams.inspectorAdapters property + +Signature: + +```typescript +inspectorAdapters?: Adapters; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md new file mode 100644 index 0000000000000..b8a174f93fb99 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) + +## IExpressionLoaderParams interface + +Signature: + +```typescript +export interface IExpressionLoaderParams +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.context.md) | ExpressionValue | | +| [customFunctions](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customfunctions.md) | [] | | +| [customRenderers](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.customrenderers.md) | [] | | +| [disableCaching](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.disablecaching.md) | boolean | | +| [inspectorAdapters](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.inspectoradapters.md) | Adapters | | +| [onRenderError](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md) | RenderErrorHandlerFnType | | +| [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | ExecutionContextSearch | | +| [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) | unknown | | +| [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) | Record<string, any> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md new file mode 100644 index 0000000000000..f45a9c76242c4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [onRenderError](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.onrendererror.md) + +## IExpressionLoaderParams.onRenderError property + +Signature: + +```typescript +onRenderError?: RenderErrorHandlerFnType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md new file mode 100644 index 0000000000000..523d0c562f7ca --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) + +## IExpressionLoaderParams.searchContext property + +Signature: + +```typescript +searchContext?: ExecutionContextSearch; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md new file mode 100644 index 0000000000000..dca5032dabc78 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) + +## IExpressionLoaderParams.uiState property + +Signature: + +```typescript +uiState?: unknown; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md new file mode 100644 index 0000000000000..0a04671919bd0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) + +## IExpressionLoaderParams.variables property + +Signature: + +```typescript +variables?: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.done.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.done.md new file mode 100644 index 0000000000000..533cf498d72cf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.done.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [done](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.done.md) + +## IInterpreterRenderHandlers.done property + +Done increments the number of rendering successes + +Signature: + +```typescript +done: () => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md new file mode 100644 index 0000000000000..476167965927d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [event](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md) + +## IInterpreterRenderHandlers.event property + +Signature: + +```typescript +event: (event: any) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md new file mode 100644 index 0000000000000..9dbd18ae687b4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) + +## IInterpreterRenderHandlers interface + +Signature: + +```typescript +export interface IInterpreterRenderHandlers +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [done](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.done.md) | () => void | Done increments the number of rendering successes | +| [event](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md) | (event: any) => void | | +| [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | +| [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) | () => void | | +| [update](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md) | (params: any) => void | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md new file mode 100644 index 0000000000000..b68c2023fdc8a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) + +## IInterpreterRenderHandlers.onDestroy property + +Signature: + +```typescript +onDestroy: (fn: () => void) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md new file mode 100644 index 0000000000000..0acd440e84f12 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) + +## IInterpreterRenderHandlers.reload property + +Signature: + +```typescript +reload: () => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md new file mode 100644 index 0000000000000..28fcb58fb3c10 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [update](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.update.md) + +## IInterpreterRenderHandlers.update property + +Signature: + +```typescript +update: (params: any) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.interpretererrortype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.interpretererrortype.md new file mode 100644 index 0000000000000..8cb346eda4d74 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.interpretererrortype.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [InterpreterErrorType](./kibana-plugin-plugins-expressions-public.interpretererrortype.md) + +## InterpreterErrorType type + +> Warning: This API is now obsolete. +> +> Exported for backwards compatibility. +> + +Signature: + +```typescript +export declare type InterpreterErrorType = ExpressionValueError; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.get.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.get.md new file mode 100644 index 0000000000000..9aa696869eaa3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) > [get](./kibana-plugin-plugins-expressions-public.iregistry.get.md) + +## IRegistry.get() method + +Signature: + +```typescript +get(id: string): T | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`T | null` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.md new file mode 100644 index 0000000000000..64991d90f2ae0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) + +## IRegistry interface + +Signature: + +```typescript +export interface IRegistry +``` + +## Methods + +| Method | Description | +| --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-public.iregistry.get.md) | | +| [toArray()](./kibana-plugin-plugins-expressions-public.iregistry.toarray.md) | | +| [toJS()](./kibana-plugin-plugins-expressions-public.iregistry.tojs.md) | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.toarray.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.toarray.md new file mode 100644 index 0000000000000..36b16ca48323f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) > [toArray](./kibana-plugin-plugins-expressions-public.iregistry.toarray.md) + +## IRegistry.toArray() method + +Signature: + +```typescript +toArray(): T[]; +``` +Returns: + +`T[]` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.tojs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.tojs.md new file mode 100644 index 0000000000000..2f7a3597c1f02 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) > [toJS](./kibana-plugin-plugins-expressions-public.iregistry.tojs.md) + +## IRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.isexpressionastbuilder.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.isexpressionastbuilder.md new file mode 100644 index 0000000000000..f35e7122caeb5 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.isexpressionastbuilder.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [isExpressionAstBuilder](./kibana-plugin-plugins-expressions-public.isexpressionastbuilder.md) + +## isExpressionAstBuilder() function + +Type guard that checks whether a given value is an `ExpressionAstExpressionBuilder`. This is useful when working with subexpressions, where you might be retrieving a function argument, and need to know whether it is an expression builder instance which you can perform operations on. + +Signature: + +```typescript +export declare function isExpressionAstBuilder(val: any): val is ExpressionAstExpressionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | any | | + +Returns: + +`val is ExpressionAstExpressionBuilder` + +## Example + +const arg = myFunction.getArgument('foo'); if (isExpressionAstBuilder(foo)) { foo.toAst(); } + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibana_context_name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibana_context_name.md new file mode 100644 index 0000000000000..e568db84f383d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibana_context_name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KIBANA\_CONTEXT\_NAME](./kibana-plugin-plugins-expressions-public.kibana_context_name.md) + +## KIBANA\_CONTEXT\_NAME type + +Signature: + +```typescript +export declare type KIBANA_CONTEXT_NAME = 'kibana_context'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanacontext.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanacontext.md new file mode 100644 index 0000000000000..108533e8de357 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanacontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaContext](./kibana-plugin-plugins-expressions-public.kibanacontext.md) + +## KibanaContext type + +Signature: + +```typescript +export declare type KibanaContext = ExpressionValueSearchContext; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md new file mode 100644 index 0000000000000..c8aa768a883d6 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) + +## KibanaDatatable.columns property + +Signature: + +```typescript +columns: KibanaDatatableColumn[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md new file mode 100644 index 0000000000000..4ea1d6f42b66d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) + +## KibanaDatatable interface + +Signature: + +```typescript +export interface KibanaDatatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | +| [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) | KibanaDatatableRow[] | | +| [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md new file mode 100644 index 0000000000000..43f3243dc4fa7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) + +## KibanaDatatable.rows property + +Signature: + +```typescript +rows: KibanaDatatableRow[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md new file mode 100644 index 0000000000000..996f59cbb77a1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) + +## KibanaDatatable.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md new file mode 100644 index 0000000000000..b517c1610261b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) + +## KibanaDatatableColumn.formatHint property + +Signature: + +```typescript +formatHint?: SerializedFieldFormat; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md new file mode 100644 index 0000000000000..e7d43190589a7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) + +## KibanaDatatableColumn.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md new file mode 100644 index 0000000000000..138c19f0ec7bd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) + +## KibanaDatatableColumn interface + +Signature: + +```typescript +export interface KibanaDatatableColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | +| [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) | string | | +| [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | +| [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md new file mode 100644 index 0000000000000..df2d09bf3cc55 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) + +## KibanaDatatableColumn.meta property + +Signature: + +```typescript +meta?: KibanaDatatableColumnMeta; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md new file mode 100644 index 0000000000000..841ad67f3f521 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) + +## KibanaDatatableColumn.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md new file mode 100644 index 0000000000000..2ec6edda4cbca --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) + +## KibanaDatatableColumnMeta.aggConfigParams property + +Signature: + +```typescript +aggConfigParams?: Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md new file mode 100644 index 0000000000000..2287c28398f7f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) + +## KibanaDatatableColumnMeta.indexPatternId property + +Signature: + +```typescript +indexPatternId?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md new file mode 100644 index 0000000000000..b2f8c9d06a727 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) + +## KibanaDatatableColumnMeta interface + +Signature: + +```typescript +export interface KibanaDatatableColumnMeta +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | +| [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) | string | | +| [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) | string | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md new file mode 100644 index 0000000000000..98d4a0c2d43c3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) + +## KibanaDatatableColumnMeta.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md new file mode 100644 index 0000000000000..cb5f1ad70f628 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) + +## KibanaDatatableRow interface + +Signature: + +```typescript +export interface KibanaDatatableRow +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.knowntypetostring.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.knowntypetostring.md new file mode 100644 index 0000000000000..39c24760ca6ca --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.knowntypetostring.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KnownTypeToString](./kibana-plugin-plugins-expressions-public.knowntypetostring.md) + +## KnownTypeToString type + +Map the type of the generic to a string-based representation of the type. + +If the provided generic is its own type interface, we use the value of the `type` key as a string literal type for it. + +Signature: + +```typescript +export declare type KnownTypeToString = T extends string ? 'string' : T extends boolean ? 'boolean' : T extends number ? 'number' : T extends null ? 'null' : T extends { + type: string; +} ? T['type'] : never; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md new file mode 100644 index 0000000000000..ead6f14e0d1d7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -0,0 +1,134 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) + +## kibana-plugin-plugins-expressions-public package + +## Classes + +| Class | Description | +| --- | --- | +| [Execution](./kibana-plugin-plugins-expressions-public.execution.md) | | +| [ExecutionContract](./kibana-plugin-plugins-expressions-public.executioncontract.md) | ExecutionContract is a wrapper around Execution class. It provides the same functionality but does not expose Expressions plugin internals. | +| [Executor](./kibana-plugin-plugins-expressions-public.executor.md) | | +| [ExpressionFunction](./kibana-plugin-plugins-expressions-public.expressionfunction.md) | | +| [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-public.expressionfunctionparameter.md) | | +| [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) | | +| [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) | | +| [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) | | +| [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) | | +| [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) | ExpressionsService class is used for multiple purposes:1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. ExpressionsService defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. ExpressionsService creates a bound version of all exported contract functions. 5. Functions are bound the way there are:\`\`\`ts registerFunction = (...args: Parameters<Executor\['registerFunction'\]> ): ReturnType<Executor\['registerFunction'\]> => this.executor.registerFunction(...args); \`\`\`so that JSDoc appears in developers IDE when they use those plugins.expressions.registerFunction(. | +| [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) | | +| [FunctionsRegistry](./kibana-plugin-plugins-expressions-public.functionsregistry.md) | | +| [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) | | + +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [FontStyle](./kibana-plugin-plugins-expressions-public.fontstyle.md) | Enum of supported CSS font-style properties. | +| [FontWeight](./kibana-plugin-plugins-expressions-public.fontweight.md) | Enum of supported CSS font-weight properties. | +| [Overflow](./kibana-plugin-plugins-expressions-public.overflow.md) | Enum of supported CSS overflow properties. | +| [TextAlignment](./kibana-plugin-plugins-expressions-public.textalignment.md) | Enum of supported CSS text-align properties. | +| [TextDecoration](./kibana-plugin-plugins-expressions-public.textdecoration.md) | Enum of supported CSS text-decoration properties. | + +## Functions + +| Function | Description | +| --- | --- | +| [buildExpression(initialState)](./kibana-plugin-plugins-expressions-public.buildexpression.md) | Makes it easy to progressively build, update, and traverse an expression AST. You can either start with an empty AST, or provide an expression string, AST, or array of expression function builders to use as initial state. | +| [buildExpressionFunction(fnName, initialArgs)](./kibana-plugin-plugins-expressions-public.buildexpressionfunction.md) | Manages an AST for a single expression function. The return value can be provided to buildExpression to add this function to an expression.Note that to preserve type safety and ensure no args are missing, all required arguments for the specified function must be provided up front. If desired, they can be changed or removed later. | +| [format(ast, type)](./kibana-plugin-plugins-expressions-public.format.md) | | +| [formatExpression(ast)](./kibana-plugin-plugins-expressions-public.formatexpression.md) | Given expression pipeline AST, returns formatted string. | +| [isExpressionAstBuilder(val)](./kibana-plugin-plugins-expressions-public.isexpressionastbuilder.md) | Type guard that checks whether a given value is an ExpressionAstExpressionBuilder. This is useful when working with subexpressions, where you might be retrieving a function argument, and need to know whether it is an expression builder instance which you can perform operations on. | +| [parse(expression, startRule)](./kibana-plugin-plugins-expressions-public.parse.md) | | +| [parseExpression(expression)](./kibana-plugin-plugins-expressions-public.parseexpression.md) | Given expression pipeline string, returns parsed AST. | +| [plugin(initializerContext)](./kibana-plugin-plugins-expressions-public.plugin.md) | | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Datatable](./kibana-plugin-plugins-expressions-public.datatable.md) | A Datatable in Canvas is a unique structure that represents tabulated data. | +| [DatatableColumn](./kibana-plugin-plugins-expressions-public.datatablecolumn.md) | This type represents the shape of a column in a Datatable. | +| [ExecutionContext](./kibana-plugin-plugins-expressions-public.executioncontext.md) | ExecutionContext is an object available to all functions during a single execution; it provides various methods to perform side-effects. | +| [ExecutionParams](./kibana-plugin-plugins-expressions-public.executionparams.md) | | +| [ExecutionState](./kibana-plugin-plugins-expressions-public.executionstate.md) | | +| [ExecutorState](./kibana-plugin-plugins-expressions-public.executorstate.md) | | +| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-public.expressionastexpression.md) | | +| [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-public.expressionastexpressionbuilder.md) | | +| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-public.expressionastfunction.md) | | +| [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-public.expressionastfunctionbuilder.md) | | +| [ExpressionExecutor](./kibana-plugin-plugins-expressions-public.expressionexecutor.md) | | +| [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinition.md) | ExpressionFunctionDefinition is the interface plugins have to implement to register a function in expressions plugin. | +| [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) | A mapping of ExpressionFunctionDefinitions for functions which the Expressions services provides out-of-the-box. Any new functions registered by the Expressions plugin should have their types added here. | +| [ExpressionImage](./kibana-plugin-plugins-expressions-public.expressionimage.md) | | +| [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-public.expressionrenderdefinition.md) | | +| [ExpressionRendererEvent](./kibana-plugin-plugins-expressions-public.expressionrendererevent.md) | | +| [ExpressionRenderError](./kibana-plugin-plugins-expressions-public.expressionrendererror.md) | | +| [ExpressionsServiceStart](./kibana-plugin-plugins-expressions-public.expressionsservicestart.md) | The public contract that ExpressionsService provides to other plugins in Kibana Platform in \*start\* life-cycle. | +| [ExpressionsStart](./kibana-plugin-plugins-expressions-public.expressionsstart.md) | Expressions public start contrect, extends | +| [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.expressiontypedefinition.md) | A generic type which represents a custom Expression Type Definition that's registered to the Interpreter. | +| [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-public.expressiontypestyle.md) | An object that represents style information, typically CSS. | +| [Font](./kibana-plugin-plugins-expressions-public.font.md) | An interface representing a font in Canvas, with a textual label and the CSS font-value. | +| [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) | | +| [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) | | +| [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) | | +| [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) | | +| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) | | +| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) | | +| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) | | +| [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) | Column in a PointSeries | +| [Range](./kibana-plugin-plugins-expressions-public.range.md) | | +| [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) | | +| [SerializedDatatable](./kibana-plugin-plugins-expressions-public.serializeddatatable.md) | | +| [SerializedFieldFormat](./kibana-plugin-plugins-expressions-public.serializedfieldformat.md) | JSON representation of a field formatter configuration. Is used to carry information about how to format data in a data table as part of the column definition. | + +## Variables + +| Variable | Description | +| --- | --- | +| [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md) | | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | +| [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md) | | +| [ArgumentType](./kibana-plugin-plugins-expressions-public.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableRow](./kibana-plugin-plugins-expressions-public.datatablerow.md) | This type represents a row in a Datatable. | +| [ExecutionContainer](./kibana-plugin-plugins-expressions-public.executioncontainer.md) | | +| [ExecutorContainer](./kibana-plugin-plugins-expressions-public.executorcontainer.md) | | +| [ExpressionAstArgument](./kibana-plugin-plugins-expressions-public.expressionastargument.md) | | +| [ExpressionAstNode](./kibana-plugin-plugins-expressions-public.expressionastnode.md) | | +| [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-public.expressionfunctionkibana.md) | | +| [ExpressionRendererComponent](./kibana-plugin-plugins-expressions-public.expressionrenderercomponent.md) | | +| [ExpressionsServiceSetup](./kibana-plugin-plugins-expressions-public.expressionsservicesetup.md) | The public contract that ExpressionsService provides to other plugins in Kibana Platform in \*setup\* life-cycle. | +| [ExpressionsSetup](./kibana-plugin-plugins-expressions-public.expressionssetup.md) | Expressions public setup contract, extends [ExpressionsServiceSetup](./kibana-plugin-plugins-expressions-public.expressionsservicesetup.md) | +| [ExpressionValue](./kibana-plugin-plugins-expressions-public.expressionvalue.md) | | +| [ExpressionValueBoxed](./kibana-plugin-plugins-expressions-public.expressionvalueboxed.md) | | +| [ExpressionValueConverter](./kibana-plugin-plugins-expressions-public.expressionvalueconverter.md) | | +| [ExpressionValueError](./kibana-plugin-plugins-expressions-public.expressionvalueerror.md) | | +| [ExpressionValueFilter](./kibana-plugin-plugins-expressions-public.expressionvaluefilter.md) | Represents an object that is a Filter. | +| [ExpressionValueNum](./kibana-plugin-plugins-expressions-public.expressionvaluenum.md) | | +| [ExpressionValueRender](./kibana-plugin-plugins-expressions-public.expressionvaluerender.md) | Represents an object that is intended to be rendered. | +| [ExpressionValueSearchContext](./kibana-plugin-plugins-expressions-public.expressionvaluesearchcontext.md) | | +| [ExpressionValueUnboxed](./kibana-plugin-plugins-expressions-public.expressionvalueunboxed.md) | | +| [FontLabel](./kibana-plugin-plugins-expressions-public.fontlabel.md) | This type contains a unions of all supported font labels, or the the name of the font the user would see in a UI. | +| [FontValue](./kibana-plugin-plugins-expressions-public.fontvalue.md) | This type contains a union of all supported font values, equivalent to the CSS font-value property. | +| [InterpreterErrorType](./kibana-plugin-plugins-expressions-public.interpretererrortype.md) | | +| [KIBANA\_CONTEXT\_NAME](./kibana-plugin-plugins-expressions-public.kibana_context_name.md) | | +| [KibanaContext](./kibana-plugin-plugins-expressions-public.kibanacontext.md) | | +| [KnownTypeToString](./kibana-plugin-plugins-expressions-public.knowntypetostring.md) | Map the type of the generic to a string-based representation of the type.If the provided generic is its own type interface, we use the value of the type key as a string literal type for it. | +| [PointSeries](./kibana-plugin-plugins-expressions-public.pointseries.md) | A PointSeries is a unique structure that represents dots on a chart. | +| [PointSeriesColumnName](./kibana-plugin-plugins-expressions-public.pointseriescolumnname.md) | Allowed column names in a PointSeries | +| [PointSeriesColumns](./kibana-plugin-plugins-expressions-public.pointseriescolumns.md) | Represents a collection of valid Columns in a PointSeries | +| [PointSeriesRow](./kibana-plugin-plugins-expressions-public.pointseriesrow.md) | | +| [ReactExpressionRendererType](./kibana-plugin-plugins-expressions-public.reactexpressionrenderertype.md) | | +| [Style](./kibana-plugin-plugins-expressions-public.style.md) | | +| [TypeString](./kibana-plugin-plugins-expressions-public.typestring.md) | If the type extends a Promise, we still need to return the string representation:someArgument: Promise<boolean | string> results in types: ['boolean', 'string'] | +| [TypeToString](./kibana-plugin-plugins-expressions-public.typetostring.md) | This can convert a type into a known Expression string representation of that type. For example, TypeToString<Datatable> will resolve to 'datatable'. This allows Expression Functions to continue to specify their type in a simple string format. | +| [UnmappedTypeStrings](./kibana-plugin-plugins-expressions-public.unmappedtypestrings.md) | Types used in Expressions that don't map to a primitive cleanly:date is typed as a number or string, and represents a date | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.overflow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.overflow.md new file mode 100644 index 0000000000000..e33f1554a23d3 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.overflow.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Overflow](./kibana-plugin-plugins-expressions-public.overflow.md) + +## Overflow enum + +Enum of supported CSS `overflow` properties. + +Signature: + +```typescript +export declare enum Overflow +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| AUTO | "auto" | | +| HIDDEN | "hidden" | | +| SCROLL | "scroll" | | +| VISIBLE | "visible" | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parse.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parse.md new file mode 100644 index 0000000000000..0cbc2c15b6f54 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parse.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [parse](./kibana-plugin-plugins-expressions-public.parse.md) + +## parse() function + +Signature: + +```typescript +export declare function parse(expression: E, startRule: S): S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| expression | E | | +| startRule | S | | + +Returns: + +`S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parseexpression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parseexpression.md new file mode 100644 index 0000000000000..c4474b150dcc2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.parseexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [parseExpression](./kibana-plugin-plugins-expressions-public.parseexpression.md) + +## parseExpression() function + +Given expression pipeline string, returns parsed AST. + +Signature: + +```typescript +export declare function parseExpression(expression: string): ExpressionAstExpression; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| expression | string | | + +Returns: + +`ExpressionAstExpression` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.plugin.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.plugin.md new file mode 100644 index 0000000000000..ef707992a0a54 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.plugin.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [plugin](./kibana-plugin-plugins-expressions-public.plugin.md) + +## plugin() function + +Signature: + +```typescript +export declare function plugin(initializerContext: PluginInitializerContext): ExpressionsPublicPlugin; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + +Returns: + +`ExpressionsPublicPlugin` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseries.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseries.md new file mode 100644 index 0000000000000..14ba955ac2cc2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseries.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeries](./kibana-plugin-plugins-expressions-public.pointseries.md) + +## PointSeries type + +A `PointSeries` is a unique structure that represents dots on a chart. + +Signature: + +```typescript +export declare type PointSeries = ExpressionValueBoxed<'pointseries', { + columns: PointSeriesColumns; + rows: PointSeriesRow[]; +}>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.expression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.expression.md new file mode 100644 index 0000000000000..5c034265f4f94 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) > [expression](./kibana-plugin-plugins-expressions-public.pointseriescolumn.expression.md) + +## PointSeriesColumn.expression property + +Signature: + +```typescript +expression: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.md new file mode 100644 index 0000000000000..09ce5444caabf --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) + +## PointSeriesColumn interface + +Column in a PointSeries + +Signature: + +```typescript +export interface PointSeriesColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [expression](./kibana-plugin-plugins-expressions-public.pointseriescolumn.expression.md) | string | | +| [role](./kibana-plugin-plugins-expressions-public.pointseriescolumn.role.md) | 'measure' | 'dimension' | | +| [type](./kibana-plugin-plugins-expressions-public.pointseriescolumn.type.md) | 'number' | 'string' | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.role.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.role.md new file mode 100644 index 0000000000000..715f66a43cd62 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.role.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) > [role](./kibana-plugin-plugins-expressions-public.pointseriescolumn.role.md) + +## PointSeriesColumn.role property + +Signature: + +```typescript +role: 'measure' | 'dimension'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.type.md new file mode 100644 index 0000000000000..36a8128967cdd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumn.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) > [type](./kibana-plugin-plugins-expressions-public.pointseriescolumn.type.md) + +## PointSeriesColumn.type property + +Signature: + +```typescript +type: 'number' | 'string'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumnname.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumnname.md new file mode 100644 index 0000000000000..bc39c694307c0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumnname.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumnName](./kibana-plugin-plugins-expressions-public.pointseriescolumnname.md) + +## PointSeriesColumnName type + +Allowed column names in a PointSeries + +Signature: + +```typescript +export declare type PointSeriesColumnName = 'x' | 'y' | 'color' | 'size' | 'text'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumns.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumns.md new file mode 100644 index 0000000000000..c920a254645bd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriescolumns.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesColumns](./kibana-plugin-plugins-expressions-public.pointseriescolumns.md) + +## PointSeriesColumns type + +Represents a collection of valid Columns in a PointSeries + +Signature: + +```typescript +export declare type PointSeriesColumns = Record | {}; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriesrow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriesrow.md new file mode 100644 index 0000000000000..6e3b29572b6f4 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.pointseriesrow.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [PointSeriesRow](./kibana-plugin-plugins-expressions-public.pointseriesrow.md) + +## PointSeriesRow type + +Signature: + +```typescript +export declare type PointSeriesRow = Record; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.from.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.from.md new file mode 100644 index 0000000000000..5113a798864e9 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.from.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Range](./kibana-plugin-plugins-expressions-public.range.md) > [from](./kibana-plugin-plugins-expressions-public.range.from.md) + +## Range.from property + +Signature: + +```typescript +from: number; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.md new file mode 100644 index 0000000000000..cf0cf4cb50b71 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Range](./kibana-plugin-plugins-expressions-public.range.md) + +## Range interface + +Signature: + +```typescript +export interface Range +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [from](./kibana-plugin-plugins-expressions-public.range.from.md) | number | | +| [to](./kibana-plugin-plugins-expressions-public.range.to.md) | number | | +| [type](./kibana-plugin-plugins-expressions-public.range.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.to.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.to.md new file mode 100644 index 0000000000000..bd79997e65fc7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.to.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Range](./kibana-plugin-plugins-expressions-public.range.md) > [to](./kibana-plugin-plugins-expressions-public.range.to.md) + +## Range.to property + +Signature: + +```typescript +to: number; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.type.md new file mode 100644 index 0000000000000..4d5476516655d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.range.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Range](./kibana-plugin-plugins-expressions-public.range.md) > [type](./kibana-plugin-plugins-expressions-public.range.type.md) + +## Range.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md new file mode 100644 index 0000000000000..66c2e1e3c0c8d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRenderer](./kibana-plugin-plugins-expressions-public.reactexpressionrenderer.md) + +## ReactExpressionRenderer variable + +Signature: + +```typescript +ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, reload$, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.classname.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.classname.md new file mode 100644 index 0000000000000..b5b1391ae72fd --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.classname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [className](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.classname.md) + +## ReactExpressionRendererProps.className property + +Signature: + +```typescript +className?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md new file mode 100644 index 0000000000000..a0914ce37299f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [dataAttrs](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md) + +## ReactExpressionRendererProps.dataAttrs property + +Signature: + +```typescript +dataAttrs?: string[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md new file mode 100644 index 0000000000000..21f4294db5aeb --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [expression](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md) + +## ReactExpressionRendererProps.expression property + +Signature: + +```typescript +expression: string | ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md new file mode 100644 index 0000000000000..bd6c8cba5f784 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) + +## ReactExpressionRendererProps interface + +Signature: + +```typescript +export interface ReactExpressionRendererProps extends IExpressionLoaderParams +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [className](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.classname.md) | string | | +| [dataAttrs](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.dataattrs.md) | string[] | | +| [expression](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.expression.md) | string | ExpressionAstExpression | | +| [onEvent](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md) | (event: ExpressionRendererEvent) => void | | +| [padding](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md) | 'xs' | 's' | 'm' | 'l' | 'xl' | | +| [reload$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md) | Observable<unknown> | An observable which can be used to re-run the expression without destroying the component | +| [renderError](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md) | (error?: string | null) => React.ReactElement | React.ReactElement[] | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md new file mode 100644 index 0000000000000..4fe1e158df1b8 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [onEvent](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.onevent.md) + +## ReactExpressionRendererProps.onEvent property + +Signature: + +```typescript +onEvent?: (event: ExpressionRendererEvent) => void; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md new file mode 100644 index 0000000000000..47a23f5c1088b --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [padding](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.padding.md) + +## ReactExpressionRendererProps.padding property + +Signature: + +```typescript +padding?: 'xs' | 's' | 'm' | 'l' | 'xl'; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md new file mode 100644 index 0000000000000..a7991d559377d --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [reload$](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.reload_.md) + +## ReactExpressionRendererProps.reload$ property + +An observable which can be used to re-run the expression without destroying the component + +Signature: + +```typescript +reload$?: Observable; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md new file mode 100644 index 0000000000000..48bfe1ee5c7c7 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) > [renderError](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.rendererror.md) + +## ReactExpressionRendererProps.renderError property + +Signature: + +```typescript +renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderertype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderertype.md new file mode 100644 index 0000000000000..4ca56d534b84a --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.reactexpressionrenderertype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ReactExpressionRendererType](./kibana-plugin-plugins-expressions-public.reactexpressionrenderertype.md) + +## ReactExpressionRendererType type + +Signature: + +```typescript +export declare type ReactExpressionRendererType = React.ComponentType; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.md new file mode 100644 index 0000000000000..632cd1de2a0c2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [SerializedDatatable](./kibana-plugin-plugins-expressions-public.serializeddatatable.md) + +## SerializedDatatable interface + +Signature: + +```typescript +export interface SerializedDatatable extends Datatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [rows](./kibana-plugin-plugins-expressions-public.serializeddatatable.rows.md) | string[][] | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.rows.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.rows.md new file mode 100644 index 0000000000000..00d4323ae7025 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializeddatatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [SerializedDatatable](./kibana-plugin-plugins-expressions-public.serializeddatatable.md) > [rows](./kibana-plugin-plugins-expressions-public.serializeddatatable.rows.md) + +## SerializedDatatable.rows property + +Signature: + +```typescript +rows: string[][]; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.id.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.id.md new file mode 100644 index 0000000000000..40a45d50e9b19 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-public.serializedfieldformat.md) > [id](./kibana-plugin-plugins-expressions-public.serializedfieldformat.id.md) + +## SerializedFieldFormat.id property + +Signature: + +```typescript +id?: string; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.md new file mode 100644 index 0000000000000..74fa132ec1189 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-public.serializedfieldformat.md) + +## SerializedFieldFormat interface + +JSON representation of a field formatter configuration. Is used to carry information about how to format data in a data table as part of the column definition. + +Signature: + +```typescript +export interface SerializedFieldFormat> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-expressions-public.serializedfieldformat.id.md) | string | | +| [params](./kibana-plugin-plugins-expressions-public.serializedfieldformat.params.md) | TParams | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.params.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.params.md new file mode 100644 index 0000000000000..32d7e54cbc884 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.serializedfieldformat.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-public.serializedfieldformat.md) > [params](./kibana-plugin-plugins-expressions-public.serializedfieldformat.params.md) + +## SerializedFieldFormat.params property + +Signature: + +```typescript +params?: TParams; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.style.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.style.md new file mode 100644 index 0000000000000..f42df4b8b314e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.style.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [Style](./kibana-plugin-plugins-expressions-public.style.md) + +## Style type + +Signature: + +```typescript +export declare type Style = ExpressionTypeStyle; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textalignment.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textalignment.md new file mode 100644 index 0000000000000..351a7ba6e1f27 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textalignment.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TextAlignment](./kibana-plugin-plugins-expressions-public.textalignment.md) + +## TextAlignment enum + +Enum of supported CSS `text-align` properties. + +Signature: + +```typescript +export declare enum TextAlignment +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| CENTER | "center" | | +| JUSTIFY | "justify" | | +| LEFT | "left" | | +| RIGHT | "right" | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textdecoration.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textdecoration.md new file mode 100644 index 0000000000000..3cd8e89f4cd8c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.textdecoration.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TextDecoration](./kibana-plugin-plugins-expressions-public.textdecoration.md) + +## TextDecoration enum + +Enum of supported CSS `text-decoration` properties. + +Signature: + +```typescript +export declare enum TextDecoration +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| NONE | "none" | | +| UNDERLINE | "underline" | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry._constructor_.md new file mode 100644 index 0000000000000..856bf2bf05ad1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) > [(constructor)](./kibana-plugin-plugins-expressions-public.typesregistry._constructor_.md) + +## TypesRegistry.(constructor) + +Constructs a new instance of the `TypesRegistry` class + +Signature: + +```typescript +constructor(executor: Executor); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| executor | Executor<any> | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.get.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.get.md new file mode 100644 index 0000000000000..f83e7435485c5 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) > [get](./kibana-plugin-plugins-expressions-public.typesregistry.get.md) + +## TypesRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionType | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionType | null` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.md new file mode 100644 index 0000000000000..f1f386ec4210f --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) + +## TypesRegistry class + +Signature: + +```typescript +export declare class TypesRegistry implements IRegistry +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(executor)](./kibana-plugin-plugins-expressions-public.typesregistry._constructor_.md) | | Constructs a new instance of the TypesRegistry class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-public.typesregistry.get.md) | | | +| [register(typeDefinition)](./kibana-plugin-plugins-expressions-public.typesregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-public.typesregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-public.typesregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.register.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.register.md new file mode 100644 index 0000000000000..b328f26aa50e2 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) > [register](./kibana-plugin-plugins-expressions-public.typesregistry.register.md) + +## TypesRegistry.register() method + +Signature: + +```typescript +register(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| typeDefinition | AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.toarray.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.toarray.md new file mode 100644 index 0000000000000..2e9c8799cbd61 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) > [toArray](./kibana-plugin-plugins-expressions-public.typesregistry.toarray.md) + +## TypesRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionType[]; +``` +Returns: + +`ExpressionType[]` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.tojs.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.tojs.md new file mode 100644 index 0000000000000..14a22a890f0d1 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typesregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-public.typesregistry.md) > [toJS](./kibana-plugin-plugins-expressions-public.typesregistry.tojs.md) + +## TypesRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typestring.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typestring.md new file mode 100644 index 0000000000000..1e85625907bb0 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typestring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypeString](./kibana-plugin-plugins-expressions-public.typestring.md) + +## TypeString type + +If the type extends a Promise, we still need to return the string representation: + +`someArgument: Promise` results in `types: ['boolean', 'string']` + +Signature: + +```typescript +export declare type TypeString = KnownTypeToString>; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typetostring.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typetostring.md new file mode 100644 index 0000000000000..78f350a0c06ec --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.typetostring.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [TypeToString](./kibana-plugin-plugins-expressions-public.typetostring.md) + +## TypeToString type + +This can convert a type into a known Expression string representation of that type. For example, `TypeToString` will resolve to `'datatable'`. This allows Expression Functions to continue to specify their type in a simple string format. + +Signature: + +```typescript +export declare type TypeToString = KnownTypeToString | UnmappedTypeStrings; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.unmappedtypestrings.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.unmappedtypestrings.md new file mode 100644 index 0000000000000..6455d6520bcec --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.unmappedtypestrings.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [UnmappedTypeStrings](./kibana-plugin-plugins-expressions-public.unmappedtypestrings.md) + +## UnmappedTypeStrings type + +Types used in Expressions that don't map to a primitive cleanly: + +`date` is typed as a number or string, and represents a date + +Signature: + +```typescript +export declare type UnmappedTypeStrings = 'date' | 'filter'; +``` diff --git a/docs/development/plugins/expressions/server/index.md b/docs/development/plugins/expressions/server/index.md new file mode 100644 index 0000000000000..8c35c1631ba04 --- /dev/null +++ b/docs/development/plugins/expressions/server/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md new file mode 100644 index 0000000000000..04e652a66aa5c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md) + +## AnyExpressionFunctionDefinition type + +Type to capture every possible expression function definition. + +Signature: + +```typescript +export declare type AnyExpressionFunctionDefinition = ExpressionFunctionDefinition, any>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md new file mode 100644 index 0000000000000..c28e1aa411a34 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md) + +## AnyExpressionTypeDefinition type + +Signature: + +```typescript +export declare type AnyExpressionTypeDefinition = ExpressionTypeDefinition; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.argumenttype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.argumenttype.md new file mode 100644 index 0000000000000..360b8999f2053 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.argumenttype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ArgumentType](./kibana-plugin-plugins-expressions-server.argumenttype.md) + +## ArgumentType type + +This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each `arg` in the specification. + +Signature: + +```typescript +export declare type ArgumentType = SingleArgumentType | MultipleArgumentType | UnresolvedSingleArgumentType | UnresolvedMultipleArgumentType; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpression.md new file mode 100644 index 0000000000000..2e84c2b706bfc --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [buildExpression](./kibana-plugin-plugins-expressions-server.buildexpression.md) + +## buildExpression() function + +Makes it easy to progressively build, update, and traverse an expression AST. You can either start with an empty AST, or provide an expression string, AST, or array of expression function builders to use as initial state. + +Signature: + +```typescript +export declare function buildExpression(initialState?: ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string): ExpressionAstExpressionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initialState | ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string | | + +Returns: + +`ExpressionAstExpressionBuilder` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpressionfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpressionfunction.md new file mode 100644 index 0000000000000..09afd3d59a070 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.buildexpressionfunction.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [buildExpressionFunction](./kibana-plugin-plugins-expressions-server.buildexpressionfunction.md) + +## buildExpressionFunction() function + +Manages an AST for a single expression function. The return value can be provided to `buildExpression` to add this function to an expression. + +Note that to preserve type safety and ensure no args are missing, all required arguments for the specified function must be provided up front. If desired, they can be changed or removed later. + +Signature: + +```typescript +export declare function buildExpressionFunction(fnName: InferFunctionDefinition['name'], +initialArgs: { + [K in keyof FunctionArgs]: FunctionArgs[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[]; +}): ExpressionAstFunctionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fnName | InferFunctionDefinition<FnDef>['name'] | | +| initialArgs | {
[K in keyof FunctionArgs<FnDef>]: FunctionArgs<FnDef>[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[];
} | | + +Returns: + +`ExpressionAstFunctionBuilder` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.columns.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.columns.md new file mode 100644 index 0000000000000..1bd089af13c6c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.columns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Datatable](./kibana-plugin-plugins-expressions-server.datatable.md) > [columns](./kibana-plugin-plugins-expressions-server.datatable.columns.md) + +## Datatable.columns property + +Signature: + +```typescript +columns: DatatableColumn[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.md new file mode 100644 index 0000000000000..7dc2ab2596e12 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Datatable](./kibana-plugin-plugins-expressions-server.datatable.md) + +## Datatable interface + +A `Datatable` in Canvas is a unique structure that represents tabulated data. + +Signature: + +```typescript +export interface Datatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [columns](./kibana-plugin-plugins-expressions-server.datatable.columns.md) | DatatableColumn[] | | +| [rows](./kibana-plugin-plugins-expressions-server.datatable.rows.md) | DatatableRow[] | | +| [type](./kibana-plugin-plugins-expressions-server.datatable.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.rows.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.rows.md new file mode 100644 index 0000000000000..75bd8e2f56bde --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Datatable](./kibana-plugin-plugins-expressions-server.datatable.md) > [rows](./kibana-plugin-plugins-expressions-server.datatable.rows.md) + +## Datatable.rows property + +Signature: + +```typescript +rows: DatatableRow[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.type.md new file mode 100644 index 0000000000000..bcd250c5a9f9e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Datatable](./kibana-plugin-plugins-expressions-server.datatable.md) > [type](./kibana-plugin-plugins-expressions-server.datatable.type.md) + +## Datatable.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.id.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.id.md new file mode 100644 index 0000000000000..1f246825fa30a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-server.datatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-server.datatablecolumn.id.md) + +## DatatableColumn.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.md new file mode 100644 index 0000000000000..662f65d6fad21 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-server.datatablecolumn.md) + +## DatatableColumn interface + +This type represents the shape of a column in a `Datatable`. + +Signature: + +```typescript +export interface DatatableColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-expressions-server.datatablecolumn.id.md) | string | | +| [meta](./kibana-plugin-plugins-expressions-server.datatablecolumn.meta.md) | DatatableColumnMeta | | +| [name](./kibana-plugin-plugins-expressions-server.datatablecolumn.name.md) | string | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.meta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.meta.md new file mode 100644 index 0000000000000..ef47c67a8d606 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.meta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-server.datatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-server.datatablecolumn.meta.md) + +## DatatableColumn.meta property + +Signature: + +```typescript +meta: DatatableColumnMeta; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.name.md new file mode 100644 index 0000000000000..112b4ac3b9941 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumn.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableColumn](./kibana-plugin-plugins-expressions-server.datatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-server.datatablecolumn.name.md) + +## DatatableColumn.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md new file mode 100644 index 0000000000000..4afce913526de --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) + +## DatatableColumnType type + +This type represents the `type` of any `DatatableColumn` in a `Datatable`. + +Signature: + +```typescript +export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablerow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablerow.md new file mode 100644 index 0000000000000..56ef342b22a28 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablerow.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [DatatableRow](./kibana-plugin-plugins-expressions-server.datatablerow.md) + +## DatatableRow type + +This type represents a row in a `Datatable`. + +Signature: + +```typescript +export declare type DatatableRow = Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution._constructor_.md new file mode 100644 index 0000000000000..75f4cc4c2a017 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.execution._constructor_.md) + +## Execution.(constructor) + +Constructs a new instance of the `Execution` class + +Signature: + +```typescript +constructor(params: ExecutionParams); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| params | ExecutionParams<ExtraContext> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cancel.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cancel.md new file mode 100644 index 0000000000000..2ee091da80504 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cancel.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [cancel](./kibana-plugin-plugins-expressions-server.execution.cancel.md) + +## Execution.cancel() method + +Stop execution of expression. + +Signature: + +```typescript +cancel(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cast.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cast.md new file mode 100644 index 0000000000000..22b876332efb4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.cast.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [cast](./kibana-plugin-plugins-expressions-server.execution.cast.md) + +## Execution.cast() method + +Signature: + +```typescript +cast(value: any, toTypeNames?: string[]): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| value | any | | +| toTypeNames | string[] | | + +Returns: + +`any` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.context.md new file mode 100644 index 0000000000000..d1969fb0859b7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.context.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [context](./kibana-plugin-plugins-expressions-server.execution.context.md) + +## Execution.context property + +Execution context - object that allows to do side-effects. Context is passed to every function. + +Signature: + +```typescript +readonly context: ExecutionContext & ExtraContext; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.contract.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.contract.md new file mode 100644 index 0000000000000..149b5a7ced9cb --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.contract.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [contract](./kibana-plugin-plugins-expressions-server.execution.contract.md) + +## Execution.contract property + +Contract is a public representation of `Execution` instances. Contract we can return to other plugins for their consumption. + +Signature: + +```typescript +readonly contract: ExecutionContract; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.expression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.expression.md new file mode 100644 index 0000000000000..0487378ce1bba --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [expression](./kibana-plugin-plugins-expressions-server.execution.expression.md) + +## Execution.expression property + +Signature: + +```typescript +readonly expression: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.input.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.input.md new file mode 100644 index 0000000000000..ea411523a2b0b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.input.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [input](./kibana-plugin-plugins-expressions-server.execution.input.md) + +## Execution.input property + +Initial input of the execution. + +N.B. It is initialized to `null` rather than `undefined` for legacy reasons, because in legacy interpreter it was set to `null` by default. + +Signature: + +```typescript +input: Input; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md new file mode 100644 index 0000000000000..99bcca267f8ed --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [inspectorAdapters](./kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md) + +## Execution.inspectorAdapters property + +Signature: + +```typescript +get inspectorAdapters(): InspectorAdapters; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.interpret.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.interpret.md new file mode 100644 index 0000000000000..cf59e796e6120 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.interpret.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [interpret](./kibana-plugin-plugins-expressions-server.execution.interpret.md) + +## Execution.interpret() method + +Signature: + +```typescript +interpret(ast: ExpressionAstNode, input: T, options?: ExpressionExecOptions): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstNode | | +| input | T | | +| options | ExpressionExecOptions | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokechain.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokechain.md new file mode 100644 index 0000000000000..9ada611f32bf2 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokechain.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [invokeChain](./kibana-plugin-plugins-expressions-server.execution.invokechain.md) + +## Execution.invokeChain() method + +Signature: + +```typescript +invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| chainArr | ExpressionAstFunction[] | | +| input | unknown | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokefunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokefunction.md new file mode 100644 index 0000000000000..4519d21ee250a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.invokefunction.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [invokeFunction](./kibana-plugin-plugins-expressions-server.execution.invokefunction.md) + +## Execution.invokeFunction() method + +Signature: + +```typescript +invokeFunction(fn: ExpressionFunction, input: unknown, args: Record): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fn | ExpressionFunction | | +| input | unknown | | +| args | Record<string, unknown> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md new file mode 100644 index 0000000000000..fc663dd115580 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md @@ -0,0 +1,43 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) + +## Execution class + +Signature: + +```typescript +export declare class Execution = Record, Input = unknown, Output = unknown, InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object ? ExtraContext['inspectorAdapters'] : DefaultInspectorAdapters> +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(params)](./kibana-plugin-plugins-expressions-server.execution._constructor_.md) | | Constructs a new instance of the Execution class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-server.execution.context.md) | | ExecutionContext<Input, InspectorAdapters> & ExtraContext | Execution context - object that allows to do side-effects. Context is passed to every function. | +| [contract](./kibana-plugin-plugins-expressions-server.execution.contract.md) | | ExecutionContract<ExtraContext, Input, Output, InspectorAdapters> | Contract is a public representation of Execution instances. Contract we can return to other plugins for their consumption. | +| [expression](./kibana-plugin-plugins-expressions-server.execution.expression.md) | | string | | +| [input](./kibana-plugin-plugins-expressions-server.execution.input.md) | | Input | Initial input of the execution.N.B. It is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | +| [inspectorAdapters](./kibana-plugin-plugins-expressions-server.execution.inspectoradapters.md) | | InspectorAdapters | | +| [params](./kibana-plugin-plugins-expressions-server.execution.params.md) | | ExecutionParams<ExtraContext> | | +| [result](./kibana-plugin-plugins-expressions-server.execution.result.md) | | Promise<Output | ExpressionValueError> | | +| [state](./kibana-plugin-plugins-expressions-server.execution.state.md) | | ExecutionContainer<Output | ExpressionValueError> | Dynamic state of the execution. | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [cancel()](./kibana-plugin-plugins-expressions-server.execution.cancel.md) | | Stop execution of expression. | +| [cast(value, toTypeNames)](./kibana-plugin-plugins-expressions-server.execution.cast.md) | | | +| [interpret(ast, input, options)](./kibana-plugin-plugins-expressions-server.execution.interpret.md) | | | +| [invokeChain(chainArr, input)](./kibana-plugin-plugins-expressions-server.execution.invokechain.md) | | | +| [invokeFunction(fn, input, args)](./kibana-plugin-plugins-expressions-server.execution.invokefunction.md) | | | +| [resolveArgs(fnDef, input, argAsts)](./kibana-plugin-plugins-expressions-server.execution.resolveargs.md) | | | +| [start(input)](./kibana-plugin-plugins-expressions-server.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.params.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.params.md new file mode 100644 index 0000000000000..498f9bbfccfa4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [params](./kibana-plugin-plugins-expressions-server.execution.params.md) + +## Execution.params property + +Signature: + +```typescript +readonly params: ExecutionParams; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.resolveargs.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.resolveargs.md new file mode 100644 index 0000000000000..48cc43b2d7767 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.resolveargs.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [resolveArgs](./kibana-plugin-plugins-expressions-server.execution.resolveargs.md) + +## Execution.resolveArgs() method + +Signature: + +```typescript +resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fnDef | ExpressionFunction | | +| input | unknown | | +| argAsts | any | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.result.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.result.md new file mode 100644 index 0000000000000..be0134cd2542e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.result.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [result](./kibana-plugin-plugins-expressions-server.execution.result.md) + +## Execution.result property + +Signature: + +```typescript +get result(): Promise; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md new file mode 100644 index 0000000000000..9a4e93fe6a9af --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [start](./kibana-plugin-plugins-expressions-server.execution.start.md) + +## Execution.start() method + +Call this method to start execution. + +N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons, because in legacy interpreter it was set to `null` by default. + +Signature: + +```typescript +start(input?: Input): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | Input | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.state.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.state.md new file mode 100644 index 0000000000000..41e7e693a1da4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.state.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Execution](./kibana-plugin-plugins-expressions-server.execution.md) > [state](./kibana-plugin-plugins-expressions-server.execution.state.md) + +## Execution.state property + +Dynamic state of the execution. + +Signature: + +```typescript +readonly state: ExecutionContainer; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontainer.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontainer.md new file mode 100644 index 0000000000000..5dc82fcbb9dd8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContainer](./kibana-plugin-plugins-expressions-server.executioncontainer.md) + +## ExecutionContainer type + +Signature: + +```typescript +export declare type ExecutionContainer = StateContainer, ExecutionPureTransitions>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.abortsignal.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.abortsignal.md new file mode 100644 index 0000000000000..5c43623c8e603 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.abortsignal.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [abortSignal](./kibana-plugin-plugins-expressions-server.executioncontext.abortsignal.md) + +## ExecutionContext.abortSignal property + +Adds ability to abort current execution. + +Signature: + +```typescript +abortSignal: AbortSignal; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getinitialinput.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getinitialinput.md new file mode 100644 index 0000000000000..b5f9b91e1c7b7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getinitialinput.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [getInitialInput](./kibana-plugin-plugins-expressions-server.executioncontext.getinitialinput.md) + +## ExecutionContext.getInitialInput property + +Get initial input with which execution started. + +Signature: + +```typescript +getInitialInput: () => Input; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md new file mode 100644 index 0000000000000..b8c8f4f3bb067 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [getSavedObject](./kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md) + +## ExecutionContext.getSavedObject property + +Allows to fetch saved objects from ElasticSearch. In browser `getSavedObject` function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. + +Signature: + +```typescript +getSavedObject?: (type: string, id: string) => Promise>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.inspectoradapters.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.inspectoradapters.md new file mode 100644 index 0000000000000..b937432e4c180 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.inspectoradapters.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [inspectorAdapters](./kibana-plugin-plugins-expressions-server.executioncontext.inspectoradapters.md) + +## ExecutionContext.inspectorAdapters property + +Adapters for `inspector` plugin. + +Signature: + +```typescript +inspectorAdapters: InspectorAdapters; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md new file mode 100644 index 0000000000000..0128ba934da73 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) + +## ExecutionContext interface + +`ExecutionContext` is an object available to all functions during a single execution; it provides various methods to perform side-effects. + +Signature: + +```typescript +export interface ExecutionContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [abortSignal](./kibana-plugin-plugins-expressions-server.executioncontext.abortsignal.md) | AbortSignal | Adds ability to abort current execution. | +| [getInitialInput](./kibana-plugin-plugins-expressions-server.executioncontext.getinitialinput.md) | () => Input | Get initial input with which execution started. | +| [getSavedObject](./kibana-plugin-plugins-expressions-server.executioncontext.getsavedobject.md) | <T extends SavedObjectAttributes = SavedObjectAttributes>(type: string, id: string) => Promise<SavedObject<T>> | Allows to fetch saved objects from ElasticSearch. In browser getSavedObject function is provided automatically by the Expressions plugin. On the server the caller of the expression has to provide this context function. The reason is because on the browser we always know the user who tries to fetch a saved object, thus saved object client is scoped automatically to that user. However, on the server we can scope that saved object client to any user, or even not scope it at all and execute it as an "internal" user. | +| [inspectorAdapters](./kibana-plugin-plugins-expressions-server.executioncontext.inspectoradapters.md) | InspectorAdapters | Adapters for inspector plugin. | +| [search](./kibana-plugin-plugins-expressions-server.executioncontext.search.md) | ExecutionContextSearch | Search context in which expression should operate. | +| [types](./kibana-plugin-plugins-expressions-server.executioncontext.types.md) | Record<string, ExpressionType> | A map of available expression types. | +| [variables](./kibana-plugin-plugins-expressions-server.executioncontext.variables.md) | Record<string, unknown> | Context variables that can be consumed using var and var_set functions. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.search.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.search.md new file mode 100644 index 0000000000000..641e50696f6e0 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.search.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [search](./kibana-plugin-plugins-expressions-server.executioncontext.search.md) + +## ExecutionContext.search property + +Search context in which expression should operate. + +Signature: + +```typescript +search?: ExecutionContextSearch; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.types.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.types.md new file mode 100644 index 0000000000000..9f594a588b200 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.types.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [types](./kibana-plugin-plugins-expressions-server.executioncontext.types.md) + +## ExecutionContext.types property + +A map of available expression types. + +Signature: + +```typescript +types: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.variables.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.variables.md new file mode 100644 index 0000000000000..bce3b7bb1bc4d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executioncontext.variables.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) > [variables](./kibana-plugin-plugins-expressions-server.executioncontext.variables.md) + +## ExecutionContext.variables property + +Context variables that can be consumed using `var` and `var_set` functions. + +Signature: + +```typescript +variables: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.ast.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.ast.md new file mode 100644 index 0000000000000..adaccf091bc5e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) > [ast](./kibana-plugin-plugins-expressions-server.executionparams.ast.md) + +## ExecutionParams.ast property + +Signature: + +```typescript +ast?: ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.context.md new file mode 100644 index 0000000000000..8b9a210416dd6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) > [context](./kibana-plugin-plugins-expressions-server.executionparams.context.md) + +## ExecutionParams.context property + +Signature: + +```typescript +context?: ExtraContext; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.debug.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.debug.md new file mode 100644 index 0000000000000..b3631e0aeebe6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.debug.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) > [debug](./kibana-plugin-plugins-expressions-server.executionparams.debug.md) + +## ExecutionParams.debug property + +Whether to execute expression in \*debug mode\*. In \*debug mode\* inputs and outputs as well as all resolved arguments and time it took to execute each function are saved and are available for introspection. + +Signature: + +```typescript +debug?: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.executor.md new file mode 100644 index 0000000000000..fef0f6f8e2495 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.executor.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) > [executor](./kibana-plugin-plugins-expressions-server.executionparams.executor.md) + +## ExecutionParams.executor property + +Signature: + +```typescript +executor: Executor; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.expression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.expression.md new file mode 100644 index 0000000000000..7d75bd51a611b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) > [expression](./kibana-plugin-plugins-expressions-server.executionparams.expression.md) + +## ExecutionParams.expression property + +Signature: + +```typescript +expression?: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.md new file mode 100644 index 0000000000000..a7594bff48c1a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionparams.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) + +## ExecutionParams interface + +Signature: + +```typescript +export interface ExecutionParams = Record> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-server.executionparams.ast.md) | ExpressionAstExpression | | +| [context](./kibana-plugin-plugins-expressions-server.executionparams.context.md) | ExtraContext | | +| [debug](./kibana-plugin-plugins-expressions-server.executionparams.debug.md) | boolean | Whether to execute expression in \*debug mode\*. In \*debug mode\* inputs and outputs as well as all resolved arguments and time it took to execute each function are saved and are available for introspection. | +| [executor](./kibana-plugin-plugins-expressions-server.executionparams.executor.md) | Executor<any> | | +| [expression](./kibana-plugin-plugins-expressions-server.executionparams.expression.md) | string | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.ast.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.ast.md new file mode 100644 index 0000000000000..0eab94589a75e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) > [ast](./kibana-plugin-plugins-expressions-server.executionstate.ast.md) + +## ExecutionState.ast property + +Signature: + +```typescript +ast: ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.error.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.error.md new file mode 100644 index 0000000000000..350d38697571a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.error.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) > [error](./kibana-plugin-plugins-expressions-server.executionstate.error.md) + +## ExecutionState.error property + +Error happened during the execution. + +Signature: + +```typescript +error?: Error; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.md new file mode 100644 index 0000000000000..a3b28bda8c864 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) + +## ExecutionState interface + +Signature: + +```typescript +export interface ExecutionState extends ExecutorState +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-server.executionstate.ast.md) | ExpressionAstExpression | | +| [error](./kibana-plugin-plugins-expressions-server.executionstate.error.md) | Error | Error happened during the execution. | +| [result](./kibana-plugin-plugins-expressions-server.executionstate.result.md) | Output | Result of the expression execution. | +| [state](./kibana-plugin-plugins-expressions-server.executionstate.state.md) | 'not-started' | 'pending' | 'result' | 'error' | Tracks state of execution.- not-started - before .start() method was called. - pending - immediately after .start() method is called. - result - when expression execution completed. - error - when execution failed with error. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.result.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.result.md new file mode 100644 index 0000000000000..b23ba17172a10 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.result.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) > [result](./kibana-plugin-plugins-expressions-server.executionstate.result.md) + +## ExecutionState.result property + +Result of the expression execution. + +Signature: + +```typescript +result?: Output; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.state.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.state.md new file mode 100644 index 0000000000000..6dcfca1a4fa0e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executionstate.state.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) > [state](./kibana-plugin-plugins-expressions-server.executionstate.state.md) + +## ExecutionState.state property + +Tracks state of execution. + +- `not-started` - before .start() method was called. - `pending` - immediately after .start() method is called. - `result` - when expression execution completed. - `error` - when execution failed with error. + +Signature: + +```typescript +state: 'not-started' | 'pending' | 'result' | 'error'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor._constructor_.md new file mode 100644 index 0000000000000..f9b6759ef5529 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.executor._constructor_.md) + +## Executor.(constructor) + +Constructs a new instance of the `Executor` class + +Signature: + +```typescript +constructor(state?: ExecutorState); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| state | ExecutorState<Context> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.context.md new file mode 100644 index 0000000000000..d53401c6d0419 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [context](./kibana-plugin-plugins-expressions-server.executor.context.md) + +## Executor.context property + +Signature: + +```typescript +get context(): Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createexecution.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createexecution.md new file mode 100644 index 0000000000000..8ed228d70ff37 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createexecution.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [createExecution](./kibana-plugin-plugins-expressions-server.executor.createexecution.md) + +## Executor.createExecution() method + +Signature: + +```typescript +createExecution = Record, Input = unknown, Output = unknown>(ast: string | ExpressionAstExpression, context?: ExtraContext, { debug }?: ExpressionExecOptions): Execution; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | string | ExpressionAstExpression | | +| context | ExtraContext | | +| { debug } | ExpressionExecOptions | | + +Returns: + +`Execution` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createwithdefaults.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createwithdefaults.md new file mode 100644 index 0000000000000..67863cc9e9ebe --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.createwithdefaults.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [createWithDefaults](./kibana-plugin-plugins-expressions-server.executor.createwithdefaults.md) + +## Executor.createWithDefaults() method + +Signature: + +```typescript +static createWithDefaults = Record>(state?: ExecutorState): Executor; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| state | ExecutorState<Ctx> | | + +Returns: + +`Executor` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extendcontext.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extendcontext.md new file mode 100644 index 0000000000000..d78b4193b62fa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.extendcontext.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [extendContext](./kibana-plugin-plugins-expressions-server.executor.extendcontext.md) + +## Executor.extendContext() method + +Signature: + +```typescript +extendContext(extraContext: Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| extraContext | Record<string, unknown> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.fork.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.fork.md new file mode 100644 index 0000000000000..8cfec983e6cbd --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.fork.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [fork](./kibana-plugin-plugins-expressions-server.executor.fork.md) + +## Executor.fork() method + +Signature: + +```typescript +fork(): Executor; +``` +Returns: + +`Executor` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.functions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.functions.md new file mode 100644 index 0000000000000..36cbe8608c872 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.functions.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [functions](./kibana-plugin-plugins-expressions-server.executor.functions.md) + +## Executor.functions property + +> Warning: This API is now obsolete. +> +> + +Signature: + +```typescript +readonly functions: FunctionsRegistry; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunction.md new file mode 100644 index 0000000000000..0c3f307214d01 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunction.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getFunction](./kibana-plugin-plugins-expressions-server.executor.getfunction.md) + +## Executor.getFunction() method + +Signature: + +```typescript +getFunction(name: string): ExpressionFunction | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`ExpressionFunction | undefined` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunctions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunctions.md new file mode 100644 index 0000000000000..9f4132e30297d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.getfunctions.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getFunctions](./kibana-plugin-plugins-expressions-server.executor.getfunctions.md) + +## Executor.getFunctions() method + +Signature: + +```typescript +getFunctions(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettype.md new file mode 100644 index 0000000000000..ccff4bc632284 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getType](./kibana-plugin-plugins-expressions-server.executor.gettype.md) + +## Executor.getType() method + +Signature: + +```typescript +getType(name: string): ExpressionType | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`ExpressionType | undefined` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettypes.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettypes.md new file mode 100644 index 0000000000000..8658f36867971 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.gettypes.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [getTypes](./kibana-plugin-plugins-expressions-server.executor.gettypes.md) + +## Executor.getTypes() method + +Signature: + +```typescript +getTypes(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md new file mode 100644 index 0000000000000..7e6bb8c7ded5e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.md @@ -0,0 +1,43 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) + +## Executor class + +Signature: + +```typescript +export declare class Executor = Record> +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(state)](./kibana-plugin-plugins-expressions-server.executor._constructor_.md) | | Constructs a new instance of the Executor class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-server.executor.context.md) | | Record<string, unknown> | | +| [functions](./kibana-plugin-plugins-expressions-server.executor.functions.md) | | FunctionsRegistry | | +| [state](./kibana-plugin-plugins-expressions-server.executor.state.md) | | ExecutorContainer<Context> | | +| [types](./kibana-plugin-plugins-expressions-server.executor.types.md) | | TypesRegistry | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [createExecution(ast, context, { debug })](./kibana-plugin-plugins-expressions-server.executor.createexecution.md) | | | +| [createWithDefaults(state)](./kibana-plugin-plugins-expressions-server.executor.createwithdefaults.md) | static | | +| [extendContext(extraContext)](./kibana-plugin-plugins-expressions-server.executor.extendcontext.md) | | | +| [fork()](./kibana-plugin-plugins-expressions-server.executor.fork.md) | | | +| [getFunction(name)](./kibana-plugin-plugins-expressions-server.executor.getfunction.md) | | | +| [getFunctions()](./kibana-plugin-plugins-expressions-server.executor.getfunctions.md) | | | +| [getType(name)](./kibana-plugin-plugins-expressions-server.executor.gettype.md) | | | +| [getTypes()](./kibana-plugin-plugins-expressions-server.executor.gettypes.md) | | | +| [registerFunction(functionDefinition)](./kibana-plugin-plugins-expressions-server.executor.registerfunction.md) | | | +| [registerType(typeDefinition)](./kibana-plugin-plugins-expressions-server.executor.registertype.md) | | | +| [run(ast, input, context)](./kibana-plugin-plugins-expressions-server.executor.run.md) | | Execute expression and return result. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registerfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registerfunction.md new file mode 100644 index 0000000000000..0cdd62735980c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registerfunction.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [registerFunction](./kibana-plugin-plugins-expressions-server.executor.registerfunction.md) + +## Executor.registerFunction() method + +Signature: + +```typescript +registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registertype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registertype.md new file mode 100644 index 0000000000000..355ff92921f10 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.registertype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [registerType](./kibana-plugin-plugins-expressions-server.executor.registertype.md) + +## Executor.registerType() method + +Signature: + +```typescript +registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| typeDefinition | AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.run.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.run.md new file mode 100644 index 0000000000000..784a1df5d3ac2 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.run.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [run](./kibana-plugin-plugins-expressions-server.executor.run.md) + +## Executor.run() method + +Execute expression and return result. + +Signature: + +```typescript +run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | string | ExpressionAstExpression | | +| input | Input | | +| context | ExtraContext | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.state.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.state.md new file mode 100644 index 0000000000000..2c3041484712a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.state.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [state](./kibana-plugin-plugins-expressions-server.executor.state.md) + +## Executor.state property + +Signature: + +```typescript +readonly state: ExecutorContainer; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.types.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.types.md new file mode 100644 index 0000000000000..2082917cf0624 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executor.types.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Executor](./kibana-plugin-plugins-expressions-server.executor.md) > [types](./kibana-plugin-plugins-expressions-server.executor.types.md) + +## Executor.types property + +> Warning: This API is now obsolete. +> +> + +Signature: + +```typescript +readonly types: TypesRegistry; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorcontainer.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorcontainer.md new file mode 100644 index 0000000000000..a3847c5ad9a77 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorcontainer.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutorContainer](./kibana-plugin-plugins-expressions-server.executorcontainer.md) + +## ExecutorContainer type + +Signature: + +```typescript +export declare type ExecutorContainer = Record> = StateContainer, ExecutorPureTransitions, ExecutorPureSelectors>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.context.md new file mode 100644 index 0000000000000..0829f2d6caf04 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) > [context](./kibana-plugin-plugins-expressions-server.executorstate.context.md) + +## ExecutorState.context property + +Signature: + +```typescript +context: Context; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.functions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.functions.md new file mode 100644 index 0000000000000..92a0865a7bb2f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.functions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) > [functions](./kibana-plugin-plugins-expressions-server.executorstate.functions.md) + +## ExecutorState.functions property + +Signature: + +```typescript +functions: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.md new file mode 100644 index 0000000000000..5faa326ee3534 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) + +## ExecutorState interface + +Signature: + +```typescript +export interface ExecutorState = Record> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [context](./kibana-plugin-plugins-expressions-server.executorstate.context.md) | Context | | +| [functions](./kibana-plugin-plugins-expressions-server.executorstate.functions.md) | Record<string, ExpressionFunction> | | +| [types](./kibana-plugin-plugins-expressions-server.executorstate.types.md) | Record<string, ExpressionType> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.types.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.types.md new file mode 100644 index 0000000000000..a435fabfedf92 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.executorstate.types.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) > [types](./kibana-plugin-plugins-expressions-server.executorstate.types.md) + +## ExecutorState.types property + +Signature: + +```typescript +types: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastargument.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastargument.md new file mode 100644 index 0000000000000..0518949ad612f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastargument.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstArgument](./kibana-plugin-plugins-expressions-server.expressionastargument.md) + +## ExpressionAstArgument type + +Signature: + +```typescript +export declare type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md new file mode 100644 index 0000000000000..cc8006b918dec --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) > [chain](./kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md) + +## ExpressionAstExpression.chain property + +Signature: + +```typescript +chain: ExpressionAstFunction[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md new file mode 100644 index 0000000000000..b5f83d1af7cb7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) + +## ExpressionAstExpression interface + +Signature: + +```typescript +export interface ExpressionAstExpression +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [chain](./kibana-plugin-plugins-expressions-server.expressionastexpression.chain.md) | ExpressionAstFunction[] | | +| [type](./kibana-plugin-plugins-expressions-server.expressionastexpression.type.md) | 'expression' | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md new file mode 100644 index 0000000000000..46cd60cecaa84 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpression.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastexpression.type.md) + +## ExpressionAstExpression.type property + +Signature: + +```typescript +type: 'expression'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.findfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.findfunction.md new file mode 100644 index 0000000000000..28cf8707c17d6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.findfunction.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) > [findFunction](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.findfunction.md) + +## ExpressionAstExpressionBuilder.findFunction property + +Recursively searches expression for all ocurrences of the function, including in subexpressions. + +Useful when performing migrations on a specific function, as you can iterate over the array of references and update all functions at once. + +Signature: + +```typescript +findFunction: (fnName: InferFunctionDefinition['name']) => Array> | []; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.functions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.functions.md new file mode 100644 index 0000000000000..c3e1add70511b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.functions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) > [functions](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.functions.md) + +## ExpressionAstExpressionBuilder.functions property + +Array of each of the `buildExpressionFunction()` instances in this expression. Use this to remove or reorder functions in the expression. + +Signature: + +```typescript +functions: ExpressionAstFunctionBuilder[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md new file mode 100644 index 0000000000000..50a9c76daaa2b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) + +## ExpressionAstExpressionBuilder interface + +Signature: + +```typescript +export interface ExpressionAstExpressionBuilder +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [findFunction](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.findfunction.md) | <FnDef extends AnyExpressionFunctionDefinition = AnyExpressionFunctionDefinition>(fnName: InferFunctionDefinition<FnDef>['name']) => Array<ExpressionAstFunctionBuilder<FnDef>> | [] | Recursively searches expression for all ocurrences of the function, including in subexpressions.Useful when performing migrations on a specific function, as you can iterate over the array of references and update all functions at once. | +| [functions](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.functions.md) | ExpressionAstFunctionBuilder[] | Array of each of the buildExpressionFunction() instances in this expression. Use this to remove or reorder functions in the expression. | +| [toAst](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.toast.md) | () => ExpressionAstExpression | Converts expression to an AST. ExpressionAstExpression | +| [toString](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.tostring.md) | () => string | Converts expression to an expression string. string | +| [type](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.type.md) | 'expression_builder' | Used to identify expression builder objects. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.toast.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.toast.md new file mode 100644 index 0000000000000..5c6189e6a46c4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.toast.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) > [toAst](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.toast.md) + +## ExpressionAstExpressionBuilder.toAst property + +Converts expression to an AST. + + `ExpressionAstExpression` + +Signature: + +```typescript +toAst: () => ExpressionAstExpression; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.tostring.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.tostring.md new file mode 100644 index 0000000000000..80aaeef1700c3 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.tostring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) > [toString](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.tostring.md) + +## ExpressionAstExpressionBuilder.toString property + +Converts expression to an expression string. + + `string` + +Signature: + +```typescript +toString: () => string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.type.md new file mode 100644 index 0000000000000..401b2e3725e84 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.type.md) + +## ExpressionAstExpressionBuilder.type property + +Used to identify expression builder objects. + +Signature: + +```typescript +type: 'expression_builder'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md new file mode 100644 index 0000000000000..052cadffb9bdb --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md) + +## ExpressionAstFunction.arguments property + +Signature: + +```typescript +arguments: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md new file mode 100644 index 0000000000000..b3227c2ac5822 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [debug](./kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md) + +## ExpressionAstFunction.debug property + +Debug information added to each function when expression is executed in \*debug mode\*. + +Signature: + +```typescript +debug?: ExpressionAstFunctionDebug; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md new file mode 100644 index 0000000000000..9964409f49119 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.function.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [function](./kibana-plugin-plugins-expressions-server.expressionastfunction.function.md) + +## ExpressionAstFunction.function property + +Signature: + +```typescript +function: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md new file mode 100644 index 0000000000000..1d49de44b571d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) + +## ExpressionAstFunction interface + +Signature: + +```typescript +export interface ExpressionAstFunction +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunction.arguments.md) | Record<string, ExpressionAstArgument[]> | | +| [debug](./kibana-plugin-plugins-expressions-server.expressionastfunction.debug.md) | ExpressionAstFunctionDebug | Debug information added to each function when expression is executed in \*debug mode\*. | +| [function](./kibana-plugin-plugins-expressions-server.expressionastfunction.function.md) | string | | +| [type](./kibana-plugin-plugins-expressions-server.expressionastfunction.type.md) | 'function' | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md new file mode 100644 index 0000000000000..3fd10524c1599 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunction.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastfunction.type.md) + +## ExpressionAstFunction.type property + +Signature: + +```typescript +type: 'function'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.addargument.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.addargument.md new file mode 100644 index 0000000000000..29e3baec18a2e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.addargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [addArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.addargument.md) + +## ExpressionAstFunctionBuilder.addArgument property + +Adds an additional argument to the function. For multi-args, this should be called once for each new arg. Note that TS will not enforce whether multi-args are available, so only use this to update an existing arg if you are certain it is a multi-arg. + +Signature: + +```typescript +addArgument:
>(name: A, value: FunctionArgs[A] | ExpressionAstExpressionBuilder) => this; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.arguments.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.arguments.md new file mode 100644 index 0000000000000..4c0eee637b3e1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.arguments.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.arguments.md) + +## ExpressionAstFunctionBuilder.arguments property + +Object of all args currently added to the function. This is structured similarly to `ExpressionAstFunction['arguments']`, however any subexpressions are returned as expression builder instances instead of expression ASTs. + +Signature: + +```typescript +arguments: FunctionBuilderArguments; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.getargument.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.getargument.md new file mode 100644 index 0000000000000..09b76ccbf23d9 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.getargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [getArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.getargument.md) + +## ExpressionAstFunctionBuilder.getArgument property + +Retrieves an existing argument by name. Useful when you want to retrieve the current array of args and add something to it before calling `replaceArgument`. Any subexpression arguments will be returned as expression builder instances. + +Signature: + +```typescript +getArgument: >(name: A) => Array[A] | ExpressionAstExpressionBuilder> | undefined; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md new file mode 100644 index 0000000000000..2a502d6f05e0b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) + +## ExpressionAstFunctionBuilder interface + +Signature: + +```typescript +export interface ExpressionAstFunctionBuilder +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.addargument.md) | <A extends FunctionArgName<FnDef>>(name: A, value: FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder) => this | Adds an additional argument to the function. For multi-args, this should be called once for each new arg. Note that TS will not enforce whether multi-args are available, so only use this to update an existing arg if you are certain it is a multi-arg. | +| [arguments](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.arguments.md) | FunctionBuilderArguments<FnDef> | Object of all args currently added to the function. This is structured similarly to ExpressionAstFunction['arguments'], however any subexpressions are returned as expression builder instances instead of expression ASTs. | +| [getArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.getargument.md) | <A extends FunctionArgName<FnDef>>(name: A) => Array<FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder> | undefined | Retrieves an existing argument by name. Useful when you want to retrieve the current array of args and add something to it before calling replaceArgument. Any subexpression arguments will be returned as expression builder instances. | +| [name](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.name.md) | InferFunctionDefinition<FnDef>['name'] | Name of this expression function. | +| [removeArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.removeargument.md) | <A extends OptionalKeys<FunctionArgs<FnDef>>>(name: A) => this | Removes an (optional) argument from the function.TypeScript will enforce that you only remove optional arguments. For manipulating required args, use replaceArgument. | +| [replaceArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.replaceargument.md) | <A extends FunctionArgName<FnDef>>(name: A, value: Array<FunctionArgs<FnDef>[A] | ExpressionAstExpressionBuilder>) => this | Overwrites an existing argument with a new value. In order to support multi-args, the value given must always be an array. | +| [toAst](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.toast.md) | () => ExpressionAstFunction | Converts function to an AST. ExpressionAstFunction | +| [toString](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.tostring.md) | () => string | Converts function to an expression string. string | +| [type](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.type.md) | 'expression_function_builder' | Used to identify expression function builder objects. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.name.md new file mode 100644 index 0000000000000..a2b6a4128549f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [name](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.name.md) + +## ExpressionAstFunctionBuilder.name property + +Name of this expression function. + +Signature: + +```typescript +name: InferFunctionDefinition['name']; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.removeargument.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.removeargument.md new file mode 100644 index 0000000000000..b5fd0cc7e3727 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.removeargument.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [removeArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.removeargument.md) + +## ExpressionAstFunctionBuilder.removeArgument property + +Removes an (optional) argument from the function. + +TypeScript will enforce that you only remove optional arguments. For manipulating required args, use `replaceArgument`. + +Signature: + +```typescript +removeArgument: >>(name: A) => this; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.replaceargument.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.replaceargument.md new file mode 100644 index 0000000000000..943d8ea235763 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.replaceargument.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [replaceArgument](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.replaceargument.md) + +## ExpressionAstFunctionBuilder.replaceArgument property + +Overwrites an existing argument with a new value. In order to support multi-args, the value given must always be an array. + +Signature: + +```typescript +replaceArgument: >(name: A, value: Array[A] | ExpressionAstExpressionBuilder>) => this; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.toast.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.toast.md new file mode 100644 index 0000000000000..a8e9205610501 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.toast.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [toAst](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.toast.md) + +## ExpressionAstFunctionBuilder.toAst property + +Converts function to an AST. + + `ExpressionAstFunction` + +Signature: + +```typescript +toAst: () => ExpressionAstFunction; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.tostring.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.tostring.md new file mode 100644 index 0000000000000..af307cbc84c9f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.tostring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [toString](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.tostring.md) + +## ExpressionAstFunctionBuilder.toString property + +Converts function to an expression string. + + `string` + +Signature: + +```typescript +toString: () => string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.type.md new file mode 100644 index 0000000000000..ed1db54c6eeaa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) > [type](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.type.md) + +## ExpressionAstFunctionBuilder.type property + +Used to identify expression function builder objects. + +Signature: + +```typescript +type: 'expression_function_builder'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastnode.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastnode.md new file mode 100644 index 0000000000000..d04c5556ff0ff --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionastnode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionAstNode](./kibana-plugin-plugins-expressions-server.expressionastnode.md) + +## ExpressionAstNode type + +Signature: + +```typescript +export declare type ExpressionAstNode = ExpressionAstExpression | ExpressionAstFunction | ExpressionAstArgument; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction._constructor_.md new file mode 100644 index 0000000000000..96ed22f3277b4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.expressionfunction._constructor_.md) + +## ExpressionFunction.(constructor) + +Constructs a new instance of the `ExpressionFunction` class + +Signature: + +```typescript +constructor(functionDefinition: AnyExpressionFunctionDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md new file mode 100644 index 0000000000000..25008a56e0465 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [accepts](./kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md) + +## ExpressionFunction.accepts property + +Signature: + +```typescript +accepts: (type: string) => boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md new file mode 100644 index 0000000000000..6e11246275d04 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [aliases](./kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md) + +## ExpressionFunction.aliases property + +Aliases that can be used instead of `name`. + +Signature: + +```typescript +aliases: string[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.args.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.args.md new file mode 100644 index 0000000000000..ffa8cd0d11f7a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.args.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [args](./kibana-plugin-plugins-expressions-server.expressionfunction.args.md) + +## ExpressionFunction.args property + +Specification of expression function parameters. + +Signature: + +```typescript +args: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.fn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.fn.md new file mode 100644 index 0000000000000..12056cac12cce --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.fn.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [fn](./kibana-plugin-plugins-expressions-server.expressionfunction.fn.md) + +## ExpressionFunction.fn property + +Function to run function (context, args) + +Signature: + +```typescript +fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.help.md new file mode 100644 index 0000000000000..0a20a1ec60860 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [help](./kibana-plugin-plugins-expressions-server.expressionfunction.help.md) + +## ExpressionFunction.help property + +A short help text. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md new file mode 100644 index 0000000000000..1fa11bbb77416 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md) + +## ExpressionFunction.inputTypes property + +Type of inputs that this function supports. + +Signature: + +```typescript +inputTypes: string[] | undefined; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md new file mode 100644 index 0000000000000..aac3878b8c859 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.md @@ -0,0 +1,31 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) + +## ExpressionFunction class + +Signature: + +```typescript +export declare class ExpressionFunction +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(functionDefinition)](./kibana-plugin-plugins-expressions-server.expressionfunction._constructor_.md) | | Constructs a new instance of the ExpressionFunction class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [accepts](./kibana-plugin-plugins-expressions-server.expressionfunction.accepts.md) | | (type: string) => boolean | | +| [aliases](./kibana-plugin-plugins-expressions-server.expressionfunction.aliases.md) | | string[] | Aliases that can be used instead of name. | +| [args](./kibana-plugin-plugins-expressions-server.expressionfunction.args.md) | | Record<string, ExpressionFunctionParameter> | Specification of expression function parameters. | +| [fn](./kibana-plugin-plugins-expressions-server.expressionfunction.fn.md) | | (input: ExpressionValue, params: Record<string, any>, handlers: object) => ExpressionValue | Function to run function (context, args) | +| [help](./kibana-plugin-plugins-expressions-server.expressionfunction.help.md) | | string | A short help text. | +| [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunction.inputtypes.md) | | string[] | undefined | Type of inputs that this function supports. | +| [name](./kibana-plugin-plugins-expressions-server.expressionfunction.name.md) | | string | Name of function | +| [type](./kibana-plugin-plugins-expressions-server.expressionfunction.type.md) | | string | Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.name.md new file mode 100644 index 0000000000000..46115c10c79ad --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [name](./kibana-plugin-plugins-expressions-server.expressionfunction.name.md) + +## ExpressionFunction.name property + +Name of function + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.type.md new file mode 100644 index 0000000000000..82bfff184b7cf --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunction.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) > [type](./kibana-plugin-plugins-expressions-server.expressionfunction.type.md) + +## ExpressionFunction.type property + +Return type of function. This SHOULD be supplied. We use it for UI and autocomplete hinting. We may also use it for optimizations in the future. + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.aliases.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.aliases.md new file mode 100644 index 0000000000000..3f5a608cc9bd2 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.aliases.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [aliases](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.aliases.md) + +## ExpressionFunctionDefinition.aliases property + + What is this? + +Signature: + +```typescript +aliases?: string[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md new file mode 100644 index 0000000000000..4ceb1d92bf8eb --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [args](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md) + +## ExpressionFunctionDefinition.args property + +Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. + +Signature: + +```typescript +args: { + [key in keyof Arguments]: ArgumentType; + }; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md new file mode 100644 index 0000000000000..54d5c7c8a688d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md) + +## ExpressionFunctionDefinition.context property + +> Warning: This API is now obsolete. +> +> Use `inputTypes` instead. +> + +Signature: + +```typescript +context?: { + types: AnyExpressionFunctionDefinition['inputTypes']; + }; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.fn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.fn.md new file mode 100644 index 0000000000000..41f85be7141be --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.fn.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [fn](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.fn.md) + +## ExpressionFunctionDefinition.fn() method + +The actual implementation of the function. + +Signature: + +```typescript +fn(input: Input, args: Arguments, context: Context): Output; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| input | Input | | +| args | Arguments | | +| context | Context | | + +Returns: + +`Output` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md new file mode 100644 index 0000000000000..594cb768f3caa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [help](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md) + +## ExpressionFunctionDefinition.help property + +Help text displayed in the Expression editor. This text should be internationalized. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md new file mode 100644 index 0000000000000..b47dc915cc72e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md) + +## ExpressionFunctionDefinition.inputTypes property + +List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. + +Signature: + +```typescript +inputTypes?: Array>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md new file mode 100644 index 0000000000000..6463c6ac537b9 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) + +## ExpressionFunctionDefinition interface + +`ExpressionFunctionDefinition` is the interface plugins have to implement to register a function in `expressions` plugin. + +Signature: + +```typescript +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aliases](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.aliases.md) | string[] | What is this? | +| [args](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.args.md) | {
[key in keyof Arguments]: ArgumentType<Arguments[key]>;
} | Specification of arguments that function supports. This list will also be used for autocomplete functionality when your function is being edited. | +| [context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.context.md) | {
types: AnyExpressionFunctionDefinition['inputTypes'];
} | | +| [help](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.help.md) | string | Help text displayed in the Expression editor. This text should be internationalized. | +| [inputTypes](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.inputtypes.md) | Array<TypeToString<Input>> | List of allowed type names for input value of this function. If this property is set the input of function will be cast to the first possible type in this list. If this property is missing the input will be provided to the function as-is. | +| [name](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md) | Name | The name of the function, as will be used in expression. | +| [type](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.type.md) | TypeToString<UnwrapPromiseOrReturn<Output>> | Name of type of value this function outputs. | + +## Methods + +| Method | Description | +| --- | --- | +| [fn(input, args, context)](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.fn.md) | The actual implementation of the function. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md new file mode 100644 index 0000000000000..177b44aab4ce8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [name](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.name.md) + +## ExpressionFunctionDefinition.name property + +The name of the function, as will be used in expression. + +Signature: + +```typescript +name: Name; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.type.md new file mode 100644 index 0000000000000..a73ded342f053 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) > [type](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.type.md) + +## ExpressionFunctionDefinition.type property + +Name of type of value this function outputs. + +Signature: + +```typescript +type?: TypeToString>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md new file mode 100644 index 0000000000000..0c01e93152c75 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [clog](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md) + +## ExpressionFunctionDefinitions.clog property + +Signature: + +```typescript +clog: ExpressionFunctionClog; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md new file mode 100644 index 0000000000000..842e3e069d91e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [font](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md) + +## ExpressionFunctionDefinitions.font property + +Signature: + +```typescript +font: ExpressionFunctionFont; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md new file mode 100644 index 0000000000000..8e6d189f8f450 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [kibana](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md) + +## ExpressionFunctionDefinitions.kibana property + +Signature: + +```typescript +kibana: ExpressionFunctionKibana; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md new file mode 100644 index 0000000000000..f9e248ad6d913 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [kibana\_context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md) + +## ExpressionFunctionDefinitions.kibana\_context property + +Signature: + +```typescript +kibana_context: ExpressionFunctionKibanaContext; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md new file mode 100644 index 0000000000000..71cd0b98a68c2 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) + +## ExpressionFunctionDefinitions interface + +A mapping of `ExpressionFunctionDefinition`s for functions which the Expressions services provides out-of-the-box. Any new functions registered by the Expressions plugin should have their types added here. + +Signature: + +```typescript +export interface ExpressionFunctionDefinitions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [clog](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.clog.md) | ExpressionFunctionClog | | +| [font](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | +| [kibana\_context](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana_context.md) | ExpressionFunctionKibanaContext | | +| [kibana](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.kibana.md) | ExpressionFunctionKibana | | +| [theme](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md) | ExpressionFunctionTheme | | +| [var\_set](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md) | ExpressionFunctionVarSet | | +| [var](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md) | ExpressionFunctionVar | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md new file mode 100644 index 0000000000000..98291fe35c1aa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [theme](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md) + +## ExpressionFunctionDefinitions.theme property + +Signature: + +```typescript +theme: ExpressionFunctionTheme; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md new file mode 100644 index 0000000000000..55d576193ba9c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [var](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md) + +## ExpressionFunctionDefinitions.var property + +Signature: + +```typescript +var: ExpressionFunctionVar; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md new file mode 100644 index 0000000000000..3163cf1accab1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [var\_set](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md) + +## ExpressionFunctionDefinitions.var\_set property + +Signature: + +```typescript +var_set: ExpressionFunctionVarSet; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md new file mode 100644 index 0000000000000..aac2ae1c3ca4e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md) + +## ExpressionFunctionKibana type + +Signature: + +```typescript +export declare type ExpressionFunctionKibana = ExpressionFunctionDefinition<'kibana', ExpressionValueSearchContext | null, object, ExpressionValueSearchContext>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter._constructor_.md new file mode 100644 index 0000000000000..a9ad2069cfe15 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter._constructor_.md) + +## ExpressionFunctionParameter.(constructor) + +Constructs a new instance of the `ExpressionFunctionParameter` class + +Signature: + +```typescript +constructor(name: string, arg: ArgumentType); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | +| arg | ArgumentType<any> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.accepts.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.accepts.md new file mode 100644 index 0000000000000..083bbdf1da4e7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.accepts.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [accepts](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.accepts.md) + +## ExpressionFunctionParameter.accepts() method + +Signature: + +```typescript +accepts(type: string): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.aliases.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.aliases.md new file mode 100644 index 0000000000000..c7d27a48bdad5 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.aliases.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [aliases](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.aliases.md) + +## ExpressionFunctionParameter.aliases property + +Signature: + +```typescript +aliases: string[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.default.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.default.md new file mode 100644 index 0000000000000..d4105febffe86 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.default.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [default](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.default.md) + +## ExpressionFunctionParameter.default property + +Signature: + +```typescript +default: any; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.help.md new file mode 100644 index 0000000000000..b21626df64121 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [help](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.help.md) + +## ExpressionFunctionParameter.help property + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md new file mode 100644 index 0000000000000..e9e35183e4e76 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md @@ -0,0 +1,38 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) + +## ExpressionFunctionParameter class + +Signature: + +```typescript +export declare class ExpressionFunctionParameter +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(name, arg)](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter._constructor_.md) | | Constructs a new instance of the ExpressionFunctionParameter class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [aliases](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.aliases.md) | | string[] | | +| [default](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.default.md) | | any | | +| [help](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.help.md) | | string | | +| [multi](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.multi.md) | | boolean | | +| [name](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.name.md) | | string | | +| [options](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.options.md) | | any[] | | +| [required](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.required.md) | | boolean | | +| [resolve](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.resolve.md) | | boolean | | +| [types](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.types.md) | | string[] | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [accepts(type)](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.accepts.md) | | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.multi.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.multi.md new file mode 100644 index 0000000000000..86e1921910a30 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.multi.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [multi](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.multi.md) + +## ExpressionFunctionParameter.multi property + +Signature: + +```typescript +multi: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.name.md new file mode 100644 index 0000000000000..8aab81d92e65a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [name](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.name.md) + +## ExpressionFunctionParameter.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.options.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.options.md new file mode 100644 index 0000000000000..95369ebd5ce88 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.options.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [options](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.options.md) + +## ExpressionFunctionParameter.options property + +Signature: + +```typescript +options: any[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.required.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.required.md new file mode 100644 index 0000000000000..0e58b41e2a22c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.required.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [required](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.required.md) + +## ExpressionFunctionParameter.required property + +Signature: + +```typescript +required: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.resolve.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.resolve.md new file mode 100644 index 0000000000000..3415c5f6a7639 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.resolve.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [resolve](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.resolve.md) + +## ExpressionFunctionParameter.resolve property + +Signature: + +```typescript +resolve: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.types.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.types.md new file mode 100644 index 0000000000000..f7d6079705e8e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctionparameter.types.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) > [types](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.types.md) + +## ExpressionFunctionParameter.types property + +Signature: + +```typescript +types: string[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.dataurl.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.dataurl.md new file mode 100644 index 0000000000000..a51dc1eaea66f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.dataurl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-server.expressionimage.md) > [dataurl](./kibana-plugin-plugins-expressions-server.expressionimage.dataurl.md) + +## ExpressionImage.dataurl property + +Signature: + +```typescript +dataurl: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.md new file mode 100644 index 0000000000000..7f323fba7bfe1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-server.expressionimage.md) + +## ExpressionImage interface + +Signature: + +```typescript +export interface ExpressionImage +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [dataurl](./kibana-plugin-plugins-expressions-server.expressionimage.dataurl.md) | string | | +| [mode](./kibana-plugin-plugins-expressions-server.expressionimage.mode.md) | string | | +| [type](./kibana-plugin-plugins-expressions-server.expressionimage.type.md) | 'image' | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.mode.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.mode.md new file mode 100644 index 0000000000000..9aae0ed3ea8b4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.mode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-server.expressionimage.md) > [mode](./kibana-plugin-plugins-expressions-server.expressionimage.mode.md) + +## ExpressionImage.mode property + +Signature: + +```typescript +mode: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.type.md new file mode 100644 index 0000000000000..0cc0418228281 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionimage.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionImage](./kibana-plugin-plugins-expressions-server.expressionimage.md) > [type](./kibana-plugin-plugins-expressions-server.expressionimage.type.md) + +## ExpressionImage.type property + +Signature: + +```typescript +type: 'image'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md new file mode 100644 index 0000000000000..e936e25cee6ca --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [displayName](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md) + +## ExpressionRenderDefinition.displayName property + +A user friendly name of the renderer as will be displayed to user in UI. + +Signature: + +```typescript +displayName: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.help.md new file mode 100644 index 0000000000000..971abba04fdf9 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [help](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.help.md) + +## ExpressionRenderDefinition.help property + +Help text as will be displayed to user. A sentence or few about what this element does. + +Signature: + +```typescript +help?: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md new file mode 100644 index 0000000000000..9cefb6ef196cf --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) + +## ExpressionRenderDefinition interface + +Signature: + +```typescript +export interface ExpressionRenderDefinition +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [displayName](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md) | string | A user friendly name of the renderer as will be displayed to user in UI. | +| [help](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.help.md) | string | Help text as will be displayed to user. A sentence or few about what this element does. | +| [name](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.name.md) | string | Technical name of the renderer, used as ID to identify renderer in expression renderer registry. This must match the name of the expression function that is used to create the type: render object. | +| [render](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.render.md) | (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise<void> | The function called to render the output data of an expression. | +| [reuseDomNode](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.reusedomnode.md) | boolean | Tell the renderer if the dom node should be reused, it's recreated each time by default. | +| [validate](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.validate.md) | () => undefined | Error | Used to validate the data before calling the render function. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.name.md new file mode 100644 index 0000000000000..62eec0109c374 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [name](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.name.md) + +## ExpressionRenderDefinition.name property + +Technical name of the renderer, used as ID to identify renderer in expression renderer registry. This must match the name of the expression function that is used to create the `type: render` object. + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.render.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.render.md new file mode 100644 index 0000000000000..99698e1828637 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.render.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [render](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.render.md) + +## ExpressionRenderDefinition.render property + +The function called to render the output data of an expression. + +Signature: + +```typescript +render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.reusedomnode.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.reusedomnode.md new file mode 100644 index 0000000000000..435920cccc642 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.reusedomnode.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [reuseDomNode](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.reusedomnode.md) + +## ExpressionRenderDefinition.reuseDomNode property + +Tell the renderer if the dom node should be reused, it's recreated each time by default. + +Signature: + +```typescript +reuseDomNode: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.validate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.validate.md new file mode 100644 index 0000000000000..f640744374eda --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.validate.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) > [validate](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.validate.md) + +## ExpressionRenderDefinition.validate property + +Used to validate the data before calling the render function. + +Signature: + +```typescript +validate?: () => undefined | Error; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer._constructor_.md new file mode 100644 index 0000000000000..5db39853728af --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.expressionrenderer._constructor_.md) + +## ExpressionRenderer.(constructor) + +Constructs a new instance of the `ExpressionRenderer` class + +Signature: + +```typescript +constructor(config: ExpressionRenderDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| config | ExpressionRenderDefinition<Config> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.displayname.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.displayname.md new file mode 100644 index 0000000000000..41846bf41997f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.displayname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [displayName](./kibana-plugin-plugins-expressions-server.expressionrenderer.displayname.md) + +## ExpressionRenderer.displayName property + +Signature: + +```typescript +readonly displayName: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.help.md new file mode 100644 index 0000000000000..9cf60c832fb95 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [help](./kibana-plugin-plugins-expressions-server.expressionrenderer.help.md) + +## ExpressionRenderer.help property + +Signature: + +```typescript +readonly help: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.md new file mode 100644 index 0000000000000..6f5c336a89e5c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.md @@ -0,0 +1,29 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) + +## ExpressionRenderer class + +Signature: + +```typescript +export declare class ExpressionRenderer +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(config)](./kibana-plugin-plugins-expressions-server.expressionrenderer._constructor_.md) | | Constructs a new instance of the ExpressionRenderer class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [displayName](./kibana-plugin-plugins-expressions-server.expressionrenderer.displayname.md) | | string | | +| [help](./kibana-plugin-plugins-expressions-server.expressionrenderer.help.md) | | string | | +| [name](./kibana-plugin-plugins-expressions-server.expressionrenderer.name.md) | | string | | +| [render](./kibana-plugin-plugins-expressions-server.expressionrenderer.render.md) | | ExpressionRenderDefinition<Config>['render'] | | +| [reuseDomNode](./kibana-plugin-plugins-expressions-server.expressionrenderer.reusedomnode.md) | | boolean | | +| [validate](./kibana-plugin-plugins-expressions-server.expressionrenderer.validate.md) | | () => void | Error | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.name.md new file mode 100644 index 0000000000000..f320fcd8408ab --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [name](./kibana-plugin-plugins-expressions-server.expressionrenderer.name.md) + +## ExpressionRenderer.name property + +Signature: + +```typescript +readonly name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.render.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.render.md new file mode 100644 index 0000000000000..d7cf04e6d8bfa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.render.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [render](./kibana-plugin-plugins-expressions-server.expressionrenderer.render.md) + +## ExpressionRenderer.render property + +Signature: + +```typescript +readonly render: ExpressionRenderDefinition['render']; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.reusedomnode.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.reusedomnode.md new file mode 100644 index 0000000000000..8fd9c5fa8e6ff --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.reusedomnode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [reuseDomNode](./kibana-plugin-plugins-expressions-server.expressionrenderer.reusedomnode.md) + +## ExpressionRenderer.reuseDomNode property + +Signature: + +```typescript +readonly reuseDomNode: boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.validate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.validate.md new file mode 100644 index 0000000000000..d40945cfb88f1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderer.validate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) > [validate](./kibana-plugin-plugins-expressions-server.expressionrenderer.validate.md) + +## ExpressionRenderer.validate property + +Signature: + +```typescript +readonly validate: () => void | Error; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.get.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.get.md new file mode 100644 index 0000000000000..6f8e6c868ac9b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) > [get](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.get.md) + +## ExpressionRendererRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionRenderer | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionRenderer | null` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.md new file mode 100644 index 0000000000000..d4a34ab140854 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) + +## ExpressionRendererRegistry class + +Signature: + +```typescript +export declare class ExpressionRendererRegistry implements IRegistry +``` + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.get.md) | | | +| [register(definition)](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.register.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.register.md new file mode 100644 index 0000000000000..d5411a327fbcd --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) > [register](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.register.md) + +## ExpressionRendererRegistry.register() method + +Signature: + +```typescript +register(definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| definition | AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.toarray.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.toarray.md new file mode 100644 index 0000000000000..edb153000b458 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) > [toArray](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.toarray.md) + +## ExpressionRendererRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionRenderer[]; +``` +Returns: + +`ExpressionRenderer[]` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.tojs.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.tojs.md new file mode 100644 index 0000000000000..f7230e9102c8f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrendererregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) > [toJS](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.tojs.md) + +## ExpressionRendererRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin._constructor_.md new file mode 100644 index 0000000000000..639ae379f0ed7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.expressionsserverplugin._constructor_.md) + +## ExpressionsServerPlugin.(constructor) + +Constructs a new instance of the `ExpressionsServerPlugin` class + +Signature: + +```typescript +constructor(initializerContext: PluginInitializerContext); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.expressions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.expressions.md new file mode 100644 index 0000000000000..a391220a6349e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.expressions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) > [expressions](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.expressions.md) + +## ExpressionsServerPlugin.expressions property + +Signature: + +```typescript +readonly expressions: ExpressionsService; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.md new file mode 100644 index 0000000000000..f92d572b1111a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) + +## ExpressionsServerPlugin class + +Signature: + +```typescript +export declare class ExpressionsServerPlugin implements Plugin +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(initializerContext)](./kibana-plugin-plugins-expressions-server.expressionsserverplugin._constructor_.md) | | Constructs a new instance of the ExpressionsServerPlugin class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [expressions](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.expressions.md) | | ExpressionsService | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [setup(core)](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.setup.md) | | | +| [start(core)](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.start.md) | | | +| [stop()](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.stop.md) | | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.setup.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.setup.md new file mode 100644 index 0000000000000..18e33d4e0bc60 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.setup.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) > [setup](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.setup.md) + +## ExpressionsServerPlugin.setup() method + +Signature: + +```typescript +setup(core: CoreSetup): ExpressionsServerSetup; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreSetup | | + +Returns: + +`ExpressionsServerSetup` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.start.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.start.md new file mode 100644 index 0000000000000..31578685ff386 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.start.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) > [start](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.start.md) + +## ExpressionsServerPlugin.start() method + +Signature: + +```typescript +start(core: CoreStart): ExpressionsServerStart; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| core | CoreStart | | + +Returns: + +`ExpressionsServerStart` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.stop.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.stop.md new file mode 100644 index 0000000000000..2f6abade901b1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverplugin.stop.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) > [stop](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.stop.md) + +## ExpressionsServerPlugin.stop() method + +Signature: + +```typescript +stop(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserversetup.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserversetup.md new file mode 100644 index 0000000000000..2cf591a59c4f6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserversetup.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerSetup](./kibana-plugin-plugins-expressions-server.expressionsserversetup.md) + +## ExpressionsServerSetup type + +Signature: + +```typescript +export declare type ExpressionsServerSetup = ExpressionsServiceSetup; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverstart.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverstart.md new file mode 100644 index 0000000000000..9ceb261a7f689 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverstart.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionsServerStart](./kibana-plugin-plugins-expressions-server.expressionsserverstart.md) + +## ExpressionsServerStart type + +Signature: + +```typescript +export declare type ExpressionsServerStart = ExpressionsServiceStart; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype._constructor_.md new file mode 100644 index 0000000000000..966955c03ff08 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.expressiontype._constructor_.md) + +## ExpressionType.(constructor) + +Constructs a new instance of the `ExpressionType` class + +Signature: + +```typescript +constructor(definition: AnyExpressionTypeDefinition); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| definition | AnyExpressionTypeDefinition | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.castsfrom.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.castsfrom.md new file mode 100644 index 0000000000000..57758e8fa7788 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.castsfrom.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [castsFrom](./kibana-plugin-plugins-expressions-server.expressiontype.castsfrom.md) + +## ExpressionType.castsFrom property + +Signature: + +```typescript +castsFrom: (value: ExpressionValue) => boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.caststo.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.caststo.md new file mode 100644 index 0000000000000..eec17f8606817 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.caststo.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [castsTo](./kibana-plugin-plugins-expressions-server.expressiontype.caststo.md) + +## ExpressionType.castsTo property + +Signature: + +```typescript +castsTo: (value: ExpressionValue) => boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.create.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.create.md new file mode 100644 index 0000000000000..3fbd1f7986254 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.create.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [create](./kibana-plugin-plugins-expressions-server.expressiontype.create.md) + +## ExpressionType.create property + +Signature: + +```typescript +create: unknown; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.deserialize.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.deserialize.md new file mode 100644 index 0000000000000..232d70b846092 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.deserialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [deserialize](./kibana-plugin-plugins-expressions-server.expressiontype.deserialize.md) + +## ExpressionType.deserialize property + +Signature: + +```typescript +deserialize?: (serialized: any) => ExpressionValue; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.from.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.from.md new file mode 100644 index 0000000000000..4d24a4162c096 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.from.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [from](./kibana-plugin-plugins-expressions-server.expressiontype.from.md) + +## ExpressionType.from property + +Signature: + +```typescript +from: (value: ExpressionValue, types: Record) => any; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.getfromfn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.getfromfn.md new file mode 100644 index 0000000000000..092227af92a19 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.getfromfn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [getFromFn](./kibana-plugin-plugins-expressions-server.expressiontype.getfromfn.md) + +## ExpressionType.getFromFn property + +Signature: + +```typescript +getFromFn: (typeName: string) => undefined | ExpressionValueConverter; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.gettofn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.gettofn.md new file mode 100644 index 0000000000000..8454116f50ac8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.gettofn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [getToFn](./kibana-plugin-plugins-expressions-server.expressiontype.gettofn.md) + +## ExpressionType.getToFn property + +Signature: + +```typescript +getToFn: (typeName: string) => undefined | ExpressionValueConverter; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.help.md new file mode 100644 index 0000000000000..bd5be7329d6a4 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.help.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [help](./kibana-plugin-plugins-expressions-server.expressiontype.help.md) + +## ExpressionType.help property + +A short help text. + +Signature: + +```typescript +help: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.md new file mode 100644 index 0000000000000..49f3f504c9419 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) + +## ExpressionType class + +Signature: + +```typescript +export declare class ExpressionType +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(definition)](./kibana-plugin-plugins-expressions-server.expressiontype._constructor_.md) | | Constructs a new instance of the ExpressionType class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [castsFrom](./kibana-plugin-plugins-expressions-server.expressiontype.castsfrom.md) | | (value: ExpressionValue) => boolean | | +| [castsTo](./kibana-plugin-plugins-expressions-server.expressiontype.caststo.md) | | (value: ExpressionValue) => boolean | | +| [create](./kibana-plugin-plugins-expressions-server.expressiontype.create.md) | | unknown | | +| [deserialize](./kibana-plugin-plugins-expressions-server.expressiontype.deserialize.md) | | (serialized: any) => ExpressionValue | | +| [from](./kibana-plugin-plugins-expressions-server.expressiontype.from.md) | | (value: ExpressionValue, types: Record<string, ExpressionType>) => any | | +| [getFromFn](./kibana-plugin-plugins-expressions-server.expressiontype.getfromfn.md) | | (typeName: string) => undefined | ExpressionValueConverter<ExpressionValue, ExpressionValue> | | +| [getToFn](./kibana-plugin-plugins-expressions-server.expressiontype.gettofn.md) | | (typeName: string) => undefined | ExpressionValueConverter<ExpressionValue, ExpressionValue> | | +| [help](./kibana-plugin-plugins-expressions-server.expressiontype.help.md) | | string | A short help text. | +| [name](./kibana-plugin-plugins-expressions-server.expressiontype.name.md) | | string | | +| [serialize](./kibana-plugin-plugins-expressions-server.expressiontype.serialize.md) | | (value: ExpressionValue) => any | Optional serialization (used when passing context around client/server). | +| [to](./kibana-plugin-plugins-expressions-server.expressiontype.to.md) | | (value: ExpressionValue, toTypeName: string, types: Record<string, ExpressionType>) => any | | +| [validate](./kibana-plugin-plugins-expressions-server.expressiontype.validate.md) | | (type: any) => void | Error | Type validation, useful for checking function output. | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.name.md new file mode 100644 index 0000000000000..44e0e18270b14 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [name](./kibana-plugin-plugins-expressions-server.expressiontype.name.md) + +## ExpressionType.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.serialize.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.serialize.md new file mode 100644 index 0000000000000..013b95bf2d0ce --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.serialize.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [serialize](./kibana-plugin-plugins-expressions-server.expressiontype.serialize.md) + +## ExpressionType.serialize property + +Optional serialization (used when passing context around client/server). + +Signature: + +```typescript +serialize?: (value: ExpressionValue) => any; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.to.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.to.md new file mode 100644 index 0000000000000..70e4504324f22 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.to.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [to](./kibana-plugin-plugins-expressions-server.expressiontype.to.md) + +## ExpressionType.to property + +Signature: + +```typescript +to: (value: ExpressionValue, toTypeName: string, types: Record) => any; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.validate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.validate.md new file mode 100644 index 0000000000000..6e1fd681a732b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontype.validate.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) > [validate](./kibana-plugin-plugins-expressions-server.expressiontype.validate.md) + +## ExpressionType.validate property + +Type validation, useful for checking function output. + +Signature: + +```typescript +validate: (type: any) => void | Error; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.deserialize.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.deserialize.md new file mode 100644 index 0000000000000..71e9ecd7270d9 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.deserialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [deserialize](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.deserialize.md) + +## ExpressionTypeDefinition.deserialize property + +Signature: + +```typescript +deserialize?: (type: SerializedType) => Value; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.from.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.from.md new file mode 100644 index 0000000000000..f3ad8791c7bac --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.from.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [from](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.from.md) + +## ExpressionTypeDefinition.from property + +Signature: + +```typescript +from?: { + [type: string]: ExpressionValueConverter; + }; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.help.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.help.md new file mode 100644 index 0000000000000..f1c4d48599da6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.help.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [help](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.help.md) + +## ExpressionTypeDefinition.help property + +Signature: + +```typescript +help?: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.md new file mode 100644 index 0000000000000..5179bd1df7311 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) + +## ExpressionTypeDefinition interface + +A generic type which represents a custom Expression Type Definition that's registered to the Interpreter. + +Signature: + +```typescript +export interface ExpressionTypeDefinition +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [deserialize](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.deserialize.md) | (type: SerializedType) => Value | | +| [from](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.from.md) | {
[type: string]: ExpressionValueConverter<any, Value>;
} | | +| [help](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.help.md) | string | | +| [name](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.name.md) | Name | | +| [serialize](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.serialize.md) | (type: Value) => SerializedType | | +| [to](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.to.md) | {
[type: string]: ExpressionValueConverter<Value, any>;
} | | +| [validate](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.validate.md) | (type: any) => void | Error | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.name.md new file mode 100644 index 0000000000000..cfc1cebac16da --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [name](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.name.md) + +## ExpressionTypeDefinition.name property + +Signature: + +```typescript +name: Name; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.serialize.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.serialize.md new file mode 100644 index 0000000000000..05ec569f62638 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.serialize.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [serialize](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.serialize.md) + +## ExpressionTypeDefinition.serialize property + +Signature: + +```typescript +serialize?: (type: Value) => SerializedType; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.to.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.to.md new file mode 100644 index 0000000000000..6c2c22fc902c6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.to.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [to](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.to.md) + +## ExpressionTypeDefinition.to property + +Signature: + +```typescript +to?: { + [type: string]: ExpressionValueConverter; + }; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.validate.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.validate.md new file mode 100644 index 0000000000000..acdcf089fcbe0 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypedefinition.validate.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) > [validate](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.validate.md) + +## ExpressionTypeDefinition.validate property + +Signature: + +```typescript +validate?: (type: any) => void | Error; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.css.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.css.md new file mode 100644 index 0000000000000..7cb6e9bc8b45d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.css.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-server.expressiontypestyle.md) > [css](./kibana-plugin-plugins-expressions-server.expressiontypestyle.css.md) + +## ExpressionTypeStyle.css property + +Signature: + +```typescript +css: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.md new file mode 100644 index 0000000000000..274e9b7b6772c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-server.expressiontypestyle.md) + +## ExpressionTypeStyle interface + +An object that represents style information, typically CSS. + +Signature: + +```typescript +export interface ExpressionTypeStyle +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [css](./kibana-plugin-plugins-expressions-server.expressiontypestyle.css.md) | string | | +| [spec](./kibana-plugin-plugins-expressions-server.expressiontypestyle.spec.md) | CSSStyle | | +| [type](./kibana-plugin-plugins-expressions-server.expressiontypestyle.type.md) | 'style' | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.spec.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.spec.md new file mode 100644 index 0000000000000..95f3edbc2b725 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.spec.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-server.expressiontypestyle.md) > [spec](./kibana-plugin-plugins-expressions-server.expressiontypestyle.spec.md) + +## ExpressionTypeStyle.spec property + +Signature: + +```typescript +spec: CSSStyle; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.type.md new file mode 100644 index 0000000000000..be3b476cb8b53 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressiontypestyle.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-server.expressiontypestyle.md) > [type](./kibana-plugin-plugins-expressions-server.expressiontypestyle.type.md) + +## ExpressionTypeStyle.type property + +Signature: + +```typescript +type: 'style'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalue.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalue.md new file mode 100644 index 0000000000000..fc9af5fbc6695 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalue.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValue](./kibana-plugin-plugins-expressions-server.expressionvalue.md) + +## ExpressionValue type + +Signature: + +```typescript +export declare type ExpressionValue = ExpressionValueUnboxed | ExpressionValueBoxed; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueboxed.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueboxed.md new file mode 100644 index 0000000000000..ad84aec0dc6d5 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueboxed.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueBoxed](./kibana-plugin-plugins-expressions-server.expressionvalueboxed.md) + +## ExpressionValueBoxed type + +Signature: + +```typescript +export declare type ExpressionValueBoxed = { + type: Type; +} & Value; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueconverter.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueconverter.md new file mode 100644 index 0000000000000..d1b69590141cb --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueconverter.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueConverter](./kibana-plugin-plugins-expressions-server.expressionvalueconverter.md) + +## ExpressionValueConverter type + +Signature: + +```typescript +export declare type ExpressionValueConverter = (input: I, availableTypes: Record) => O; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md new file mode 100644 index 0000000000000..b90e4360e055a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueerror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueError](./kibana-plugin-plugins-expressions-server.expressionvalueerror.md) + +## ExpressionValueError type + +Signature: + +```typescript +export declare type ExpressionValueError = ExpressionValueBoxed<'error', { + error: { + message: string; + type?: string; + name?: string; + stack?: string; + original?: Error; + }; + info?: unknown; +}>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluefilter.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluefilter.md new file mode 100644 index 0000000000000..fb65bc2550513 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluefilter.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueFilter](./kibana-plugin-plugins-expressions-server.expressionvaluefilter.md) + +## ExpressionValueFilter type + +Represents an object that is a Filter. + +Signature: + +```typescript +export declare type ExpressionValueFilter = ExpressionValueBoxed<'filter', { + filterType?: string; + value?: string; + column?: string; + and: ExpressionValueFilter[]; + to?: string; + from?: string; + query?: string | null; +}>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluenum.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluenum.md new file mode 100644 index 0000000000000..b109a23dc7259 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluenum.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueNum](./kibana-plugin-plugins-expressions-server.expressionvaluenum.md) + +## ExpressionValueNum type + +Signature: + +```typescript +export declare type ExpressionValueNum = ExpressionValueBoxed<'num', { + value: number; +}>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluerender.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluerender.md new file mode 100644 index 0000000000000..96958d753a78e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluerender.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueRender](./kibana-plugin-plugins-expressions-server.expressionvaluerender.md) + +## ExpressionValueRender type + +Represents an object that is intended to be rendered. + +Signature: + +```typescript +export declare type ExpressionValueRender = ExpressionValueBoxed; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluesearchcontext.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluesearchcontext.md new file mode 100644 index 0000000000000..6e38adde3ba91 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvaluesearchcontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueSearchContext](./kibana-plugin-plugins-expressions-server.expressionvaluesearchcontext.md) + +## ExpressionValueSearchContext type + +Signature: + +```typescript +export declare type ExpressionValueSearchContext = ExpressionValueBoxed<'kibana_context', ExecutionContextSearch>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueunboxed.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueunboxed.md new file mode 100644 index 0000000000000..2393b2bb70e6b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionvalueunboxed.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionValueUnboxed](./kibana-plugin-plugins-expressions-server.expressionvalueunboxed.md) + +## ExpressionValueUnboxed type + +Signature: + +```typescript +export declare type ExpressionValueUnboxed = any; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.label.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.label.md new file mode 100644 index 0000000000000..5f11f866be2f6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.label.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Font](./kibana-plugin-plugins-expressions-server.font.md) > [label](./kibana-plugin-plugins-expressions-server.font.label.md) + +## Font.label property + +Signature: + +```typescript +label: FontLabel; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.md new file mode 100644 index 0000000000000..f3ff25e034624 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Font](./kibana-plugin-plugins-expressions-server.font.md) + +## Font interface + +An interface representing a font in Canvas, with a textual label and the CSS `font-value`. + +Signature: + +```typescript +export interface Font +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [label](./kibana-plugin-plugins-expressions-server.font.label.md) | FontLabel | | +| [value](./kibana-plugin-plugins-expressions-server.font.value.md) | FontValue | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.value.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.value.md new file mode 100644 index 0000000000000..1bb1fac163661 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.font.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Font](./kibana-plugin-plugins-expressions-server.font.md) > [value](./kibana-plugin-plugins-expressions-server.font.value.md) + +## Font.value property + +Signature: + +```typescript +value: FontValue; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontlabel.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontlabel.md new file mode 100644 index 0000000000000..4837abb7542fa --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontlabel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FontLabel](./kibana-plugin-plugins-expressions-server.fontlabel.md) + +## FontLabel type + +This type contains a unions of all supported font labels, or the the name of the font the user would see in a UI. + +Signature: + +```typescript +export declare type FontLabel = typeof fonts[number]['label']; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontstyle.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontstyle.md new file mode 100644 index 0000000000000..26588096666df --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontstyle.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FontStyle](./kibana-plugin-plugins-expressions-server.fontstyle.md) + +## FontStyle enum + +Enum of supported CSS `font-style` properties. + +Signature: + +```typescript +export declare enum FontStyle +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| ITALIC | "italic" | | +| NORMAL | "normal" | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontvalue.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontvalue.md new file mode 100644 index 0000000000000..6c0332067a369 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontvalue.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FontValue](./kibana-plugin-plugins-expressions-server.fontvalue.md) + +## FontValue type + +This type contains a union of all supported font values, equivalent to the CSS `font-value` property. + +Signature: + +```typescript +export declare type FontValue = typeof fonts[number]['value']; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontweight.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontweight.md new file mode 100644 index 0000000000000..314e4b17df01e --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.fontweight.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FontWeight](./kibana-plugin-plugins-expressions-server.fontweight.md) + +## FontWeight enum + +Enum of supported CSS `font-weight` properties. + +Signature: + +```typescript +export declare enum FontWeight +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| BOLD | "bold" | | +| BOLDER | "bolder" | | +| EIGHT | "800" | | +| FIVE | "500" | | +| FOUR | "400" | | +| LIGHTER | "lighter" | | +| NINE | "900" | | +| NORMAL | "normal" | | +| ONE | "100" | | +| SEVEN | "700" | | +| SIX | "600" | | +| THREE | "300" | | +| TWO | "200" | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.format.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.format.md new file mode 100644 index 0000000000000..aae8498bce03f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.format.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [format](./kibana-plugin-plugins-expressions-server.format.md) + +## format() function + +Signature: + +```typescript +export declare function format(ast: T, type: T extends ExpressionAstExpression ? 'expression' : 'argument'): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | T | | +| type | T extends ExpressionAstExpression ? 'expression' : 'argument' | | + +Returns: + +`string` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.formatexpression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.formatexpression.md new file mode 100644 index 0000000000000..701d7b448f69f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.formatexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [formatExpression](./kibana-plugin-plugins-expressions-server.formatexpression.md) + +## formatExpression() function + +Given expression pipeline AST, returns formatted string. + +Signature: + +```typescript +export declare function formatExpression(ast: ExpressionAstExpression): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | ExpressionAstExpression | | + +Returns: + +`string` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry._constructor_.md new file mode 100644 index 0000000000000..c3dc8b8e9b16f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.functionsregistry._constructor_.md) + +## FunctionsRegistry.(constructor) + +Constructs a new instance of the `FunctionsRegistry` class + +Signature: + +```typescript +constructor(executor: Executor); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| executor | Executor<any> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.get.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.get.md new file mode 100644 index 0000000000000..795b3a87eac09 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) > [get](./kibana-plugin-plugins-expressions-server.functionsregistry.get.md) + +## FunctionsRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionFunction | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionFunction | null` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.md new file mode 100644 index 0000000000000..790105c68241a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) + +## FunctionsRegistry class + +Signature: + +```typescript +export declare class FunctionsRegistry implements IRegistry +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(executor)](./kibana-plugin-plugins-expressions-server.functionsregistry._constructor_.md) | | Constructs a new instance of the FunctionsRegistry class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-server.functionsregistry.get.md) | | | +| [register(functionDefinition)](./kibana-plugin-plugins-expressions-server.functionsregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-server.functionsregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-server.functionsregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.register.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.register.md new file mode 100644 index 0000000000000..7da47937e80f0 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) > [register](./kibana-plugin-plugins-expressions-server.functionsregistry.register.md) + +## FunctionsRegistry.register() method + +Signature: + +```typescript +register(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| functionDefinition | AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.toarray.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.toarray.md new file mode 100644 index 0000000000000..5f9ca38990076 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) > [toArray](./kibana-plugin-plugins-expressions-server.functionsregistry.toarray.md) + +## FunctionsRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionFunction[]; +``` +Returns: + +`ExpressionFunction[]` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.tojs.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.tojs.md new file mode 100644 index 0000000000000..35751bb534e58 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.functionsregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) > [toJS](./kibana-plugin-plugins-expressions-server.functionsregistry.tojs.md) + +## FunctionsRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.done.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.done.md new file mode 100644 index 0000000000000..c6204769e893c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.done.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [done](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.done.md) + +## IInterpreterRenderHandlers.done property + +Done increments the number of rendering successes + +Signature: + +```typescript +done: () => void; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md new file mode 100644 index 0000000000000..6a011aaf7f132 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [event](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md) + +## IInterpreterRenderHandlers.event property + +Signature: + +```typescript +event: (event: any) => void; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md new file mode 100644 index 0000000000000..cbaffa04bae8f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) + +## IInterpreterRenderHandlers interface + +Signature: + +```typescript +export interface IInterpreterRenderHandlers +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [done](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.done.md) | () => void | Done increments the number of rendering successes | +| [event](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md) | (event: any) => void | | +| [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | +| [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) | () => void | | +| [update](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md) | (params: any) => void | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md new file mode 100644 index 0000000000000..14ef98d17769c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) + +## IInterpreterRenderHandlers.onDestroy property + +Signature: + +```typescript +onDestroy: (fn: () => void) => void; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md new file mode 100644 index 0000000000000..c5e74e79f652b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) + +## IInterpreterRenderHandlers.reload property + +Signature: + +```typescript +reload: () => void; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md new file mode 100644 index 0000000000000..2649ea99b3386 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [update](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.update.md) + +## IInterpreterRenderHandlers.update property + +Signature: + +```typescript +update: (params: any) => void; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.interpretererrortype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.interpretererrortype.md new file mode 100644 index 0000000000000..032cea643c5bf --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.interpretererrortype.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [InterpreterErrorType](./kibana-plugin-plugins-expressions-server.interpretererrortype.md) + +## InterpreterErrorType type + +> Warning: This API is now obsolete. +> +> Exported for backwards compatibility. +> + +Signature: + +```typescript +export declare type InterpreterErrorType = ExpressionValueError; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.get.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.get.md new file mode 100644 index 0000000000000..b0b4524afe40a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) > [get](./kibana-plugin-plugins-expressions-server.iregistry.get.md) + +## IRegistry.get() method + +Signature: + +```typescript +get(id: string): T | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`T | null` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.md new file mode 100644 index 0000000000000..71aafe2db2dd1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) + +## IRegistry interface + +Signature: + +```typescript +export interface IRegistry +``` + +## Methods + +| Method | Description | +| --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-server.iregistry.get.md) | | +| [toArray()](./kibana-plugin-plugins-expressions-server.iregistry.toarray.md) | | +| [toJS()](./kibana-plugin-plugins-expressions-server.iregistry.tojs.md) | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.toarray.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.toarray.md new file mode 100644 index 0000000000000..73718cd036c85 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) > [toArray](./kibana-plugin-plugins-expressions-server.iregistry.toarray.md) + +## IRegistry.toArray() method + +Signature: + +```typescript +toArray(): T[]; +``` +Returns: + +`T[]` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.tojs.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.tojs.md new file mode 100644 index 0000000000000..af83efbd99aa7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) > [toJS](./kibana-plugin-plugins-expressions-server.iregistry.tojs.md) + +## IRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.isexpressionastbuilder.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.isexpressionastbuilder.md new file mode 100644 index 0000000000000..7692ff21ae934 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.isexpressionastbuilder.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [isExpressionAstBuilder](./kibana-plugin-plugins-expressions-server.isexpressionastbuilder.md) + +## isExpressionAstBuilder() function + +Type guard that checks whether a given value is an `ExpressionAstExpressionBuilder`. This is useful when working with subexpressions, where you might be retrieving a function argument, and need to know whether it is an expression builder instance which you can perform operations on. + +Signature: + +```typescript +export declare function isExpressionAstBuilder(val: any): val is ExpressionAstExpressionBuilder; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| val | any | | + +Returns: + +`val is ExpressionAstExpressionBuilder` + +## Example + +const arg = myFunction.getArgument('foo'); if (isExpressionAstBuilder(foo)) { foo.toAst(); } + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibana_context_name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibana_context_name.md new file mode 100644 index 0000000000000..bd47c52e0d5ce --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibana_context_name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KIBANA\_CONTEXT\_NAME](./kibana-plugin-plugins-expressions-server.kibana_context_name.md) + +## KIBANA\_CONTEXT\_NAME type + +Signature: + +```typescript +export declare type KIBANA_CONTEXT_NAME = 'kibana_context'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanacontext.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanacontext.md new file mode 100644 index 0000000000000..023748173e7dd --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanacontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaContext](./kibana-plugin-plugins-expressions-server.kibanacontext.md) + +## KibanaContext type + +Signature: + +```typescript +export declare type KibanaContext = ExpressionValueSearchContext; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md new file mode 100644 index 0000000000000..423e543e4307a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) + +## KibanaDatatable.columns property + +Signature: + +```typescript +columns: KibanaDatatableColumn[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md new file mode 100644 index 0000000000000..30ee3ac2fcd13 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) + +## KibanaDatatable interface + +Signature: + +```typescript +export interface KibanaDatatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | +| [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) | KibanaDatatableRow[] | | +| [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md new file mode 100644 index 0000000000000..42170a83fc3c8 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) + +## KibanaDatatable.rows property + +Signature: + +```typescript +rows: KibanaDatatableRow[]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md new file mode 100644 index 0000000000000..c36674540a1ba --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) + +## KibanaDatatable.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md new file mode 100644 index 0000000000000..a1e6949019dcb --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) + +## KibanaDatatableColumn.formatHint property + +Signature: + +```typescript +formatHint?: SerializedFieldFormat; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md new file mode 100644 index 0000000000000..6f90da1ac9c94 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) + +## KibanaDatatableColumn.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md new file mode 100644 index 0000000000000..171477911502f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) + +## KibanaDatatableColumn interface + +Signature: + +```typescript +export interface KibanaDatatableColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | +| [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) | string | | +| [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | +| [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) | string | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md new file mode 100644 index 0000000000000..40b20d51e6ec6 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) + +## KibanaDatatableColumn.meta property + +Signature: + +```typescript +meta?: KibanaDatatableColumnMeta; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md new file mode 100644 index 0000000000000..3a85e2325483a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) + +## KibanaDatatableColumn.name property + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md new file mode 100644 index 0000000000000..539b24174f725 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) + +## KibanaDatatableColumnMeta.aggConfigParams property + +Signature: + +```typescript +aggConfigParams?: Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md new file mode 100644 index 0000000000000..2704915a15071 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) + +## KibanaDatatableColumnMeta.indexPatternId property + +Signature: + +```typescript +indexPatternId?: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md new file mode 100644 index 0000000000000..d9a96e665f010 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) + +## KibanaDatatableColumnMeta interface + +Signature: + +```typescript +export interface KibanaDatatableColumnMeta +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | +| [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) | string | | +| [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) | string | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md new file mode 100644 index 0000000000000..56e3757ef621a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) + +## KibanaDatatableColumnMeta.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md new file mode 100644 index 0000000000000..dd0f3f4cb2f60 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) + +## KibanaDatatableRow interface + +Signature: + +```typescript +export interface KibanaDatatableRow +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.knowntypetostring.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.knowntypetostring.md new file mode 100644 index 0000000000000..ed536ac3b7173 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.knowntypetostring.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KnownTypeToString](./kibana-plugin-plugins-expressions-server.knowntypetostring.md) + +## KnownTypeToString type + +Map the type of the generic to a string-based representation of the type. + +If the provided generic is its own type interface, we use the value of the `type` key as a string literal type for it. + +Signature: + +```typescript +export declare type KnownTypeToString = T extends string ? 'string' : T extends boolean ? 'boolean' : T extends number ? 'number' : T extends null ? 'null' : T extends { + type: string; +} ? T['type'] : never; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md new file mode 100644 index 0000000000000..c9fed2e00c66c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md @@ -0,0 +1,116 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) + +## kibana-plugin-plugins-expressions-server package + +## Classes + +| Class | Description | +| --- | --- | +| [Execution](./kibana-plugin-plugins-expressions-server.execution.md) | | +| [Executor](./kibana-plugin-plugins-expressions-server.executor.md) | | +| [ExpressionFunction](./kibana-plugin-plugins-expressions-server.expressionfunction.md) | | +| [ExpressionFunctionParameter](./kibana-plugin-plugins-expressions-server.expressionfunctionparameter.md) | | +| [ExpressionRenderer](./kibana-plugin-plugins-expressions-server.expressionrenderer.md) | | +| [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-server.expressionrendererregistry.md) | | +| [ExpressionsServerPlugin](./kibana-plugin-plugins-expressions-server.expressionsserverplugin.md) | | +| [ExpressionType](./kibana-plugin-plugins-expressions-server.expressiontype.md) | | +| [FunctionsRegistry](./kibana-plugin-plugins-expressions-server.functionsregistry.md) | | +| [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) | | + +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [FontStyle](./kibana-plugin-plugins-expressions-server.fontstyle.md) | Enum of supported CSS font-style properties. | +| [FontWeight](./kibana-plugin-plugins-expressions-server.fontweight.md) | Enum of supported CSS font-weight properties. | +| [Overflow](./kibana-plugin-plugins-expressions-server.overflow.md) | Enum of supported CSS overflow properties. | +| [TextAlignment](./kibana-plugin-plugins-expressions-server.textalignment.md) | Enum of supported CSS text-align properties. | +| [TextDecoration](./kibana-plugin-plugins-expressions-server.textdecoration.md) | Enum of supported CSS text-decoration properties. | + +## Functions + +| Function | Description | +| --- | --- | +| [buildExpression(initialState)](./kibana-plugin-plugins-expressions-server.buildexpression.md) | Makes it easy to progressively build, update, and traverse an expression AST. You can either start with an empty AST, or provide an expression string, AST, or array of expression function builders to use as initial state. | +| [buildExpressionFunction(fnName, initialArgs)](./kibana-plugin-plugins-expressions-server.buildexpressionfunction.md) | Manages an AST for a single expression function. The return value can be provided to buildExpression to add this function to an expression.Note that to preserve type safety and ensure no args are missing, all required arguments for the specified function must be provided up front. If desired, they can be changed or removed later. | +| [format(ast, type)](./kibana-plugin-plugins-expressions-server.format.md) | | +| [formatExpression(ast)](./kibana-plugin-plugins-expressions-server.formatexpression.md) | Given expression pipeline AST, returns formatted string. | +| [isExpressionAstBuilder(val)](./kibana-plugin-plugins-expressions-server.isexpressionastbuilder.md) | Type guard that checks whether a given value is an ExpressionAstExpressionBuilder. This is useful when working with subexpressions, where you might be retrieving a function argument, and need to know whether it is an expression builder instance which you can perform operations on. | +| [parse(expression, startRule)](./kibana-plugin-plugins-expressions-server.parse.md) | | +| [parseExpression(expression)](./kibana-plugin-plugins-expressions-server.parseexpression.md) | Given expression pipeline string, returns parsed AST. | +| [plugin(initializerContext)](./kibana-plugin-plugins-expressions-server.plugin.md) | | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Datatable](./kibana-plugin-plugins-expressions-server.datatable.md) | A Datatable in Canvas is a unique structure that represents tabulated data. | +| [DatatableColumn](./kibana-plugin-plugins-expressions-server.datatablecolumn.md) | This type represents the shape of a column in a Datatable. | +| [ExecutionContext](./kibana-plugin-plugins-expressions-server.executioncontext.md) | ExecutionContext is an object available to all functions during a single execution; it provides various methods to perform side-effects. | +| [ExecutionParams](./kibana-plugin-plugins-expressions-server.executionparams.md) | | +| [ExecutionState](./kibana-plugin-plugins-expressions-server.executionstate.md) | | +| [ExecutorState](./kibana-plugin-plugins-expressions-server.executorstate.md) | | +| [ExpressionAstExpression](./kibana-plugin-plugins-expressions-server.expressionastexpression.md) | | +| [ExpressionAstExpressionBuilder](./kibana-plugin-plugins-expressions-server.expressionastexpressionbuilder.md) | | +| [ExpressionAstFunction](./kibana-plugin-plugins-expressions-server.expressionastfunction.md) | | +| [ExpressionAstFunctionBuilder](./kibana-plugin-plugins-expressions-server.expressionastfunctionbuilder.md) | | +| [ExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinition.md) | ExpressionFunctionDefinition is the interface plugins have to implement to register a function in expressions plugin. | +| [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) | A mapping of ExpressionFunctionDefinitions for functions which the Expressions services provides out-of-the-box. Any new functions registered by the Expressions plugin should have their types added here. | +| [ExpressionImage](./kibana-plugin-plugins-expressions-server.expressionimage.md) | | +| [ExpressionRenderDefinition](./kibana-plugin-plugins-expressions-server.expressionrenderdefinition.md) | | +| [ExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.expressiontypedefinition.md) | A generic type which represents a custom Expression Type Definition that's registered to the Interpreter. | +| [ExpressionTypeStyle](./kibana-plugin-plugins-expressions-server.expressiontypestyle.md) | An object that represents style information, typically CSS. | +| [Font](./kibana-plugin-plugins-expressions-server.font.md) | An interface representing a font in Canvas, with a textual label and the CSS font-value. | +| [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) | | +| [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) | | +| [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) | | +| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) | | +| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) | | +| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) | | +| [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) | Column in a PointSeries | +| [Range](./kibana-plugin-plugins-expressions-server.range.md) | | +| [SerializedDatatable](./kibana-plugin-plugins-expressions-server.serializeddatatable.md) | | +| [SerializedFieldFormat](./kibana-plugin-plugins-expressions-server.serializedfieldformat.md) | JSON representation of a field formatter configuration. Is used to carry information about how to format data in a data table as part of the column definition. | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | +| [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md) | | +| [ArgumentType](./kibana-plugin-plugins-expressions-server.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableRow](./kibana-plugin-plugins-expressions-server.datatablerow.md) | This type represents a row in a Datatable. | +| [ExecutionContainer](./kibana-plugin-plugins-expressions-server.executioncontainer.md) | | +| [ExecutorContainer](./kibana-plugin-plugins-expressions-server.executorcontainer.md) | | +| [ExpressionAstArgument](./kibana-plugin-plugins-expressions-server.expressionastargument.md) | | +| [ExpressionAstNode](./kibana-plugin-plugins-expressions-server.expressionastnode.md) | | +| [ExpressionFunctionKibana](./kibana-plugin-plugins-expressions-server.expressionfunctionkibana.md) | | +| [ExpressionsServerSetup](./kibana-plugin-plugins-expressions-server.expressionsserversetup.md) | | +| [ExpressionsServerStart](./kibana-plugin-plugins-expressions-server.expressionsserverstart.md) | | +| [ExpressionValue](./kibana-plugin-plugins-expressions-server.expressionvalue.md) | | +| [ExpressionValueBoxed](./kibana-plugin-plugins-expressions-server.expressionvalueboxed.md) | | +| [ExpressionValueConverter](./kibana-plugin-plugins-expressions-server.expressionvalueconverter.md) | | +| [ExpressionValueError](./kibana-plugin-plugins-expressions-server.expressionvalueerror.md) | | +| [ExpressionValueFilter](./kibana-plugin-plugins-expressions-server.expressionvaluefilter.md) | Represents an object that is a Filter. | +| [ExpressionValueNum](./kibana-plugin-plugins-expressions-server.expressionvaluenum.md) | | +| [ExpressionValueRender](./kibana-plugin-plugins-expressions-server.expressionvaluerender.md) | Represents an object that is intended to be rendered. | +| [ExpressionValueSearchContext](./kibana-plugin-plugins-expressions-server.expressionvaluesearchcontext.md) | | +| [ExpressionValueUnboxed](./kibana-plugin-plugins-expressions-server.expressionvalueunboxed.md) | | +| [FontLabel](./kibana-plugin-plugins-expressions-server.fontlabel.md) | This type contains a unions of all supported font labels, or the the name of the font the user would see in a UI. | +| [FontValue](./kibana-plugin-plugins-expressions-server.fontvalue.md) | This type contains a union of all supported font values, equivalent to the CSS font-value property. | +| [InterpreterErrorType](./kibana-plugin-plugins-expressions-server.interpretererrortype.md) | | +| [KIBANA\_CONTEXT\_NAME](./kibana-plugin-plugins-expressions-server.kibana_context_name.md) | | +| [KibanaContext](./kibana-plugin-plugins-expressions-server.kibanacontext.md) | | +| [KnownTypeToString](./kibana-plugin-plugins-expressions-server.knowntypetostring.md) | Map the type of the generic to a string-based representation of the type.If the provided generic is its own type interface, we use the value of the type key as a string literal type for it. | +| [PointSeries](./kibana-plugin-plugins-expressions-server.pointseries.md) | A PointSeries is a unique structure that represents dots on a chart. | +| [PointSeriesColumnName](./kibana-plugin-plugins-expressions-server.pointseriescolumnname.md) | Allowed column names in a PointSeries | +| [PointSeriesColumns](./kibana-plugin-plugins-expressions-server.pointseriescolumns.md) | Represents a collection of valid Columns in a PointSeries | +| [PointSeriesRow](./kibana-plugin-plugins-expressions-server.pointseriesrow.md) | | +| [Style](./kibana-plugin-plugins-expressions-server.style.md) | | +| [TypeString](./kibana-plugin-plugins-expressions-server.typestring.md) | If the type extends a Promise, we still need to return the string representation:someArgument: Promise<boolean | string> results in types: ['boolean', 'string'] | +| [TypeToString](./kibana-plugin-plugins-expressions-server.typetostring.md) | This can convert a type into a known Expression string representation of that type. For example, TypeToString<Datatable> will resolve to 'datatable'. This allows Expression Functions to continue to specify their type in a simple string format. | +| [UnmappedTypeStrings](./kibana-plugin-plugins-expressions-server.unmappedtypestrings.md) | Types used in Expressions that don't map to a primitive cleanly:date is typed as a number or string, and represents a date | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.overflow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.overflow.md new file mode 100644 index 0000000000000..2b1d1a34cd46a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.overflow.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Overflow](./kibana-plugin-plugins-expressions-server.overflow.md) + +## Overflow enum + +Enum of supported CSS `overflow` properties. + +Signature: + +```typescript +export declare enum Overflow +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| AUTO | "auto" | | +| HIDDEN | "hidden" | | +| SCROLL | "scroll" | | +| VISIBLE | "visible" | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parse.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parse.md new file mode 100644 index 0000000000000..ec2534986006f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parse.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [parse](./kibana-plugin-plugins-expressions-server.parse.md) + +## parse() function + +Signature: + +```typescript +export declare function parse(expression: E, startRule: S): S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| expression | E | | +| startRule | S | | + +Returns: + +`S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parseexpression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parseexpression.md new file mode 100644 index 0000000000000..0d8547fd5243b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.parseexpression.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [parseExpression](./kibana-plugin-plugins-expressions-server.parseexpression.md) + +## parseExpression() function + +Given expression pipeline string, returns parsed AST. + +Signature: + +```typescript +export declare function parseExpression(expression: string): ExpressionAstExpression; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| expression | string | | + +Returns: + +`ExpressionAstExpression` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.plugin.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.plugin.md new file mode 100644 index 0000000000000..79a7100ebf540 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.plugin.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [plugin](./kibana-plugin-plugins-expressions-server.plugin.md) + +## plugin() function + +Signature: + +```typescript +export declare function plugin(initializerContext: PluginInitializerContext): ExpressionsServerPlugin; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + +Returns: + +`ExpressionsServerPlugin` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseries.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseries.md new file mode 100644 index 0000000000000..f65efd705666d --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseries.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeries](./kibana-plugin-plugins-expressions-server.pointseries.md) + +## PointSeries type + +A `PointSeries` is a unique structure that represents dots on a chart. + +Signature: + +```typescript +export declare type PointSeries = ExpressionValueBoxed<'pointseries', { + columns: PointSeriesColumns; + rows: PointSeriesRow[]; +}>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.expression.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.expression.md new file mode 100644 index 0000000000000..c857a9f29fa60 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.expression.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) > [expression](./kibana-plugin-plugins-expressions-server.pointseriescolumn.expression.md) + +## PointSeriesColumn.expression property + +Signature: + +```typescript +expression: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.md new file mode 100644 index 0000000000000..5aec683421dd1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) + +## PointSeriesColumn interface + +Column in a PointSeries + +Signature: + +```typescript +export interface PointSeriesColumn +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [expression](./kibana-plugin-plugins-expressions-server.pointseriescolumn.expression.md) | string | | +| [role](./kibana-plugin-plugins-expressions-server.pointseriescolumn.role.md) | 'measure' | 'dimension' | | +| [type](./kibana-plugin-plugins-expressions-server.pointseriescolumn.type.md) | 'number' | 'string' | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.role.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.role.md new file mode 100644 index 0000000000000..1f6b770ecba15 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.role.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) > [role](./kibana-plugin-plugins-expressions-server.pointseriescolumn.role.md) + +## PointSeriesColumn.role property + +Signature: + +```typescript +role: 'measure' | 'dimension'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.type.md new file mode 100644 index 0000000000000..5cb51f460d722 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumn.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) > [type](./kibana-plugin-plugins-expressions-server.pointseriescolumn.type.md) + +## PointSeriesColumn.type property + +Signature: + +```typescript +type: 'number' | 'string'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumnname.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumnname.md new file mode 100644 index 0000000000000..2d8522b30903c --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumnname.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumnName](./kibana-plugin-plugins-expressions-server.pointseriescolumnname.md) + +## PointSeriesColumnName type + +Allowed column names in a PointSeries + +Signature: + +```typescript +export declare type PointSeriesColumnName = 'x' | 'y' | 'color' | 'size' | 'text'; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumns.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumns.md new file mode 100644 index 0000000000000..f6eee6e2bc9d1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriescolumns.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesColumns](./kibana-plugin-plugins-expressions-server.pointseriescolumns.md) + +## PointSeriesColumns type + +Represents a collection of valid Columns in a PointSeries + +Signature: + +```typescript +export declare type PointSeriesColumns = Record | {}; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriesrow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriesrow.md new file mode 100644 index 0000000000000..d9a77305e9f99 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.pointseriesrow.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [PointSeriesRow](./kibana-plugin-plugins-expressions-server.pointseriesrow.md) + +## PointSeriesRow type + +Signature: + +```typescript +export declare type PointSeriesRow = Record; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.from.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.from.md new file mode 100644 index 0000000000000..f349681c1472f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.from.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Range](./kibana-plugin-plugins-expressions-server.range.md) > [from](./kibana-plugin-plugins-expressions-server.range.from.md) + +## Range.from property + +Signature: + +```typescript +from: number; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.md new file mode 100644 index 0000000000000..d369d882757fc --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Range](./kibana-plugin-plugins-expressions-server.range.md) + +## Range interface + +Signature: + +```typescript +export interface Range +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [from](./kibana-plugin-plugins-expressions-server.range.from.md) | number | | +| [to](./kibana-plugin-plugins-expressions-server.range.to.md) | number | | +| [type](./kibana-plugin-plugins-expressions-server.range.type.md) | typeof name | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.to.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.to.md new file mode 100644 index 0000000000000..c5a1fe2fe2080 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.to.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Range](./kibana-plugin-plugins-expressions-server.range.md) > [to](./kibana-plugin-plugins-expressions-server.range.to.md) + +## Range.to property + +Signature: + +```typescript +to: number; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.type.md new file mode 100644 index 0000000000000..dd856dc0eb713 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.range.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Range](./kibana-plugin-plugins-expressions-server.range.md) > [type](./kibana-plugin-plugins-expressions-server.range.type.md) + +## Range.type property + +Signature: + +```typescript +type: typeof name; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.md new file mode 100644 index 0000000000000..12951f9323503 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [SerializedDatatable](./kibana-plugin-plugins-expressions-server.serializeddatatable.md) + +## SerializedDatatable interface + +Signature: + +```typescript +export interface SerializedDatatable extends Datatable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [rows](./kibana-plugin-plugins-expressions-server.serializeddatatable.rows.md) | string[][] | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.rows.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.rows.md new file mode 100644 index 0000000000000..e82504f153f6b --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializeddatatable.rows.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [SerializedDatatable](./kibana-plugin-plugins-expressions-server.serializeddatatable.md) > [rows](./kibana-plugin-plugins-expressions-server.serializeddatatable.rows.md) + +## SerializedDatatable.rows property + +Signature: + +```typescript +rows: string[][]; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.id.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.id.md new file mode 100644 index 0000000000000..def3296aedcf7 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-server.serializedfieldformat.md) > [id](./kibana-plugin-plugins-expressions-server.serializedfieldformat.id.md) + +## SerializedFieldFormat.id property + +Signature: + +```typescript +id?: string; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.md new file mode 100644 index 0000000000000..c62e830ccf7b9 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-server.serializedfieldformat.md) + +## SerializedFieldFormat interface + +JSON representation of a field formatter configuration. Is used to carry information about how to format data in a data table as part of the column definition. + +Signature: + +```typescript +export interface SerializedFieldFormat> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-expressions-server.serializedfieldformat.id.md) | string | | +| [params](./kibana-plugin-plugins-expressions-server.serializedfieldformat.params.md) | TParams | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.params.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.params.md new file mode 100644 index 0000000000000..8861f729aa2b1 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.serializedfieldformat.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [SerializedFieldFormat](./kibana-plugin-plugins-expressions-server.serializedfieldformat.md) > [params](./kibana-plugin-plugins-expressions-server.serializedfieldformat.params.md) + +## SerializedFieldFormat.params property + +Signature: + +```typescript +params?: TParams; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.style.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.style.md new file mode 100644 index 0000000000000..e43addfd5ff30 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.style.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [Style](./kibana-plugin-plugins-expressions-server.style.md) + +## Style type + +Signature: + +```typescript +export declare type Style = ExpressionTypeStyle; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textalignment.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textalignment.md new file mode 100644 index 0000000000000..2adc12371b4be --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textalignment.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TextAlignment](./kibana-plugin-plugins-expressions-server.textalignment.md) + +## TextAlignment enum + +Enum of supported CSS `text-align` properties. + +Signature: + +```typescript +export declare enum TextAlignment +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| CENTER | "center" | | +| JUSTIFY | "justify" | | +| LEFT | "left" | | +| RIGHT | "right" | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textdecoration.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textdecoration.md new file mode 100644 index 0000000000000..98d9b38547baf --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.textdecoration.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TextDecoration](./kibana-plugin-plugins-expressions-server.textdecoration.md) + +## TextDecoration enum + +Enum of supported CSS `text-decoration` properties. + +Signature: + +```typescript +export declare enum TextDecoration +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| NONE | "none" | | +| UNDERLINE | "underline" | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry._constructor_.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry._constructor_.md new file mode 100644 index 0000000000000..87290d88214d0 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) > [(constructor)](./kibana-plugin-plugins-expressions-server.typesregistry._constructor_.md) + +## TypesRegistry.(constructor) + +Constructs a new instance of the `TypesRegistry` class + +Signature: + +```typescript +constructor(executor: Executor); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| executor | Executor<any> | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.get.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.get.md new file mode 100644 index 0000000000000..c8d674eab50cd --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.get.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) > [get](./kibana-plugin-plugins-expressions-server.typesregistry.get.md) + +## TypesRegistry.get() method + +Signature: + +```typescript +get(id: string): ExpressionType | null; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`ExpressionType | null` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.md new file mode 100644 index 0000000000000..2c4d75e020035 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) + +## TypesRegistry class + +Signature: + +```typescript +export declare class TypesRegistry implements IRegistry +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(executor)](./kibana-plugin-plugins-expressions-server.typesregistry._constructor_.md) | | Constructs a new instance of the TypesRegistry class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [get(id)](./kibana-plugin-plugins-expressions-server.typesregistry.get.md) | | | +| [register(typeDefinition)](./kibana-plugin-plugins-expressions-server.typesregistry.register.md) | | | +| [toArray()](./kibana-plugin-plugins-expressions-server.typesregistry.toarray.md) | | | +| [toJS()](./kibana-plugin-plugins-expressions-server.typesregistry.tojs.md) | | | + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.register.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.register.md new file mode 100644 index 0000000000000..935a862407dfe --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.register.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) > [register](./kibana-plugin-plugins-expressions-server.typesregistry.register.md) + +## TypesRegistry.register() method + +Signature: + +```typescript +register(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| typeDefinition | AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition) | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.toarray.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.toarray.md new file mode 100644 index 0000000000000..e3c6b13a22a58 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.toarray.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) > [toArray](./kibana-plugin-plugins-expressions-server.typesregistry.toarray.md) + +## TypesRegistry.toArray() method + +Signature: + +```typescript +toArray(): ExpressionType[]; +``` +Returns: + +`ExpressionType[]` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.tojs.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.tojs.md new file mode 100644 index 0000000000000..2ff258bd54e44 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typesregistry.tojs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypesRegistry](./kibana-plugin-plugins-expressions-server.typesregistry.md) > [toJS](./kibana-plugin-plugins-expressions-server.typesregistry.tojs.md) + +## TypesRegistry.toJS() method + +Signature: + +```typescript +toJS(): Record; +``` +Returns: + +`Record` + diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typestring.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typestring.md new file mode 100644 index 0000000000000..af4d5ae0bf814 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typestring.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypeString](./kibana-plugin-plugins-expressions-server.typestring.md) + +## TypeString type + +If the type extends a Promise, we still need to return the string representation: + +`someArgument: Promise` results in `types: ['boolean', 'string']` + +Signature: + +```typescript +export declare type TypeString = KnownTypeToString>; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typetostring.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typetostring.md new file mode 100644 index 0000000000000..578438c03a0e5 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.typetostring.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [TypeToString](./kibana-plugin-plugins-expressions-server.typetostring.md) + +## TypeToString type + +This can convert a type into a known Expression string representation of that type. For example, `TypeToString` will resolve to `'datatable'`. This allows Expression Functions to continue to specify their type in a simple string format. + +Signature: + +```typescript +export declare type TypeToString = KnownTypeToString | UnmappedTypeStrings; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.unmappedtypestrings.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.unmappedtypestrings.md new file mode 100644 index 0000000000000..da872bfabce4f --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.unmappedtypestrings.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [UnmappedTypeStrings](./kibana-plugin-plugins-expressions-server.unmappedtypestrings.md) + +## UnmappedTypeStrings type + +Types used in Expressions that don't map to a primitive cleanly: + +`date` is typed as a number or string, and represents a date + +Signature: + +```typescript +export declare type UnmappedTypeStrings = 'date' | 'filter'; +``` diff --git a/docs/development/plugins/ui_actions/public/index.md b/docs/development/plugins/ui_actions/public/index.md new file mode 100644 index 0000000000000..cbc7035b880fa --- /dev/null +++ b/docs/development/plugins/ui_actions/public/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.execute.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.execute.md new file mode 100644 index 0000000000000..22a520123cf3f --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.execute.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [execute](./kibana-plugin-plugins-ui_actions-public.action.execute.md) + +## Action.execute() method + +Executes the action. + +Signature: + +```typescript +execute(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.getdisplayname.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.getdisplayname.md new file mode 100644 index 0000000000000..cd8cc527e96ec --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.getdisplayname.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [getDisplayName](./kibana-plugin-plugins-ui_actions-public.action.getdisplayname.md) + +## Action.getDisplayName() method + +Returns a title to be displayed to the user. + +Signature: + +```typescript +getDisplayName(context: ActionExecutionContext): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`string` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.gethref.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.gethref.md new file mode 100644 index 0000000000000..5ad9d5e24cf87 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.gethref.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [getHref](./kibana-plugin-plugins-ui_actions-public.action.gethref.md) + +## Action.getHref() method + +This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". + +Signature: + +```typescript +getHref?(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.geticontype.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.geticontype.md new file mode 100644 index 0000000000000..34d45c4ec75c2 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.geticontype.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [getIconType](./kibana-plugin-plugins-ui_actions-public.action.geticontype.md) + +## Action.getIconType() method + +Optional EUI icon type that can be displayed along with the title. + +Signature: + +```typescript +getIconType(context: ActionExecutionContext): string | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`string | undefined` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.id.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.id.md new file mode 100644 index 0000000000000..e32a5c8592cce --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [id](./kibana-plugin-plugins-ui_actions-public.action.id.md) + +## Action.id property + +A unique identifier for this action instance. + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.iscompatible.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.iscompatible.md new file mode 100644 index 0000000000000..7a1f6cd23be17 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.iscompatible.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [isCompatible](./kibana-plugin-plugins-ui_actions-public.action.iscompatible.md) + +## Action.isCompatible() method + +Returns a promise that resolves to true if this action is compatible given the context, otherwise resolves to false. + +Signature: + +```typescript +isCompatible(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.md new file mode 100644 index 0000000000000..19af63a679de8 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) + +## Action interface + +Signature: + +```typescript +export interface Action extends Partial>> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-ui_actions-public.action.id.md) | string | A unique identifier for this action instance. | +| [MenuItem](./kibana-plugin-plugins-ui_actions-public.action.menuitem.md) | UiComponent<{
context: ActionExecutionContext<Context>;
}> | UiComponent to render when displaying this action as a context menu item. If not provided, getDisplayName will be used instead. | +| [order](./kibana-plugin-plugins-ui_actions-public.action.order.md) | number | Determined the order when there is more than one action matched to a trigger. Higher numbers are displayed first. | +| [type](./kibana-plugin-plugins-ui_actions-public.action.type.md) | T | The action type is what determines the context shape. | + +## Methods + +| Method | Description | +| --- | --- | +| [execute(context)](./kibana-plugin-plugins-ui_actions-public.action.execute.md) | Executes the action. | +| [getDisplayName(context)](./kibana-plugin-plugins-ui_actions-public.action.getdisplayname.md) | Returns a title to be displayed to the user. | +| [getHref(context)](./kibana-plugin-plugins-ui_actions-public.action.gethref.md) | This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". | +| [getIconType(context)](./kibana-plugin-plugins-ui_actions-public.action.geticontype.md) | Optional EUI icon type that can be displayed along with the title. | +| [isCompatible(context)](./kibana-plugin-plugins-ui_actions-public.action.iscompatible.md) | Returns a promise that resolves to true if this action is compatible given the context, otherwise resolves to false. | +| [shouldAutoExecute(context)](./kibana-plugin-plugins-ui_actions-public.action.shouldautoexecute.md) | Determines if action should be executed automatically, without first showing up in context menu. false by default. | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.menuitem.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.menuitem.md new file mode 100644 index 0000000000000..ac2168b88e3be --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.menuitem.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [MenuItem](./kibana-plugin-plugins-ui_actions-public.action.menuitem.md) + +## Action.MenuItem property + +`UiComponent` to render when displaying this action as a context menu item. If not provided, `getDisplayName` will be used instead. + +Signature: + +```typescript +MenuItem?: UiComponent<{ + context: ActionExecutionContext; + }>; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.order.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.order.md new file mode 100644 index 0000000000000..ce9f66cfe5143 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.order.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [order](./kibana-plugin-plugins-ui_actions-public.action.order.md) + +## Action.order property + +Determined the order when there is more than one action matched to a trigger. Higher numbers are displayed first. + +Signature: + +```typescript +order?: number; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.shouldautoexecute.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.shouldautoexecute.md new file mode 100644 index 0000000000000..1a784f5dad2d5 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.shouldautoexecute.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [shouldAutoExecute](./kibana-plugin-plugins-ui_actions-public.action.shouldautoexecute.md) + +## Action.shouldAutoExecute() method + +Determines if action should be executed automatically, without first showing up in context menu. false by default. + +Signature: + +```typescript +shouldAutoExecute?(context: ActionExecutionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionExecutionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.type.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.type.md new file mode 100644 index 0000000000000..c423df9d1324c --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Action](./kibana-plugin-plugins-ui_actions-public.action.md) > [type](./kibana-plugin-plugins-ui_actions-public.action.type.md) + +## Action.type property + +The action type is what determines the context shape. + +Signature: + +```typescript +readonly type: T; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_field.md new file mode 100644 index 0000000000000..25788d7aecc9f --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_field.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ACTION\_VISUALIZE\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_field.md) + +## ACTION\_VISUALIZE\_FIELD variable + +Signature: + +```typescript +ACTION_VISUALIZE_FIELD = "ACTION_VISUALIZE_FIELD" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md new file mode 100644 index 0000000000000..c9ef93eff934b --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ACTION\_VISUALIZE\_GEO\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md) + +## ACTION\_VISUALIZE\_GEO\_FIELD variable + +Signature: + +```typescript +ACTION_VISUALIZE_GEO_FIELD = "ACTION_VISUALIZE_GEO_FIELD" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionbytype.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionbytype.md new file mode 100644 index 0000000000000..3ceb96adadb1a --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionbytype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionByType](./kibana-plugin-plugins-ui_actions-public.actionbytype.md) + +## ActionByType type + +Signature: + +```typescript +export declare type ActionByType = Action; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.__.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.__.md new file mode 100644 index 0000000000000..eb7b1e5954ed2 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.__.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) > [""](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.__.md) + +## ActionContextMapping."" property + +Signature: + +```typescript +[DEFAULT_ACTION]: BaseContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_field.md new file mode 100644 index 0000000000000..eb0547bbf8261 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_field.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) > [ACTION\_VISUALIZE\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_field.md) + +## ActionContextMapping.ACTION\_VISUALIZE\_FIELD property + +Signature: + +```typescript +[ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_geo_field.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_geo_field.md new file mode 100644 index 0000000000000..b44ed75106423 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_geo_field.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) > [ACTION\_VISUALIZE\_GEO\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_geo_field.md) + +## ActionContextMapping.ACTION\_VISUALIZE\_GEO\_FIELD property + +Signature: + +```typescript +[ACTION_VISUALIZE_GEO_FIELD]: VisualizeFieldContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md new file mode 100644 index 0000000000000..740e6ac63bfba --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) + +## ActionContextMapping interface + +Signature: + +```typescript +export interface ActionContextMapping +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [""](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.__.md) | BaseContext | | +| [ACTION\_VISUALIZE\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_field.md) | VisualizeFieldContext | | +| [ACTION\_VISUALIZE\_GEO\_FIELD](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.action_visualize_geo_field.md) | VisualizeFieldContext | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiondefinitionbytype.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiondefinitionbytype.md new file mode 100644 index 0000000000000..ba4dc39088fe4 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiondefinitionbytype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionDefinitionByType](./kibana-plugin-plugins-ui_actions-public.actiondefinitionbytype.md) + +## ActionDefinitionByType type + +Signature: + +```typescript +export declare type ActionDefinitionByType = ActionDefinition; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutioncontext.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutioncontext.md new file mode 100644 index 0000000000000..3271d86779959 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutioncontext.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionExecutionContext](./kibana-plugin-plugins-ui_actions-public.actionexecutioncontext.md) + +## ActionExecutionContext type + +Action methods are executed with Context from trigger + [ActionExecutionMeta](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md) + +Signature: + +```typescript +export declare type ActionExecutionContext = Context & ActionExecutionMeta; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md new file mode 100644 index 0000000000000..2056d8f9c7fc6 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionExecutionMeta](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md) + +## ActionExecutionMeta interface + +During action execution we can provide additional information, for example, trigger, that caused the action execution + +Signature: + +```typescript +export interface ActionExecutionMeta +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [trigger](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.trigger.md) | Trigger | Trigger that executed the action | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.trigger.md new file mode 100644 index 0000000000000..530c2fe514d2c --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.trigger.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionExecutionMeta](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md) > [trigger](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.trigger.md) + +## ActionExecutionMeta.trigger property + +Trigger that executed the action + +Signature: + +```typescript +trigger: Trigger; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiontype.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiontype.md new file mode 100644 index 0000000000000..4916585531004 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.actiontype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [ActionType](./kibana-plugin-plugins-ui_actions-public.actiontype.md) + +## ActionType type + +Signature: + +```typescript +export declare type ActionType = keyof ActionContextMapping; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md new file mode 100644 index 0000000000000..94e66bf404f5c --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md) + +## APPLY\_FILTER\_TRIGGER variable + +Signature: + +```typescript +APPLY_FILTER_TRIGGER = "FILTER_TRIGGER" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md new file mode 100644 index 0000000000000..e1fb6d342457e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [applyFilterTrigger](./kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md) + +## applyFilterTrigger variable + +Signature: + +```typescript +applyFilterTrigger: Trigger<'FILTER_TRIGGER'> +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.buildcontextmenuforactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.buildcontextmenuforactions.md new file mode 100644 index 0000000000000..2d6c0ff106072 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.buildcontextmenuforactions.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [buildContextMenuForActions](./kibana-plugin-plugins-ui_actions-public.buildcontextmenuforactions.md) + +## buildContextMenuForActions() function + +Transforms an array of Actions to the shape EuiContextMenuPanel expects. + +Signature: + +```typescript +export declare function buildContextMenuForActions({ actions, title, closeMenu, }: BuildContextMenuParams): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { actions, title, closeMenu, } | BuildContextMenuParams | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.createaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.createaction.md new file mode 100644 index 0000000000000..04ab36c2e3f58 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.createaction.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [createAction](./kibana-plugin-plugins-ui_actions-public.createaction.md) + +## createAction() function + +Signature: + +```typescript +export declare function createAction(action: ActionDefinitionByType): ActionByType; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| action | ActionDefinitionByType<T> | | + +Returns: + +`ActionByType` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror._constructor_.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror._constructor_.md new file mode 100644 index 0000000000000..f06bb05270ff0 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror._constructor_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [IncompatibleActionError](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md) > [(constructor)](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror._constructor_.md) + +## IncompatibleActionError.(constructor) + +Constructs a new instance of the `IncompatibleActionError` class + +Signature: + +```typescript +constructor(); +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.code.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.code.md new file mode 100644 index 0000000000000..f16aa47438d72 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.code.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [IncompatibleActionError](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md) > [code](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.code.md) + +## IncompatibleActionError.code property + +Signature: + +```typescript +code: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md new file mode 100644 index 0000000000000..7c9943a53c2bb --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [IncompatibleActionError](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md) + +## IncompatibleActionError class + +Signature: + +```typescript +export declare class IncompatibleActionError extends Error +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)()](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror._constructor_.md) | | Constructs a new instance of the IncompatibleActionError class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [code](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.code.md) | | string | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md new file mode 100644 index 0000000000000..ce4e8c17b9dff --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.md @@ -0,0 +1,66 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) + +## kibana-plugin-plugins-ui\_actions-public package + +## Classes + +| Class | Description | +| --- | --- | +| [IncompatibleActionError](./kibana-plugin-plugins-ui_actions-public.incompatibleactionerror.md) | | +| [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) | | + +## Functions + +| Function | Description | +| --- | --- | +| [buildContextMenuForActions({ actions, title, closeMenu, })](./kibana-plugin-plugins-ui_actions-public.buildcontextmenuforactions.md) | Transforms an array of Actions to the shape EuiContextMenuPanel expects. | +| [createAction(action)](./kibana-plugin-plugins-ui_actions-public.createaction.md) | | +| [plugin(initializerContext)](./kibana-plugin-plugins-ui_actions-public.plugin.md) | | + +## Interfaces + +| Interface | Description | +| --- | --- | +| [Action](./kibana-plugin-plugins-ui_actions-public.action.md) | | +| [ActionContextMapping](./kibana-plugin-plugins-ui_actions-public.actioncontextmapping.md) | | +| [ActionExecutionMeta](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md) | During action execution we can provide additional information, for example, trigger, that caused the action execution | +| [Trigger](./kibana-plugin-plugins-ui_actions-public.trigger.md) | This is a convenience interface used to register a \*trigger\*.Trigger specifies a named anchor to which Action can be attached. When Trigger is being \*called\* it creates a Context object and passes it to the execute method of an Action.More than one action can be attached to a single trigger, in which case when trigger is \*called\* it first displays a context menu for user to pick a single action to execute. | +| [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) | | +| [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) | A convenience interface used to register an action. | +| [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) | Represents something that can be displayed to user in UI. | +| [UiActionsServiceParams](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md) | | +| [VisualizeFieldContext](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md) | | + +## Variables + +| Variable | Description | +| --- | --- | +| [ACTION\_VISUALIZE\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_field.md) | | +| [ACTION\_VISUALIZE\_GEO\_FIELD](./kibana-plugin-plugins-ui_actions-public.action_visualize_geo_field.md) | | +| [APPLY\_FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.apply_filter_trigger.md) | | +| [applyFilterTrigger](./kibana-plugin-plugins-ui_actions-public.applyfiltertrigger.md) | | +| [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.select_range_trigger.md) | | +| [selectRangeTrigger](./kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md) | | +| [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.value_click_trigger.md) | | +| [valueClickTrigger](./kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md) | | +| [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md) | | +| [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md) | | +| [visualizeFieldTrigger](./kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md) | | +| [visualizeGeoFieldTrigger](./kibana-plugin-plugins-ui_actions-public.visualizegeofieldtrigger.md) | | + +## Type Aliases + +| Type Alias | Description | +| --- | --- | +| [ActionByType](./kibana-plugin-plugins-ui_actions-public.actionbytype.md) | | +| [ActionDefinitionByType](./kibana-plugin-plugins-ui_actions-public.actiondefinitionbytype.md) | | +| [ActionExecutionContext](./kibana-plugin-plugins-ui_actions-public.actionexecutioncontext.md) | Action methods are executed with Context from trigger + [ActionExecutionMeta](./kibana-plugin-plugins-ui_actions-public.actionexecutionmeta.md) | +| [ActionType](./kibana-plugin-plugins-ui_actions-public.actiontype.md) | | +| [TriggerContext](./kibana-plugin-plugins-ui_actions-public.triggercontext.md) | | +| [TriggerId](./kibana-plugin-plugins-ui_actions-public.triggerid.md) | | +| [UiActionsPresentableGrouping](./kibana-plugin-plugins-ui_actions-public.uiactionspresentablegrouping.md) | | +| [UiActionsSetup](./kibana-plugin-plugins-ui_actions-public.uiactionssetup.md) | | +| [UiActionsStart](./kibana-plugin-plugins-ui_actions-public.uiactionsstart.md) | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.plugin.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.plugin.md new file mode 100644 index 0000000000000..d9427317d4fc6 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.plugin.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [plugin](./kibana-plugin-plugins-ui_actions-public.plugin.md) + +## plugin() function + +Signature: + +```typescript +export declare function plugin(initializerContext: PluginInitializerContext): UiActionsPlugin; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| initializerContext | PluginInitializerContext | | + +Returns: + +`UiActionsPlugin` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md new file mode 100644 index 0000000000000..fd784ff17fa84 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.select_range_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.select_range_trigger.md) + +## SELECT\_RANGE\_TRIGGER variable + +Signature: + +```typescript +SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md new file mode 100644 index 0000000000000..0d9fa2d83ee57 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [selectRangeTrigger](./kibana-plugin-plugins-ui_actions-public.selectrangetrigger.md) + +## selectRangeTrigger variable + +Signature: + +```typescript +selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'> +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.description.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.description.md new file mode 100644 index 0000000000000..76faaf8e1a691 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.description.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Trigger](./kibana-plugin-plugins-ui_actions-public.trigger.md) > [description](./kibana-plugin-plugins-ui_actions-public.trigger.description.md) + +## Trigger.description property + +A longer user friendly description of the trigger. + +Signature: + +```typescript +description?: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md new file mode 100644 index 0000000000000..426f17f9a0352 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Trigger](./kibana-plugin-plugins-ui_actions-public.trigger.md) > [id](./kibana-plugin-plugins-ui_actions-public.trigger.id.md) + +## Trigger.id property + +Unique name of the trigger as identified in `ui_actions` plugin trigger registry, such as "SELECT\_RANGE\_TRIGGER" or "VALUE\_CLICK\_TRIGGER". + +Signature: + +```typescript +id: ID; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md new file mode 100644 index 0000000000000..b69bba892f475 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Trigger](./kibana-plugin-plugins-ui_actions-public.trigger.md) + +## Trigger interface + +This is a convenience interface used to register a \*trigger\*. + +`Trigger` specifies a named anchor to which `Action` can be attached. When `Trigger` is being \*called\* it creates a `Context` object and passes it to the `execute` method of an `Action`. + +More than one action can be attached to a single trigger, in which case when trigger is \*called\* it first displays a context menu for user to pick a single action to execute. + +Signature: + +```typescript +export interface Trigger +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [description](./kibana-plugin-plugins-ui_actions-public.trigger.description.md) | string | A longer user friendly description of the trigger. | +| [id](./kibana-plugin-plugins-ui_actions-public.trigger.id.md) | ID | Unique name of the trigger as identified in ui_actions plugin trigger registry, such as "SELECT\_RANGE\_TRIGGER" or "VALUE\_CLICK\_TRIGGER". | +| [title](./kibana-plugin-plugins-ui_actions-public.trigger.title.md) | string | User friendly name of the trigger. | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.title.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.title.md new file mode 100644 index 0000000000000..ded71c8d0c437 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.trigger.title.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [Trigger](./kibana-plugin-plugins-ui_actions-public.trigger.md) > [title](./kibana-plugin-plugins-ui_actions-public.trigger.title.md) + +## Trigger.title property + +User friendly name of the trigger. + +Signature: + +```typescript +title?: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontext.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontext.md new file mode 100644 index 0000000000000..4ce95d27ecffa --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontext.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContext](./kibana-plugin-plugins-ui_actions-public.triggercontext.md) + +## TriggerContext type + +Signature: + +```typescript +export declare type TriggerContext = T extends TriggerId ? TriggerContextMapping[T] : never; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md new file mode 100644 index 0000000000000..17ad926f8ee82 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [""](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md) + +## TriggerContextMapping."" property + +Signature: + +```typescript +[DEFAULT_TRIGGER]: TriggerContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md new file mode 100644 index 0000000000000..0ccf8aa3d7415 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md) + +## TriggerContextMapping.FILTER\_TRIGGER property + +Signature: + +```typescript +[APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md new file mode 100644 index 0000000000000..9db44d4dc7b05 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) + +## TriggerContextMapping interface + +Signature: + +```typescript +export interface TriggerContextMapping +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [""](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.__.md) | TriggerContext | | +| [FILTER\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.filter_trigger.md) | ApplyGlobalFilterActionContext | | +| [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md) | RangeSelectContext | | +| [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md) | ValueClickContext | | +| [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md) | VisualizeFieldContext | | +| [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md) | VisualizeFieldContext | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md new file mode 100644 index 0000000000000..c5ef6843390b3 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [SELECT\_RANGE\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.select_range_trigger.md) + +## TriggerContextMapping.SELECT\_RANGE\_TRIGGER property + +Signature: + +```typescript +[SELECT_RANGE_TRIGGER]: RangeSelectContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md new file mode 100644 index 0000000000000..129144a66cee5 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.value_click_trigger.md) + +## TriggerContextMapping.VALUE\_CLICK\_TRIGGER property + +Signature: + +```typescript +[VALUE_CLICK_TRIGGER]: ValueClickContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md new file mode 100644 index 0000000000000..feaaffac8a234 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_field_trigger.md) + +## TriggerContextMapping.VISUALIZE\_FIELD\_TRIGGER property + +Signature: + +```typescript +[VISUALIZE_FIELD_TRIGGER]: VisualizeFieldContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md new file mode 100644 index 0000000000000..023490a2ae027 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerContextMapping](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.md) > [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.triggercontextmapping.visualize_geo_field_trigger.md) + +## TriggerContextMapping.VISUALIZE\_GEO\_FIELD\_TRIGGER property + +Signature: + +```typescript +[VISUALIZE_GEO_FIELD_TRIGGER]: VisualizeFieldContext; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggerid.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggerid.md new file mode 100644 index 0000000000000..6e5a234e286f9 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.triggerid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [TriggerId](./kibana-plugin-plugins-ui_actions-public.triggerid.md) + +## TriggerId type + +Signature: + +```typescript +export declare type TriggerId = keyof TriggerContextMapping; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.execute.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.execute.md new file mode 100644 index 0000000000000..a2cf61ecc1306 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.execute.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [execute](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.execute.md) + +## UiActionsActionDefinition.execute() method + +Executes the action. + +Signature: + +```typescript +execute(context: ActionDefinitionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionDefinitionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.gethref.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.gethref.md new file mode 100644 index 0000000000000..83fee1233a206 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.gethref.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [getHref](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.gethref.md) + +## UiActionsActionDefinition.getHref() method + +This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". + +Signature: + +```typescript +getHref?(context: ActionDefinitionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionDefinitionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.id.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.id.md new file mode 100644 index 0000000000000..01fa6abce3b4a --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [id](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.id.md) + +## UiActionsActionDefinition.id property + +ID of the action that uniquely identifies this action in the actions registry. + +Signature: + +```typescript +readonly id: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.iscompatible.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.iscompatible.md new file mode 100644 index 0000000000000..736cc40c4243f --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.iscompatible.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [isCompatible](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.iscompatible.md) + +## UiActionsActionDefinition.isCompatible() method + +Returns a promise that resolves to true if this item is compatible given the context and should be displayed to user, otherwise resolves to false. + +Signature: + +```typescript +isCompatible?(context: ActionDefinitionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionDefinitionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md new file mode 100644 index 0000000000000..7c873715795e9 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md @@ -0,0 +1,30 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) + +## UiActionsActionDefinition interface + +A convenience interface used to register an action. + +Signature: + +```typescript +export interface ActionDefinition extends Partial>> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.id.md) | string | ID of the action that uniquely identifies this action in the actions registry. | +| [type](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.type.md) | ActionType | ID of the factory for this action. Used to construct dynamic actions. | + +## Methods + +| Method | Description | +| --- | --- | +| [execute(context)](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.execute.md) | Executes the action. | +| [getHref(context)](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.gethref.md) | This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". | +| [isCompatible(context)](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.iscompatible.md) | Returns a promise that resolves to true if this item is compatible given the context and should be displayed to user, otherwise resolves to false. | +| [shouldAutoExecute(context)](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.shouldautoexecute.md) | Determines if action should be executed automatically, without first showing up in context menu. false by default. | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.shouldautoexecute.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.shouldautoexecute.md new file mode 100644 index 0000000000000..04b9975f3b92e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.shouldautoexecute.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [shouldAutoExecute](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.shouldautoexecute.md) + +## UiActionsActionDefinition.shouldAutoExecute() method + +Determines if action should be executed automatically, without first showing up in context menu. false by default. + +Signature: + +```typescript +shouldAutoExecute?(context: ActionDefinitionContext): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | ActionDefinitionContext<Context> | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.type.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.type.md new file mode 100644 index 0000000000000..125f834e9036e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsActionDefinition](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.md) > [type](./kibana-plugin-plugins-ui_actions-public.uiactionsactiondefinition.type.md) + +## UiActionsActionDefinition.type property + +ID of the factory for this action. Used to construct dynamic actions. + +Signature: + +```typescript +readonly type?: ActionType; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplayname.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplayname.md new file mode 100644 index 0000000000000..986ad4afa5a48 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplayname.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [getDisplayName](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplayname.md) + +## UiActionsPresentable.getDisplayName() method + +Returns a title to be displayed to the user. + +Signature: + +```typescript +getDisplayName(context: Context): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | Context | | + +Returns: + +`string` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplaynametooltip.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplaynametooltip.md new file mode 100644 index 0000000000000..8fc859d5713e6 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplaynametooltip.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [getDisplayNameTooltip](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplaynametooltip.md) + +## UiActionsPresentable.getDisplayNameTooltip() method + +Returns tooltip text which should be displayed when user hovers this object. Should return empty string if tooltip should not be displayed. + +Signature: + +```typescript +getDisplayNameTooltip(context: Context): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | Context | | + +Returns: + +`string` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.gethref.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.gethref.md new file mode 100644 index 0000000000000..0c9bd434ff331 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.gethref.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [getHref](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.gethref.md) + +## UiActionsPresentable.getHref() method + +This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". + +Signature: + +```typescript +getHref?(context: Context): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | Context | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.geticontype.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.geticontype.md new file mode 100644 index 0000000000000..8bf5af0f3b7e2 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.geticontype.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [getIconType](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.geticontype.md) + +## UiActionsPresentable.getIconType() method + +Optional EUI icon type that can be displayed along with the title. + +Signature: + +```typescript +getIconType(context: Context): string | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | Context | | + +Returns: + +`string | undefined` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.grouping.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.grouping.md new file mode 100644 index 0000000000000..6b160becf1afc --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.grouping.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [grouping](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.grouping.md) + +## UiActionsPresentable.grouping property + +Grouping where this item should appear as a submenu. Each entry is a new sub-menu level. For example, used to show drilldowns and sharing options in panel context menu in a sub-menu. + +Signature: + +```typescript +readonly grouping?: PresentableGrouping; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.id.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.id.md new file mode 100644 index 0000000000000..e98401d95cba8 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.id.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [id](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.id.md) + +## UiActionsPresentable.id property + +ID that uniquely identifies this object. + +Signature: + +```typescript +readonly id: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.iscompatible.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.iscompatible.md new file mode 100644 index 0000000000000..073f75c840bcd --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.iscompatible.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [isCompatible](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.iscompatible.md) + +## UiActionsPresentable.isCompatible() method + +Returns a promise that resolves to true if this item is compatible given the context and should be displayed to user, otherwise resolves to false. + +Signature: + +```typescript +isCompatible(context: Context): Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| context | Context | | + +Returns: + +`Promise` + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md new file mode 100644 index 0000000000000..03fa7fb6e447e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md @@ -0,0 +1,33 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) + +## UiActionsPresentable interface + +Represents something that can be displayed to user in UI. + +Signature: + +```typescript +export interface Presentable +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [grouping](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.grouping.md) | PresentableGrouping<Context> | Grouping where this item should appear as a submenu. Each entry is a new sub-menu level. For example, used to show drilldowns and sharing options in panel context menu in a sub-menu. | +| [id](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.id.md) | string | ID that uniquely identifies this object. | +| [MenuItem](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.menuitem.md) | UiComponent<{
context: Context;
}> | UiComponent to render when displaying this entity as a context menu item. If not provided, getDisplayName will be used instead. | +| [order](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.order.md) | number | Determines the display order in relation to other items. Higher numbers are displayed first. | + +## Methods + +| Method | Description | +| --- | --- | +| [getDisplayName(context)](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplayname.md) | Returns a title to be displayed to the user. | +| [getDisplayNameTooltip(context)](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.getdisplaynametooltip.md) | Returns tooltip text which should be displayed when user hovers this object. Should return empty string if tooltip should not be displayed. | +| [getHref(context)](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.gethref.md) | This method should return a link if this item can be clicked on. The link is used to navigate user if user middle-clicks it or Ctrl + clicks or right-clicks and selects "Open in new tab". | +| [getIconType(context)](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.geticontype.md) | Optional EUI icon type that can be displayed along with the title. | +| [isCompatible(context)](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.iscompatible.md) | Returns a promise that resolves to true if this item is compatible given the context and should be displayed to user, otherwise resolves to false. | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.menuitem.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.menuitem.md new file mode 100644 index 0000000000000..42afe6b8361f0 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.menuitem.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [MenuItem](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.menuitem.md) + +## UiActionsPresentable.MenuItem property + +`UiComponent` to render when displaying this entity as a context menu item. If not provided, `getDisplayName` will be used instead. + +Signature: + +```typescript +readonly MenuItem?: UiComponent<{ + context: Context; + }>; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.order.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.order.md new file mode 100644 index 0000000000000..0bbf80dc89211 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentable.order.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentable](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.md) > [order](./kibana-plugin-plugins-ui_actions-public.uiactionspresentable.order.md) + +## UiActionsPresentable.order property + +Determines the display order in relation to other items. Higher numbers are displayed first. + +Signature: + +```typescript +readonly order: number; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentablegrouping.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentablegrouping.md new file mode 100644 index 0000000000000..a61ff65e39c69 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionspresentablegrouping.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsPresentableGrouping](./kibana-plugin-plugins-ui_actions-public.uiactionspresentablegrouping.md) + +## UiActionsPresentableGrouping type + +Signature: + +```typescript +export declare type PresentableGrouping = Array>; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice._constructor_.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice._constructor_.md new file mode 100644 index 0000000000000..ff272245dbbf9 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice._constructor_.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [(constructor)](./kibana-plugin-plugins-ui_actions-public.uiactionsservice._constructor_.md) + +## UiActionsService.(constructor) + +Constructs a new instance of the `UiActionsService` class + +Signature: + +```typescript +constructor({ triggers, actions, triggerToActions, }?: UiActionsServiceParams); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { triggers, actions, triggerToActions, } | UiActionsServiceParams | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md new file mode 100644 index 0000000000000..aaf4cebaf841c --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md) + +## UiActionsService.actions property + +Signature: + +```typescript +protected readonly actions: ActionRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md new file mode 100644 index 0000000000000..1782eef92442c --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) + +## UiActionsService.addTriggerAction property + +`addTriggerAction` is similar to `attachAction` as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet. + +`addTriggerAction` also infers better typing of the `action` argument. + +Signature: + +```typescript +readonly addTriggerAction: (triggerId: T, action: ActionDefinition | Action) => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md new file mode 100644 index 0000000000000..19f215a96b23b --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [attachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md) + +## UiActionsService.attachAction property + +Signature: + +```typescript +readonly attachAction: (triggerId: T, actionId: string) => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md new file mode 100644 index 0000000000000..024c7e3c3f85a --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [clear](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md) + +## UiActionsService.clear property + +Removes all registered triggers and actions. + +Signature: + +```typescript +readonly clear: () => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md new file mode 100644 index 0000000000000..a6ff2489c6f0e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [detachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md) + +## UiActionsService.detachAction property + +Signature: + +```typescript +readonly detachAction: (triggerId: TriggerId, actionId: string) => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md new file mode 100644 index 0000000000000..1bb6ca1115248 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [executeTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md) + +## UiActionsService.executeTriggerActions property + +> Warning: This API is now obsolete. +> +> Use `plugins.uiActions.getTrigger(triggerId).exec(params)` instead. +> + +Signature: + +```typescript +readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md new file mode 100644 index 0000000000000..06384cc110a59 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [executionService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md) + +## UiActionsService.executionService property + +Signature: + +```typescript +readonly executionService: UiActionsExecutionService; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md new file mode 100644 index 0000000000000..2b7a43a44cca6 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [fork](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md) + +## UiActionsService.fork property + +"Fork" a separate instance of `UiActionsService` that inherits all existing triggers and actions, but going forward all new triggers and actions added to this instance of `UiActionsService` are only available within this instance. + +Signature: + +```typescript +readonly fork: () => UiActionsService; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md new file mode 100644 index 0000000000000..0c4584a07b569 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) + +## UiActionsService.getAction property + +Signature: + +```typescript +readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md new file mode 100644 index 0000000000000..d44dc4e43a52e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [getTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md) + +## UiActionsService.getTrigger property + +Signature: + +```typescript +readonly getTrigger: (triggerId: T) => TriggerContract; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md new file mode 100644 index 0000000000000..c65a9a992da2e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) + +## UiActionsService.getTriggerActions property + +Signature: + +```typescript +readonly getTriggerActions: (triggerId: T) => Action[]; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md new file mode 100644 index 0000000000000..751abe332b08e --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) + +## UiActionsService.getTriggerCompatibleActions property + +Signature: + +```typescript +readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md new file mode 100644 index 0000000000000..2287cb3052864 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [hasAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md) + +## UiActionsService.hasAction property + +Signature: + +```typescript +readonly hasAction: (actionId: string) => boolean; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md new file mode 100644 index 0000000000000..c372eb113d682 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.md @@ -0,0 +1,41 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) + +## UiActionsService class + +Signature: + +```typescript +export declare class UiActionsService +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)({ triggers, actions, triggerToActions, })](./kibana-plugin-plugins-ui_actions-public.uiactionsservice._constructor_.md) | | Constructs a new instance of the UiActionsService class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.actions.md) | | ActionRegistry | | +| [addTriggerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.addtriggeraction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, action: ActionDefinition<TriggerContextMapping[T]> | Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">) => void | addTriggerAction is similar to attachAction as it attaches action to a trigger, but it also registers the action, if it has not been registered, yet.addTriggerAction also infers better typing of the action argument. | +| [attachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.attachaction.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, actionId: string) => void | | +| [clear](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.clear.md) | | () => void | Removes all registered triggers and actions. | +| [detachAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.detachaction.md) | | (triggerId: TriggerId, actionId: string) => void | | +| [executeTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executetriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContext<T>) => Promise<void> | | +| [executionService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.executionservice.md) | | UiActionsExecutionService | | +| [fork](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.fork.md) | | () => UiActionsService | "Fork" a separate instance of UiActionsService that inherits all existing triggers and actions, but going forward all new triggers and actions added to this instance of UiActionsService are only available within this instance. | +| [getAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.getaction.md) | | <T extends ActionDefinition<{}>>(id: string) => Action<ActionContext<T>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK"> | | +| [getTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettrigger.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => TriggerContract<T> | | +| [getTriggerActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggeractions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T) => Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">[] | | +| [getTriggerCompatibleActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.gettriggercompatibleactions.md) | | <T extends "" | "SELECT_RANGE_TRIGGER" | "VALUE_CLICK_TRIGGER" | "FILTER_TRIGGER" | "VISUALIZE_FIELD_TRIGGER" | "VISUALIZE_GEO_FIELD_TRIGGER" | "CONTEXT_MENU_TRIGGER" | "PANEL_BADGE_TRIGGER" | "PANEL_NOTIFICATION_TRIGGER">(triggerId: T, context: TriggerContextMapping[T]) => Promise<Action<TriggerContextMapping[T], "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">[]> | | +| [hasAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.hasaction.md) | | (actionId: string) => boolean | | +| [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) | | <A extends ActionDefinition<{}>>(definition: A) => Action<ActionContext<A>, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK"> | | +| [registerTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md) | | (trigger: Trigger) => void | | +| [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md) | | TriggerRegistry | | +| [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md) | | TriggerToActionsRegistry | | +| [unregisterAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.unregisteraction.md) | | (actionId: string) => void | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md new file mode 100644 index 0000000000000..c71e86fc09dc7 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [registerAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registeraction.md) + +## UiActionsService.registerAction property + +Signature: + +```typescript +readonly registerAction:
>(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md new file mode 100644 index 0000000000000..3002409c02304 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [registerTrigger](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.registertrigger.md) + +## UiActionsService.registerTrigger property + +Signature: + +```typescript +readonly registerTrigger: (trigger: Trigger) => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md new file mode 100644 index 0000000000000..07d480286e771 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggers.md) + +## UiActionsService.triggers property + +Signature: + +```typescript +protected readonly triggers: TriggerRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md new file mode 100644 index 0000000000000..1b79a1dd84593 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.triggertoactions.md) + +## UiActionsService.triggerToActions property + +Signature: + +```typescript +protected readonly triggerToActions: TriggerToActionsRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.unregisteraction.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.unregisteraction.md new file mode 100644 index 0000000000000..0e0eb971c1a7b --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsservice.unregisteraction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsService](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.md) > [unregisterAction](./kibana-plugin-plugins-ui_actions-public.uiactionsservice.unregisteraction.md) + +## UiActionsService.unregisterAction property + +Signature: + +```typescript +readonly unregisterAction: (actionId: string) => void; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.actions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.actions.md new file mode 100644 index 0000000000000..44d2957b0f8ba --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.actions.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsServiceParams](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md) > [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.actions.md) + +## UiActionsServiceParams.actions property + +Signature: + +```typescript +readonly actions?: ActionRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md new file mode 100644 index 0000000000000..756cd3de92ef8 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsServiceParams](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md) + +## UiActionsServiceParams interface + +Signature: + +```typescript +export interface UiActionsServiceParams +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [actions](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.actions.md) | ActionRegistry | | +| [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggers.md) | TriggerRegistry | | +| [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggertoactions.md) | TriggerToActionsRegistry | A 1-to-N mapping from Trigger to zero or more Action. | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggers.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggers.md new file mode 100644 index 0000000000000..061aa5eb68c5d --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsServiceParams](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md) > [triggers](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggers.md) + +## UiActionsServiceParams.triggers property + +Signature: + +```typescript +readonly triggers?: TriggerRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggertoactions.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggertoactions.md new file mode 100644 index 0000000000000..bdf1acba484e6 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggertoactions.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsServiceParams](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.md) > [triggerToActions](./kibana-plugin-plugins-ui_actions-public.uiactionsserviceparams.triggertoactions.md) + +## UiActionsServiceParams.triggerToActions property + +A 1-to-N mapping from `Trigger` to zero or more `Action`. + +Signature: + +```typescript +readonly triggerToActions?: TriggerToActionsRegistry; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionssetup.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionssetup.md new file mode 100644 index 0000000000000..d03d4cf9f1ee2 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionssetup.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsSetup](./kibana-plugin-plugins-ui_actions-public.uiactionssetup.md) + +## UiActionsSetup type + +Signature: + +```typescript +export declare type UiActionsSetup = Pick; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsstart.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsstart.md new file mode 100644 index 0000000000000..41f5bbf705e20 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsstart.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [UiActionsStart](./kibana-plugin-plugins-ui_actions-public.uiactionsstart.md) + +## UiActionsStart type + +Signature: + +```typescript +export declare type UiActionsStart = PublicMethodsOf; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md new file mode 100644 index 0000000000000..bd8d4dc50b8fd --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.value_click_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VALUE\_CLICK\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.value_click_trigger.md) + +## VALUE\_CLICK\_TRIGGER variable + +Signature: + +```typescript +VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md new file mode 100644 index 0000000000000..5c4fc284d83b1 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [valueClickTrigger](./kibana-plugin-plugins-ui_actions-public.valueclicktrigger.md) + +## valueClickTrigger variable + +Signature: + +```typescript +valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md new file mode 100644 index 0000000000000..c5d9f53557d6f --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VISUALIZE\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_field_trigger.md) + +## VISUALIZE\_FIELD\_TRIGGER variable + +Signature: + +```typescript +VISUALIZE_FIELD_TRIGGER = "VISUALIZE_FIELD_TRIGGER" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md new file mode 100644 index 0000000000000..a9396c1905485 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VISUALIZE\_GEO\_FIELD\_TRIGGER](./kibana-plugin-plugins-ui_actions-public.visualize_geo_field_trigger.md) + +## VISUALIZE\_GEO\_FIELD\_TRIGGER variable + +Signature: + +```typescript +VISUALIZE_GEO_FIELD_TRIGGER = "VISUALIZE_GEO_FIELD_TRIGGER" +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.contextualfields.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.contextualfields.md new file mode 100644 index 0000000000000..681d4127e4030 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.contextualfields.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VisualizeFieldContext](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md) > [contextualFields](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.contextualfields.md) + +## VisualizeFieldContext.contextualFields property + +Signature: + +```typescript +contextualFields?: string[]; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.fieldname.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.fieldname.md new file mode 100644 index 0000000000000..95f45b1fbee4a --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.fieldname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VisualizeFieldContext](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md) > [fieldName](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.fieldname.md) + +## VisualizeFieldContext.fieldName property + +Signature: + +```typescript +fieldName: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.indexpatternid.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.indexpatternid.md new file mode 100644 index 0000000000000..588c115cd9885 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.indexpatternid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VisualizeFieldContext](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md) > [indexPatternId](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.indexpatternid.md) + +## VisualizeFieldContext.indexPatternId property + +Signature: + +```typescript +indexPatternId: string; +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md new file mode 100644 index 0000000000000..7aeb254db7771 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [VisualizeFieldContext](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.md) + +## VisualizeFieldContext interface + +Signature: + +```typescript +export interface VisualizeFieldContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [contextualFields](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.contextualfields.md) | string[] | | +| [fieldName](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.fieldname.md) | string | | +| [indexPatternId](./kibana-plugin-plugins-ui_actions-public.visualizefieldcontext.indexpatternid.md) | string | | + diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md new file mode 100644 index 0000000000000..15510bd3eb4a3 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [visualizeFieldTrigger](./kibana-plugin-plugins-ui_actions-public.visualizefieldtrigger.md) + +## visualizeFieldTrigger variable + +Signature: + +```typescript +visualizeFieldTrigger: Trigger<'VISUALIZE_FIELD_TRIGGER'> +``` diff --git a/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizegeofieldtrigger.md b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizegeofieldtrigger.md new file mode 100644 index 0000000000000..faec6a69b71f9 --- /dev/null +++ b/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.visualizegeofieldtrigger.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-ui\_actions-public](./kibana-plugin-plugins-ui_actions-public.md) > [visualizeGeoFieldTrigger](./kibana-plugin-plugins-ui_actions-public.visualizegeofieldtrigger.md) + +## visualizeGeoFieldTrigger variable + +Signature: + +```typescript +visualizeGeoFieldTrigger: Trigger<'VISUALIZE_GEO_FIELD_TRIGGER'> +``` diff --git a/docs/discover/context.asciidoc b/docs/discover/context.asciidoc index 17ed78a163571..e26c91bfef075 100644 --- a/docs/discover/context.asciidoc +++ b/docs/discover/context.asciidoc @@ -16,7 +16,7 @@ The anchor document is highlighted in blue. [role="screenshot"] -image::images/Discover-ContextView.png[Context View] +image::images/Discover-ContextView.png[Image showing context view feature, with anchor documents highlighted in blue] [float] [[filter-context]] diff --git a/docs/discover/document-data.asciidoc b/docs/discover/document-data.asciidoc index ee130e8405483..dd245e4b4558f 100644 --- a/docs/discover/document-data.asciidoc +++ b/docs/discover/document-data.asciidoc @@ -44,7 +44,7 @@ immediately before and after your event. share the link for direct access to a particular document. [role="screenshot"] -image::images/Expanded-Document.png[] +image::images/Expanded-Document.png[Image showing expanded view, with JSON and table viewing options] [float] diff --git a/docs/discover/field-filter.asciidoc b/docs/discover/field-filter.asciidoc index 949cab2c2f976..0c521b401e4b8 100644 --- a/docs/discover/field-filter.asciidoc +++ b/docs/discover/field-filter.asciidoc @@ -19,7 +19,7 @@ the field, the top 5 values for the field, and the percentage of documents that contain each value. + [role="screenshot"] -image::images/filter-field.png[height=317] +image::images/filter-field.png[Picture showing top 5 values for each field, and correspnding percentage of documents that contain each value] . Use the image:images/PositiveFilter.jpg[Positive Filter] icon to show only documents that contain that value, diff --git a/docs/discover/images/kql-autocomplete.png b/docs/discover/images/kql-autocomplete.png new file mode 100644 index 0000000000000..60e2290c0bad7 Binary files /dev/null and b/docs/discover/images/kql-autocomplete.png differ diff --git a/docs/discover/kuery.asciidoc b/docs/discover/kuery.asciidoc index 1a481c46b3816..f306f2b8f763f 100644 --- a/docs/discover/kuery.asciidoc +++ b/docs/discover/kuery.asciidoc @@ -1,90 +1,170 @@ [[kuery-query]] === Kibana Query Language -In Kibana 6.3, we introduced a number of exciting experimental query language enhancements. These -features are now available by default in 7.0. Out of the box, Kibana's query language now includes scripted field support and a -simplified, easier to use syntax. If you have a Basic license or above, autocomplete functionality will also be enabled. +The Kibana Query Language (KQL) makes it easy to find +the fields and syntax for your {es} query. If you have the +https://www.elastic.co/subscriptions[Basic tier] or above, +simply place your cursor in the *Search* field. As you type, you’ll get suggestions for fields, +values, and operators. -==== Language syntax +[role="screenshot"] +image::images/kql-autocomplete.png[Autocomplete in Search bar] -If you're familiar with Kibana's old Lucene query syntax, you should feel right at home with the new syntax. The basics -stay the same, we've simply refined things to make the query language easier to use. +If you prefer to use Kibana’s legacy query language, based on the +<>, click *KQL* next to the *Search* field, and then turn off KQL. -`response:200` will match documents where the response field matches the value 200. +[discrete] +=== Terms query -Quotes around a search term will initiate a phrase search. For example, `message:"Quick brown fox"` will search -for the phrase "quick brown fox" in the message field. Without the quotes, your query will get broken down into tokens via -the message field's configured analyzer and will match documents that contain those tokens, regardless of the order in which -they appear. This means documents with "quick brown fox" will match, but so will "quick fox brown". Remember to use quotes if you want -to search for a phrase. +A terms query matches documents that contain one or more *exact* terms in a field. -The query parser will no longer split on whitespace. Multiple search terms must be separated by explicit -boolean operators. Lucene will combine search terms with an `or` by default, so `response:200 extension:php` would -become `response:200 or extension:php` in KQL. This will match documents where response matches 200, extension matches php, or both. -Note that boolean operators are not case sensitive. +To match documents where the response field is `200`: -We can make terms required by using `and`. +[source,yaml] +------------------- +response:200 +------------------- -`response:200 and extension:php` will match documents where response matches 200 and extension matches php. +To match documents with the phrase "quick brown fox" in the `message` field. -By default, `and` has a higher precedence than `or`. +[source,yaml] +------------------- +message:"quick brown fox" +------------------- -`response:200 and extension:php or extension:css` will match documents where response is 200 and extension is php OR documents where extension is css and response is anything. +Without the quotes, +the query matches documents regardless of the order in which +they appear. Documents with "quick brown fox" match, +and so does "quick fox brown". -We can override the default precedence with grouping. +NOTE: Terms without fields are matched against the default field in your index settings. +If a default field is not +set, terms are matched against all fields. For example, a query +for `response:200` searches for the value 200 +in the response field, but a query for just `200` searches for 200 +across all fields in your index. -`response:200 and (extension:php or extension:css)` will match documents where response is 200 and extension is either php or css. -A shorthand exists that allows us to easily search a single field for multiple values. +[discrete] +=== Boolean queries -`response:(200 or 404)` searches for docs where the `response` field matches 200 or 404. We can also search for docs -with multi-value fields that contain a list of terms, for example: `tags:(success and info and security)` +KQL supports `or`, `and`, and `not`. By default, `and` has a higher precedence than `or`. +To override the default precedence, group operators in parentheses. -Terms can be inverted by prefixing them with `not`. +To match documents where response is `200`, extension is `php`, or both: -`not response:200` will match all documents where response is not 200. +[source,yaml] +------------------- +response:200 or extension:php +------------------- -Entire groups can also be inverted. +To match documents where response is `200` and extension is `php`: -`response:200 and not (extension:php or extension:css)` +[source,yaml] +------------------- +response:200 and extension:php +------------------- -Ranges are similar to lucene with a small syntactical difference. +To match documents where response is `200` or `404`. -Instead of `bytes:>1000`, we omit the colon: `bytes > 1000`. +[source,yaml] +------------------- +response:(200 or 404) +------------------- -`>, >=, <, <=` are all valid range operators. +To match documents where response is `200` and extension is either `php` or `css`: -Exist queries are simple and do not require a special operator. `response:*` will find all docs where the response -field exists. +[source,yaml] +------------------- +response:200 and (extension:php or extension:css) +------------------- -Wildcard queries are available. `machine.os:win*` would match docs where the machine.os field starts with "win", which -would match values like "windows 7" and "windows 10". +To match documents where `response` is 200 and `extension` is +`php` or extension is `css`, and response is anything: -Wildcards also allow us to search multiple fields at once. This can come in handy when you have both `text` and `keyword` -versions of a field. Let's say we have `machine.os` and `machine.os.keyword` fields and we want to check both for the term -"windows 10". We can do it like this: `machine.os*:windows 10". +[source,yaml] +------------------- +response:200 and extension:php or extension:css +------------------- +To match documents where response is not `200`: -[NOTE] -============ -Terms without fields will be matched against the default field in your index settings. If a default field is not -set these terms will be matched against all fields. For example, a query for `response:200` will search for the value 200 -in the response field, but a query for just `200` will search for 200 across all fields in your index. -============ +[source,yaml] +------------------- +not response:200 +------------------- -==== Nested field support +To match documents where response is `200` but extension is not `php` or `css`. -KQL supports querying on {ref}/nested.html[nested fields] through a special syntax. You can query nested fields in subtly different -ways, depending on the results you want, so crafting nested queries requires extra thought. +[source,yaml] +------------------- +response:200 and not (extension:php or extension:css) +------------------- -One main consideration is how to match parts of the nested query to the individual nested documents. -There are two main approaches to take: +To match multi-value fields that contain a list of terms: -* *Parts of the query may only match a single nested document.* This is what most users want when querying on a nested field. -* *Parts of the query can match different nested documents.* This is how a regular object field works. - Although generally less useful, there might be occasions where you want to query a nested field in this way. +[source,yaml] +------------------- +tags:(success and info and security) +------------------- -Let's take a look at the first approach. In the following document, `items` is a nested field. Each document in the nested +[discrete] +=== Range queries + +KQL supports `>`, `>=`, `<`, and `<=`. For example: + +[source,yaml] +------------------- +account_number:>=100 and items_sold:<=200 +------------------- + +[discrete] +=== Exist queries + +An exist query matches documents that contain a value for a field, in this case, +response: + +[source,yaml] +------------------- +response:* +------------------- + +[discrete] +=== Wildcard queries + +To match documents where machine.os starts with `win`, such +as "windows 7" and "windows 10": + +[source,yaml] +------------------- +machine.os:win* +------------------- + +To match multiple fields: + +[source,yaml] +------------------- +machine.os*:windows 10 +------------------- + +This sytax is handy when you have text and keyword +versions of a field. The query checks machine.os and machine.os.keyword +for the term +`windows 10`. + + +[discrete] +=== Nested field queries + +A main consideration for querying {ref}/nested.html[nested fields] is how to +match parts of the nested query to the individual nested documents. +You can: + +* *Match parts of the query to a single nested document only.* This is what most users want when querying on a nested field. +* *Match parts of the query to different nested documents.* This is how a regular object field works. + This query is generally less useful than matching to a single document. + +In the following document, `items` is a nested field. Each document in the nested field contains a name, stock, and category. [source,json] @@ -116,40 +196,61 @@ field contains a name, stock, and category. } ---------------------------------- -===== Match a single nested document +[discrete] +==== Match a single document -To find stores that have more than 10 bananas in stock, you would write a query like this: +To match stores that have more than 10 bananas in stock: -`items:{ name:banana and stock > 10 }` +[source,yaml] +------------------- +items:{ name:banana and stock > 10 } +------------------- -`items` is the "nested path". Everything inside the curly braces (the "nested group") must match a single nested document. +`items` is the nested path. Everything inside the curly braces (the nested group) +must match a single nested document. -The following example returns no matches because no single nested document has bananas with a stock of 9. +The following query does not return any matches because no single nested +document has bananas with a stock of 9. -`items:{ name:banana and stock:9 }` +[source,yaml] +------------------- +items:{ name:banana and stock:9 } +------------------- -==== Match different nested documents +[discrete] +==== Match different documents -The subqueries in this example are in separate nested groups and can match different nested documents. +The following subqueries are in separate nested groups +and can match different nested documents: -`items:{ name:banana } and items:{ stock:9 }` +[source,yaml] +------------------- +items:{ name:banana } and items:{ stock:9 } +------------------- -`name:banana` matches the first document in the array and `stock:9` matches the third document in the array. +`name:banana` matches the first document in the array and `stock:9` +matches the third document in the array. -==== Combine approaches +[discrete] +==== Match single and different documents -You can combine these two approaches to create complex queries. What if you wanted to find a store with more than 10 -bananas that *also* stocks vegetables? You could do this: +To find a store with more than 10 +bananas that *also* stocks vegetables: -`items:{ name:banana and stock > 10 } and items:{ category:vegetable }` +[source,yaml] +------------------- +items:{ name:banana and stock > 10 } and items:{ category:vegetable } +------------------- -The first nested group (`name:banana and stock > 10`) must still match a single document, but the `category:vegetables` +The first nested group (`name:banana and stock > 10`) must match a single document, but the `category:vegetables` subquery can match a different nested document because it is in a separate group. +[discrete] ==== Nested fields inside other nested fields -KQL's syntax also supports nested fields inside of other nested fields—you simply have to specify the full path. Suppose you -have a document where `level1` and `level2` are both nested fields: +KQL supports nested fields inside other nested fields—you have to +specify the full path. In this document, +`level1` and `level2` are nested fields: [source,json] ---------------------------------- @@ -171,6 +272,9 @@ have a document where `level1` and `level2` are both nested fields: } ---------------------------------- -You can match on a single nested document by specifying the full path: +To match on a single nested document: -`level1.level2:{ prop1:foo and prop2:bar }` +[source,yaml] +------------------- +level1.level2:{ prop1:foo and prop2:bar } +------------------- diff --git a/docs/discover/search.asciidoc b/docs/discover/search.asciidoc index da58382deb89a..ee1e1526f9d6f 100644 --- a/docs/discover/search.asciidoc +++ b/docs/discover/search.asciidoc @@ -28,7 +28,7 @@ configure a refresh interval to periodically resubmit your searches to retrieve the latest results. [role="screenshot"] -image::images/autorefresh-interval.png[] +image::images/autorefresh-interval.png[Image showing what refresh interval option looks like. The configurable time interval is located in the dropdown] You can also manually refresh the search results by clicking the *Refresh* button. diff --git a/docs/discover/set-time-filter.asciidoc b/docs/discover/set-time-filter.asciidoc index a5b81b0fa461c..93fdf9ffd695a 100644 --- a/docs/discover/set-time-filter.asciidoc +++ b/docs/discover/set-time-filter.asciidoc @@ -14,7 +14,7 @@ range in the histogram. Use the time filter to change the time range. By default, the time filter is set to the last 15 minutes. -. Click image:images/time-filter-calendar.png[]. +. Click image:images/time-filter-calendar.png[Calendar icon]. . Choose one of the following: @@ -53,4 +53,4 @@ when you hover over a valid start point. * Click the dropdown, then select an interval. [role="screenshot"] -image::images/Histogram-Time.png[Time range selector in Histogram] +image::images/Histogram-Time.png[Time range selector in Histogram dropdown] diff --git a/docs/discover/viewing-field-stats.asciidoc b/docs/discover/viewing-field-stats.asciidoc index 5ada5839fd344..5c46177347530 100644 --- a/docs/discover/viewing-field-stats.asciidoc +++ b/docs/discover/viewing-field-stats.asciidoc @@ -11,4 +11,4 @@ they are available in the side bar if you uncheck "Hide missing fields". To view field data statistics, click the name of a field in the fields list. -image:images/filter-field.png[Field Statistics,height=317] +image:images/filter-field.png[Fields list that displays the top five search results] diff --git a/docs/getting-started/tutorial-define-index.asciidoc b/docs/getting-started/tutorial-define-index.asciidoc index fbe7450683dbc..215952c2d3595 100644 --- a/docs/getting-started/tutorial-define-index.asciidoc +++ b/docs/getting-started/tutorial-define-index.asciidoc @@ -24,7 +24,7 @@ index named `shakespeare,` and the accounts data set, which has an index named . In the *Index pattern name* field, enter `shakes*`. + [role="screenshot"] -image::images/tutorial-pattern-1.png[shakes* index patterns] +image::images/tutorial-pattern-1.png[Image showing how to enter shakes* in Index Pattern Name field] . Click *Next step*. @@ -45,7 +45,12 @@ contains the time series data. . From the *Time field* dropdown, select *@timestamp, then click *Create index pattern*. + [role="screenshot"] -image::images/tutorial_index_patterns.png[All tutorial index patterns] +image::images/tutorial_index_patterns.png[Image showing how to create an index pattern] +NOTE: When you define an index pattern, the indices that match that pattern must +exist in Elasticsearch and they must contain data. To check if the indices are +available, open the menu, go to *Dev Tools > Console*, then enter `GET _cat/indices`. Alternately, use +`curl -XGET "http://localhost:9200/_cat/indices"`. +For Windows, run `Invoke-RestMethod -Uri "http://localhost:9200/_cat/indices"` in Powershell. diff --git a/docs/getting-started/tutorial-discovering.asciidoc b/docs/getting-started/tutorial-discovering.asciidoc index ec07a74b8ac0d..99a07acf98791 100644 --- a/docs/getting-started/tutorial-discovering.asciidoc +++ b/docs/getting-started/tutorial-discovering.asciidoc @@ -21,7 +21,7 @@ The search returns all account numbers between zero and 99 with balances in excess of 47,500. Results appear for account numbers 8, 32, 78, 85, and 97. + [role="screenshot"] -image::images/tutorial-discover-2.png[] +image::images/tutorial-discover-2.png[Image showing the search results for account numbers between zero and 99, with balances in excess of 47,500] + . Hover over the list of *Available fields*, then click *Add* next to each field you want include in the table. @@ -30,6 +30,6 @@ For example, when you add the `account_number` field, the display changes to a l account numbers. + [role="screenshot"] -image::images/tutorial-discover-3.png[] +image::images/tutorial-discover-3.png[Image showing a dropdown with five account numbers, which match the previous query for account balance] Now that you know what your documents contain, it's time to gain insight into your data with visualizations. diff --git a/docs/getting-started/tutorial-full-experience.asciidoc b/docs/getting-started/tutorial-full-experience.asciidoc index 1e6fe39dbd013..a7d5412ae0632 100644 --- a/docs/getting-started/tutorial-full-experience.asciidoc +++ b/docs/getting-started/tutorial-full-experience.asciidoc @@ -25,7 +25,14 @@ curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/shakespeare. curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/accounts.zip curl -O https://download.elastic.co/demos/kibana/gettingstarted/8.x/logs.jsonl.gz -Two of the data sets are compressed. To extract the files, use the following commands: +Alternatively, for Windows users, run the following commands in Powershell: + +[source,shell] +Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/shakespeare.json -OutFile shakespeare.json +Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/accounts.zip -OutFile accounts.zip +Invoke-RestMethod https://download.elastic.co/demos/kibana/gettingstarted/8.x/logs.jsonl.gz -OutFile logs.jsonl.gz + +Two of the data sets are compressed. To extract the files, use these commands: [source,shell] unzip accounts.zip diff --git a/docs/getting-started/tutorial-visualizing.asciidoc b/docs/getting-started/tutorial-visualizing.asciidoc index 33a7035160247..a53c8cb6bc23d 100644 --- a/docs/getting-started/tutorial-visualizing.asciidoc +++ b/docs/getting-started/tutorial-visualizing.asciidoc @@ -16,7 +16,7 @@ To visualize the Shakespeare data and compare the number of speaking parts in th . Click *Create new*, then click *Lens* on the *New Visualization* window. + [role="screenshot"] -image::images/tutorial-visualize-wizard-step-1.png[Bar chart] +image::images/tutorial-visualize-wizard-step-1.png[Image showing different options for your new visualization] . Make sure the index pattern is *shakes*. @@ -39,7 +39,7 @@ image::images/tutorial-visualize-wizard-step-1.png[Bar chart] .. In the *Label* field, enter `Speaking Parts`. + [role="screenshot"] -image::images/tutorial-visualize-bar-1.5.png[Bar chart] +image::images/tutorial-visualize-bar-1.5.png[Bar chart showing the speaking parts data] . *Save* the chart with the name `Bar Example`. + @@ -85,7 +85,7 @@ Since the default search matches all documents, the pie contains a single slice. The pie chart displays the proportion of the 1,000 accounts that fall into each of the ranges. + [role="screenshot"] -image::images/tutorial-visualize-pie-2.png[Pie chart] +image::images/tutorial-visualize-pie-2.png[Pie chart displaying accounts that fall into each of the ranges, scaled to 1000 accounts] . Add another bucket aggregation that displays the ages of the account holders. @@ -99,7 +99,7 @@ The break down of the ages of the account holders are displayed in a ring around the balance ranges. + [role="screenshot"] -image::images/tutorial-visualize-pie-3.png[Final pie chart] +image::images/tutorial-visualize-pie-3.png[Final pie chart showing all of the changes] . Click *Save*, then enter `Pie Example` in the *Title* field. @@ -119,7 +119,7 @@ To visualize geographic information in the log file data, use <>. .. Set the *End date* to `May 20, 2015 @ 12:00:00.000`. + [role="screenshot"] -image::images/gs_maps_time_filter.png[Time filter for Maps tutorial] +image::images/gs_maps_time_filter.png[Image showing the time filter for Maps tutorial] .. Click *Update* @@ -140,7 +140,7 @@ image::images/gs_maps_time_filter.png[Time filter for Maps tutorial] .. From the *Border color* dropdown, select *#FFF*, then click *Save & close*. + [role="screenshot"] -image::images/tutorial-visualize-map-2.png[Map] +image::images/tutorial-visualize-map-2.png[Example of a map visualization] . Click *Save*, then enter `Map Example` in the *Title* field. @@ -170,12 +170,12 @@ The Markdown widget uses **markdown** syntax. The Markdown renders in the preview pane. + [role="screenshot"] -image::images/tutorial-visualize-md-2.png[] +image::images/tutorial-visualize-md-2.png[Image showing example markdown editing field] . Click *Save*, then enter `Markdown Example` in the *Title* field. [role="screenshot"] -image::images/tutorial-dashboard.png[] +image::images/tutorial-dashboard.png[Final visualization with bar chart, pie chart, map, and markdown text field] [float] === Next steps diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index ed20166c87f29..1bae04cc2e58b 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -1,9 +1,10 @@ [[advanced-options]] == Advanced Settings -The *Advanced Settings* UI enables you to edit settings that control the behavior of Kibana. -For example, you can change the format used to display dates, specify the default index pattern, and set the precision -for displayed decimal values. +The *Advanced Settings* UI enables you to edit settings that control the +behavior of Kibana. For example, you can change the format used to display dates, +specify the default index pattern, and set the precision for displayed decimal +values. . Open the menu, then go to *Stack Management > {kib} > Advanced Settings*. . Scroll or search for the setting you want to modify. @@ -15,8 +16,9 @@ for displayed decimal values. [[settings-read-only-access]] === [xpack]#Read only access# When you have insufficient privileges to edit advanced settings, the following -indicator in Kibana will be displayed. The buttons to edit settings won't be visible. -For more information on granting access to Kibana see <>. +indicator in Kibana will be displayed. The buttons to edit settings won't be +visible. For more information on granting access to Kibana, see +<>. [role="screenshot"] image::images/settings-read-only-badge.png[Example of Advanced Settings Management's read only access indicator in Kibana's header] @@ -25,12 +27,11 @@ image::images/settings-read-only-badge.png[Example of Advanced Settings Manageme [[kibana-settings-reference]] === Kibana settings reference -WARNING: Modifying a setting can affect {kib} -performance and cause problems that are -difficult to diagnose. Setting a property value to a blank field reverts -to the default behavior, which might not be -compatible with other configuration settings. Deleting a custom setting -removes it from {kib} permanently. +WARNING: Modifying a setting can affect {kib} performance and cause problems +that are difficult to diagnose. Setting a property value to a blank field +reverts to the default behavior, which might not be compatible with other +configuration settings. Deleting a custom setting removes it from {kib} +permanently. [float] @@ -38,72 +39,159 @@ removes it from {kib} permanently. ==== General [horizontal] -`csv:quoteValues`:: Set this property to `true` to quote exported values. -`csv:separator`:: A string that serves as the separator for exported values. -`dateFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates]. -`dateFormat:dow`:: The day that a week should start on. -`dateFormat:scaled`:: The values that define the format to use to render ordered time-based data. Formatted timestamps must -adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals]. -`dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser. -`dateNanosFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of {ref}/date_nanos.html[Elasticsearch date_nanos type]. -`defaultIndex`:: The index to access if no index is set. The default is `null`. -`defaultRoute`:: The default route when opening Kibana. Use this setting to route users to a specific dashboard, application, or saved object as they enter each space. -`fields:popularLimit`:: The top N most popular fields to show. -`filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. -`filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by default. -`format:bytes:defaultPattern`:: The default <> format for the "bytes" format. -`format:currency:defaultPattern`:: The default <> format for the "currency" format. -`format:defaultTypeMap`:: A map of the default format name for each field type. Field types that are not explicitly -mentioned use "\_default_". -`format:number:defaultLocale`:: The <> locale. -`format:number:defaultPattern`:: The <> for the "number" format. -`format:percent:defaultPattern`:: The <> for the "percent" format. -`histogram:barTarget`:: When date histograms use the `auto` interval, Kibana attempts to generate this number of bars. -`histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values -when necessary. -`history:limit`:: In fields that have history, such as query inputs, show this many recent values. -`indexPattern:placeholder`:: The default placeholder value to use in Management > Index Patterns > Create Index Pattern. -`metaFields`:: Fields that exist outside of `_source`. Kibana merges these fields -into the document when displaying it. -`metrics:max_buckets`:: The maximum numbers of buckets that a single -data source can return. This might arise when the user selects a -short interval (for example, 1s) for a long time period (1 year). -`pageNavigation`:: The style of navigation menu for Kibana. -Choices are Legacy, the legacy style where every plugin is represented in the nav, -and Modern, a new format that bundles related plugins together in flyaway nested navigation. -`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character -in a query clause. Only applies when experimental query features are -enabled in the query bar. To disallow leading wildcards in Lucene queries, -use `query:queryString:options`. -`query:queryString:options`:: Options for the Lucene query string parser. Only -used when "Query language" is set to Lucene. -`savedObjects:listingLimit`:: The number of objects to fetch for lists of saved objects. -The default value is 1000. Do not set above 10000. -`savedObjects:perPage`:: The number of objects to show on each page of the -list of saved objects. The default is 5. -`search:queryLanguage`:: The query language to use in the query bar. -Choices are <>, a language built specifically for {kib}, and the <>. -`shortDots:enable`:: Set this property to `true` to shorten long -field names in visualizations. For example, show `f.b.baz` instead of `foo.bar.baz`. -`sort:options`:: Options for the Elasticsearch {ref}/search-request-body.html#request-body-search-sort[sort] parameter. -`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the -URL, which can lead to problems when there is a lot of state information, -and the URL gets very long. -Enabling this setting stores part of the URL in your browser session to keep the -URL short. -`theme:darkMode`:: Set to `true` to enable a dark mode for the {kib} UI. You must -refresh the page to apply the setting. -`timepicker:quickRanges`:: The list of ranges to show in the Quick section of -the time filter. This should be an array of objects, with each object containing -`from`, `to` (see {ref}/common-options.html#date-math[accepted formats]), -and `display` (the title to be displayed). -`timepicker:refreshIntervalDefaults`:: The default refresh interval for the time filter. Example: `{ "display": "15 seconds", "pause": true, "value": 15000 }`. -`timepicker:timeDefaults`:: The default selection in the time filter. -`truncate:maxHeight`:: The maximum height that a cell occupies in a table. Set to 0 to disable +[[csv-quotevalues]]`csv:quoteValues`:: +Set this property to `true` to quote exported values. + +[[csv-separator]]`csv:separator`:: +A string that serves as the separator for exported values. + +[[dateformat]]`dateFormat`:: +The format to use for displaying +https://momentjs.com/docs/#/displaying/format/[pretty formatted dates]. + +[[dateformat-dow]]`dateFormat:dow`:: +The day that a week should start on. + +[[dateformat-scaled]]`dateFormat:scaled`:: +The values that define the format to use to render ordered time-based data. +Formatted timestamps must adapt to the interval between measurements. Keys are +http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals]. + +[[dateformat-tz]]`dateFormat:tz`:: +The timezone that Kibana uses. The default value of `Browser` uses the timezone +detected by the browser. + +[[datenanosformat]]`dateNanosFormat`:: +The format to use for displaying +https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of +{ref}/date_nanos.html[Elasticsearch date_nanos type]. + +[[defaultindex]]`defaultIndex`:: +The index to access if no index is set. The default is `null`. + +[[defaultroute]]`defaultRoute`:: +The default route when opening Kibana. Use this setting to route users to a +specific dashboard, application, or saved object as they enter each space. + +[[fields-popularlimit]]`fields:popularLimit`:: +The top N most popular fields to show. + +[[filtereditor-suggestvalues]]`filterEditor:suggestValues`:: +Set this property to `false` to prevent the filter editor from suggesting values +for fields. + +[[filters-pinnedbydefault]]`filters:pinnedByDefault`:: +Set this property to `true` to make filters have a global state (be pinned) by +default. + +[[format-bytes-defaultpattern]]`format:bytes:defaultPattern`:: +The default <> format for the "bytes" format. + +[[format-currency-defaultpattern]]`format:currency:defaultPattern`:: +The default <> format for the "currency" format. + +[[format-defaulttypemap]]`format:defaultTypeMap`:: +A map of the default format name for each field type. Field types that are not +explicitly mentioned use "\_default_". + +[[format-number-defaultlocale]]`format:number:defaultLocale`:: +The <> locale. + +[[format-number-defaultpattern]]`format:number:defaultPattern`:: +The <> for the "number" format. + +[[format-percent-defaultpattern]]`format:percent:defaultPattern`:: +The <> for the "percent" format. + +[[histogram-bartarget]]`histogram:barTarget`:: +When date histograms use the `auto` interval, Kibana attempts to generate this +number of bars. + +[[histogram-maxbars]]`histogram:maxBars`:: +Date histograms are not generated with more bars than the value of this property, +scaling values when necessary. + +[[history-limit]]`history:limit`:: +In fields that have history, such as query inputs, show this many recent values. + +[[indexpattern-placeholder]]`indexPattern:placeholder`:: +The default placeholder value to use in +*Management > Index Patterns > Create Index Pattern*. + +[[metafields]]`metaFields`:: +Fields that exist outside of `_source`. Kibana merges these fields into the +document when displaying it. + +[[metrics-maxbuckets]]`metrics:max_buckets`:: +The maximum numbers of buckets that a single data source can return. This might +arise when the user selects a short interval (for example, 1s) for a long time +period (1 year). + +[[pagenavigation]]`pageNavigation`:: +The style of navigation menu for Kibana. Choices are Legacy, the legacy style +where every plugin is represented in the nav, and Modern, a new format that +bundles related plugins together in flyaway nested navigation. + +[[query-allowleadingwildcards]]`query:allowLeadingWildcards`:: +Allows a wildcard (*) as the first character in a query clause. Only applies +when experimental query features are enabled in the query bar. To disallow +leading wildcards in Lucene queries, use `query:queryString:options`. + +[[query-querystring-options]]`query:queryString:options`:: +Options for the Lucene query string parser. Only used when "Query language" is +set to Lucene. + +[[savedobjects-listinglimit]]`savedObjects:listingLimit`:: +The number of objects to fetch for lists of saved objects. The default value +is 1000. Do not set above 10000. + +[[savedobjects-perpage]]`savedObjects:perPage`:: +The number of objects to show on each page of the list of saved objects. The +default is 5. + +[[search-querylanguage]]`search:queryLanguage`:: +The query language to use in the query bar. Choices are <>, a +language built specifically for {kib}, and the +<>. + +[[shortdots-enable]]`shortDots:enable`:: +Set this property to `true` to shorten long field names in visualizations. For +example, show `f.b.baz` instead of `foo.bar.baz`. + +[[sort-options]]`sort:options`:: Options for the Elasticsearch +{ref}/search-request-body.html#request-body-search-sort[sort] parameter. + +[[state-storeinsessionstorage]]`state:storeInSessionStorage`:: +experimental:[] +Kibana tracks UI state in the URL, which can lead to problems +when there is a lot of state information, and the URL gets very long. Enabling +this setting stores part of the URL in your browser session to keep the URL +short. + +[[theme-darkmode]]`theme:darkMode`:: +Set to `true` to enable a dark mode for the {kib} UI. You must refresh the page +to apply the setting. + +[[timepicker-quickranges]]`timepicker:quickRanges`:: +The list of ranges to show in the Quick section of the time filter. This should +be an array of objects, with each object containing `from`, `to` (see +{ref}/common-options.html#date-math[accepted formats]), and `display` (the title +to be displayed). + +[[timepicker-refreshintervaldefaults]]`timepicker:refreshIntervalDefaults`:: +The default refresh interval for the time filter. Example: +`{ "display": "15 seconds", "pause": true, "value": 15000 }`. + +[[timepicker-timedefaults]]`timepicker:timeDefaults`:: +The default selection in the time filter. + +[[truncate-maxheight]]`truncate:maxHeight`:: +The maximum height that a cell occupies in a table. Set to 0 to disable truncation. -`xPack:defaultAdminEmail`:: Email address for X-Pack admin operations, such as -cluster alert notifications from Monitoring. + +[[xpack-defaultadminemail]]`xPack:defaultAdminEmail`:: +Email address for {xpack} admin operations, such as cluster alert notifications +from *{stack-monitor-app}*. [float] @@ -111,15 +199,17 @@ cluster alert notifications from Monitoring. ==== Accessibility [horizontal] -`accessibility:disableAnimations`:: Turns off all unnecessary animations in the -{kib} UI. Refresh the page to apply the changes. +[[accessibility-disableanimations]]`accessibility:disableAnimations`:: +Turns off all unnecessary animations in the {kib} UI. Refresh the page to apply +the changes. [float] [[kibana-dashboard-settings]] ==== Dashboard [horizontal] -`xpackDashboardMode:roles`:: **Deprecated. Use <> instead.** +[[xpackdashboardmode-roles]]`xpackDashboardMode:roles`:: +**Deprecated. Use <> instead.** The roles that belong to <>. [float] @@ -127,38 +217,63 @@ The roles that belong to <>. ==== Discover [horizontal] -`context:defaultSize`:: The number of surrounding entries to display in the context view. The default value is 5. -`context:step`:: The number by which to increment or decrement the context size. The default value is 5. -`context:tieBreakerFields`:: A comma-separated list of fields to use -for breaking a tie between documents that have the same timestamp value. The first -field that is present and sortable in the current index pattern is used. -`defaultColumns`:: The columns that appear by default on the Discover page. -The default is `_source`. -`discover:aggs:terms:size`:: The number terms that are visualized when clicking -the Visualize button in the field drop down. The default is `20`. -`discover:sampleSize`:: The number of rows to show in the Discover table. -`discover:sort:defaultOrder`:: The default sort direction for time-based index patterns. -`discover:searchOnPageLoad`:: Controls whether a search is executed when Discover first loads. -This setting does not have an effect when loading a saved search. -`doc_table:hideTimeColumn`:: Hides the "Time" column in Discover and in all saved searches on dashboards. -`doc_table:highlight`:: Highlights results in Discover and saved searches on dashboards. -Highlighting slows requests when -working on big documents. +[[context-defaultsize]]`context:defaultSize`:: +The number of surrounding entries to display in the context view. The default +value is 5. + +[[context-step]]`context:step`:: +The number by which to increment or decrement the context size. The default +value is 5. + +[[context-tiebreakerfields]]`context:tieBreakerFields`:: +A comma-separated list of fields to use for breaking a tie between documents +that have the same timestamp value. The first field that is present and sortable +in the current index pattern is used. + +[[defaultcolumns]]`defaultColumns`:: +The columns that appear by default on the *Discover* page. The default is +`_source`. + +[[discover-aggs-terms-size]]`discover:aggs:terms:size`:: +The number terms that are visualized when clicking the *Visualize* button in the +field drop down. The default is `20`. + +[[discover-samplesize]]`discover:sampleSize`:: +The number of rows to show in the *Discover* table. + +[[discover-sort-defaultorder]]`discover:sort:defaultOrder`:: +The default sort direction for time-based index patterns. + +[[discover-searchonpageload]]`discover:searchOnPageLoad`:: +Controls whether a search is executed when *Discover* first loads. This setting +does not have an effect when loading a saved search. + +[[doctable-hidetimecolumn]]`doc_table:hideTimeColumn`:: +Hides the "Time" column in *Discover* and in all saved searches on dashboards. + +[[doctable-highlight]]`doc_table:highlight`:: +Highlights results in *Discover* and saved searches on dashboards. Highlighting +slows requests when working on big documents. [float] [[kibana-ml-settings]] ==== Machine learning [horizontal] -`ml:anomalyDetection:results:enableTimeDefaults`:: Use the default time filter -in the *Single Metric Viewer* and *Anomaly Explorer*. If this setting is -disabled, the results for the full time range are shown. -`ml:anomalyDetection:results:timeDefaults`:: Sets the default time filter for -viewing {anomaly-job} results. This setting must contain `from` and `to` values (see {ref}/common-options.html#date-math[accepted formats]). It is ignored -unless `ml:anomalyDetection:results:enableTimeDefaults` is enabled. -`ml:fileDataVisualizerMaxFileSize`:: Sets the file size limit when importing -data in the {data-viz}. The default value is `100MB`. The highest supported -value for this setting is `1GB`. +[[ml-anomalydetection-results-enabletimedefaults]]`ml:anomalyDetection:results:enableTimeDefaults`:: +Use the default time filter in the *Single Metric Viewer* and +*Anomaly Explorer*. If this setting is disabled, the results for the full time +range are shown. + +[[ml-anomalydetection-results-timedefaults]]`ml:anomalyDetection:results:timeDefaults`:: +Sets the default time filter for viewing {anomaly-job} results. This setting +must contain `from` and `to` values (see +{ref}/common-options.html#date-math[accepted formats]). It is ignored unless +`ml:anomalyDetection:results:enableTimeDefaults` is enabled. + +[[ml-filedatavisualizermaxfilesize]]`ml:fileDataVisualizerMaxFileSize`:: +Sets the file size limit when importing data in the {data-viz}. The default +value is `100MB`. The highest supported value for this setting is `1GB`. [float] @@ -166,18 +281,26 @@ value for this setting is `1GB`. ==== Notifications [horizontal] -`notifications:banner`:: A custom banner intended for temporary notices to all users. -Supports https://help.github.com/en/articles/basic-writing-and-formatting-syntax[Markdown]. -`notifications:lifetime:banner`:: The duration, in milliseconds, for banner -notification displays. The default value is 3000000. Set this field to `Infinity` -to disable banner notifications. -`notifications:lifetime:error`:: The duration, in milliseconds, for error -notification displays. The default value is 300000. Set this field to `Infinity` to disable error notifications. -`notifications:lifetime:info`:: The duration, in milliseconds, for information notification displays. -The default value is 5000. Set this field to `Infinity` to disable information notifications. -`notifications:lifetime:warning`:: The duration, in milliseconds, for warning notification -displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications. +[[notifications-banner]]`notifications:banner`:: +A custom banner intended for temporary notices to all users. Supports +https://help.github.com/en/articles/basic-writing-and-formatting-syntax[Markdown]. +[[notifications-lifetime-banner]]`notifications:lifetime:banner`:: +The duration, in milliseconds, for banner notification displays. The default +value is 3000000. Set this field to `Infinity` to disable banner notifications. + +[[notificatios-lifetime-error]]`notifications:lifetime:error`:: +The duration, in milliseconds, for error notification displays. The default +value is 300000. Set this field to `Infinity` to disable error notifications. + +[[notifications-lifetime-info]]`notifications:lifetime:info`:: +The duration, in milliseconds, for information notification displays. The +default value is 5000. Set this field to `Infinity` to disable information +notifications. + +[[notifications-lifetime-warning]]`notifications:lifetime:warning`:: +The duration, in milliseconds, for warning notification displays. The default +value is 10000. Set this field to `Infinity` to disable warning notifications. [float] @@ -185,7 +308,8 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa ==== Reporting [horizontal] -`xpackReporting:customPdfLogo`:: A custom image to use in the footer of the PDF. +[[xpackreporting-custompdflogo]]`xpackReporting:customPdfLogo`:: +A custom image to use in the footer of the PDF. [float] @@ -193,9 +317,10 @@ displays. The default value is 10000. Set this field to `Infinity` to disable wa ==== Rollup [horizontal] -`rollups:enableIndexPatterns`:: Enables the creation of index patterns that -capture rollup indices, which in turn enables visualizations based on rollup data. -Refresh the page to apply the changes. +[[rollups-enableindexpatterns]]`rollups:enableIndexPatterns`:: +Enables the creation of index patterns that capture rollup indices, which in +turn enables visualizations based on rollup data. Refresh the page to apply the +changes. [float] @@ -203,67 +328,117 @@ Refresh the page to apply the changes. ==== Search [horizontal] -`courier:batchSearches`:: **Deprecated in 7.6. Starting in 8.0, this setting will be optimized internally.** -When disabled, dashboard panels will load individually, and search requests will terminate when -users navigate away or update the query. When enabled, dashboard panels will load together when all of the data is loaded, -and searches will not terminate. -`courier:customRequestPreference`:: {ref}/search-request-body.html#request-body-search-preference[Request preference] +[[courier-batchsearches]]`courier:batchSearches`:: +**Deprecated in 7.6. Starting in 8.0, this setting will be optimized internally.** +When disabled, dashboard panels will load individually, and search requests will +terminate when users navigate away or update the query. When enabled, dashboard +panels will load together when all of the data is loaded, and searches will not +terminate. + +[[courier-customrequestpreference]]`courier:customRequestPreference`:: +{ref}/search-request-body.html#request-body-search-preference[Request preference] to use when `courier:setRequestPreference` is set to "custom". -`courier:ignoreFilterIfFieldNotInIndex`:: Skips filters that apply to fields that don't exist in the index for a visualization. -Useful when dashboards consist of visualizations from multiple index patterns. -`courier:maxConcurrentShardRequests`:: Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests] + +[[courier-ignorefilteriffieldnotinindex]]`courier:ignoreFilterIfFieldNotInIndex`:: +Skips filters that apply to fields that don't exist in the index for a +visualization. Useful when dashboards consist of visualizations from multiple +index patterns. + +[[courier-maxconcurrentshardrequests]]`courier:maxConcurrentShardRequests`:: +Controls the {ref}/search-multi-search.html[max_concurrent_shard_requests] setting used for `_msearch` requests sent by {kib}. Set to 0 to disable this config and use the {es} default. -`courier:setRequestPreference`:: Enables you to set which shards handle your search requests. -* *Session ID:* Restricts operations to execute all search requests on the same shards. -This has the benefit of reusing shard caches across requests. -* *Custom:* Allows you to define your own preference. Use `courier:customRequestPreference` -to customize your preference value. + +[[courier-setrequestpreference]]`courier:setRequestPreference`:: +Enables you to set which shards handle your search requests. +* *Session ID:* Restricts operations to execute all search requests on the same +shards. This has the benefit of reusing shard caches across requests. +* *Custom:* Allows you to define your own preference. Use +`courier:customRequestPreference` to customize your preference value. * *None:* Do not set a preference. This might provide better performance because requests can be spread across all shard copies. However, results might be inconsistent because different shards might be in different refresh states. -`search:includeFrozen`:: Includes {ref}/frozen-indices.html[frozen indices] in results. -Searching through frozen indices -might increase the search time. This setting is off by default. Users must opt-in to include frozen indices. -`search:timeout`:: Change the maximum timeout for a search session or set to 0 to disable the timeout and allow queries to run to completion. + +[[search-includefrozen]]`search:includeFrozen`:: +Includes {ref}/frozen-indices.html[frozen indices] in results. Searching through +frozen indices might increase the search time. This setting is off by default. +Users must opt-in to include frozen indices. + +[[search-timeout]]`search:timeout`:: Change the maximum timeout for a search +session or set to 0 to disable the timeout and allow queries to run to +completion. [float] [[kibana-siem-settings]] -==== Security Solution +==== Security solution [horizontal] -`securitySolution:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the Security app. -`securitySolution:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the Security app collects events. -`securitySolution:ipReputationLinks`:: A JSON array containing links for verifying the reputation of an IP address. The links are displayed on -{security-guide}/network-page-overview.html[IP detail] pages. -`securitySolution:enableNewsFeed`:: Enables the security news feed on the Security *Overview* -page. -`securitySolution:newsFeedUrl`:: The URL from which the security news feed content is -retrieved. -`securitySolution:refreshIntervalDefaults`:: The default refresh interval for the Security time filter, in milliseconds. -`securitySolution:timeDefaults`:: The default period of time in the Security time filter. +[[securitysolution-defaultanomalyscore]]`securitySolution:defaultAnomalyScore`:: +The threshold above which {ml} job anomalies are displayed in the {security-app}. + +[[securitysolution-defaultindex]]`securitySolution:defaultIndex`:: +A comma-delimited list of {es} indices from which the {security-app} collects +events. + +[[securitysolution-ipreputationlinks]]`securitySolution:ipReputationLinks`:: +A JSON array containing links for verifying the reputation of an IP address. The +links are displayed on {security-guide}/network-page-overview.html[IP detail] +pages. + +[[securitysolution-enablenewsfeed]]`securitySolution:enableNewsFeed`:: Enables +the security news feed on the Security *Overview* page. + +[[securitysolution-newsfeedurl]]`securitySolution:newsFeedUrl`:: +The URL from which the security news feed content is retrieved. + +[[securitysolution-refreshintervaldefaults]]`securitySolution:refreshIntervalDefaults`:: +The default refresh interval for the Security time filter, in milliseconds. + +[[securitysolution-timedefaults]]`securitySolution:timeDefaults`:: +The default period of time in the Security time filter. [float] [[kibana-timelion-settings]] ==== Timelion [horizontal] -`timelion:default_columns`:: The default number of columns to use on a Timelion sheet. -`timelion:default_rows`:: The default number of rows to use on a Timelion sheet. -`timelion:es.default_index`:: The default index when using the `.es()` query. -`timelion:es.timefield`:: The default field containing a timestamp when using the `.es()` query. -`timelion:graphite.url`:: [experimental] Used with graphite queries, this is the URL of your graphite host -in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can be -selected from an allow-list configured in the `kibana.yml` under `timelion.graphiteUrls`. -`timelion:max_buckets`:: The maximum number of buckets a single data source can return. -This value is used for calculating automatic intervals in visualizations. -`timelion:min_interval`:: The smallest interval to calculate when using "auto". -`timelion:quandl.key`:: [experimental] Used with quandl queries, this is your API key from https://www.quandl.com/[www.quandl.com]. -`timelion:showTutorial`:: Shows the Timelion tutorial -to users when they first open the Timelion app. -`timelion:target_buckets`:: Used for calculating automatic intervals in visualizations, -this is the number of buckets to try to represent. +[[timelion-defaultcolumns]]`timelion:default_columns`:: +The default number of columns to use on a Timelion sheet. + +[[timelion-defaultrows]]`timelion:default_rows`:: +The default number of rows to use on a Timelion sheet. + +[[timelion-esdefaultindex]]`timelion:es.default_index`:: +The default index when using the `.es()` query. + +[[timelion-estimefield]]`timelion:es.timefield`:: +The default field containing a timestamp when using the `.es()` query. + +[[timelion-graphite-url]]`timelion:graphite.url`:: +experimental:[] +Used with graphite queries, this is the URL of your graphite host +in the form https://www.hostedgraphite.com/UID/ACCESS_KEY/graphite. This URL can +be selected from an allow-list configured in the `kibana.yml` under +`timelion.graphiteUrls`. + +[[timelion-maxbuckets]]`timelion:max_buckets`:: +The maximum number of buckets a single data source can return. This value is +used for calculating automatic intervals in visualizations. +[[timelion-mininterval]]`timelion:min_interval`:: +The smallest interval to calculate when using "auto". + +[[timelion-quandlkey]]`timelion:quandl.key`:: +experimental:[] +Used with quandl queries, this is your API key from +https://www.quandl.com/[www.quandl.com]. + +[[timelion-showtutorial]]`timelion:showTutorial`:: +Shows the Timelion tutorial to users when they first open the Timelion app. + +[[timelion-targetbuckets]]`timelion:target_buckets`:: +Used for calculating automatic intervals in visualizations, this is the number +of buckets to try to represent. [float] @@ -271,20 +446,32 @@ this is the number of buckets to try to represent. ==== Visualization [horizontal] -`visualization:colorMapping`:: Maps values to specified colors in visualizations. -`visualization:dimmingOpacity`:: The opacity of the chart items that are dimmed -when highlighting another element of the chart. The lower this number, the more -the highlighted element stands out. This must be a number between 0 and 1. -`visualization:loadingDelay`:: The time to wait before dimming visualizations -during a query. -`visualization:regionmap:showWarnings`:: Shows -a warning in a region map when terms cannot be joined to a shape. -`visualization:tileMap:WMSdefaults`:: The default properties for the WMS map server support in the coordinate map. -`visualization:tileMap:maxPrecision`:: The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, +[[visualization-colormapping]]`visualization:colorMapping`:: +Maps values to specified colors in visualizations. + +[[visualization-dimmingopacity]]`visualization:dimmingOpacity`:: +The opacity of the chart items that are dimmed when highlighting another element +of the chart. The lower this number, the more the highlighted element stands out. +This must be a number between 0 and 1. + +[[visualization-loadingdelay]]`visualization:loadingDelay`:: +The time to wait before dimming visualizations during a query. + +[[visualization-regionmap-showwarnings]]`visualization:regionmap:showWarnings`:: +Shows a warning in a region map when terms cannot be joined to a shape. + +[[visualization-tilemap-wmsdefaults]]`visualization:tileMap:WMSdefaults`:: +The default properties for the WMS map server support in the coordinate map. + +[[visualization-tilemap-maxprecision]]`visualization:tileMap:maxPrecision`:: +The maximum geoHash precision displayed on tile maps: 7 is high, 10 is very high, and 12 is the maximum. See this {ref}/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[explanation of cell dimensions]. -`visualize:enableLabs`:: Enables users to create, view, and edit experimental visualizations. -If disabled, only visualizations that are considered production-ready are available to the user. + +[[visualize-enablelabs]]`visualize:enableLabs`:: +Enables users to create, view, and edit experimental visualizations. If disabled, +only visualizations that are considered production-ready are available to the +user. [float] diff --git a/docs/management/images/add_remote_cluster.png b/docs/management/images/add_remote_cluster.png deleted file mode 100755 index 160d29b741c62..0000000000000 Binary files a/docs/management/images/add_remote_cluster.png and /dev/null differ diff --git a/docs/management/images/auto_follow_pattern.png b/docs/management/images/auto_follow_pattern.png deleted file mode 100755 index f80de9352280f..0000000000000 Binary files a/docs/management/images/auto_follow_pattern.png and /dev/null differ diff --git a/docs/management/images/cross-cluster-replication-list-view.png b/docs/management/images/cross-cluster-replication-list-view.png deleted file mode 100755 index 4c45174cff7f1..0000000000000 Binary files a/docs/management/images/cross-cluster-replication-list-view.png and /dev/null differ diff --git a/docs/management/images/remote-clusters-list-view.png b/docs/management/images/remote-clusters-list-view.png deleted file mode 100755 index c28379863b74b..0000000000000 Binary files a/docs/management/images/remote-clusters-list-view.png and /dev/null differ diff --git a/docs/management/managing-ccr.asciidoc b/docs/management/managing-ccr.asciidoc deleted file mode 100644 index 9c06e479e28b2..0000000000000 --- a/docs/management/managing-ccr.asciidoc +++ /dev/null @@ -1,80 +0,0 @@ -[role="xpack"] -[[managing-cross-cluster-replication]] -== Cross-Cluster Replication - -Use *Cross-Cluster Replication* to reproduce indices in -remote clusters on a local cluster. {ref}/xpack-ccr.html[Cross-cluster replication] -is commonly used to provide remote backups for disaster recovery and for -geo-proximite copies of data. - -To get started, open the menu, then go to *Stack Management > Data > Cross-Cluster Replication*. - -[role="screenshot"] -image::images/cross-cluster-replication-list-view.png[][Cross-cluster replication list view] - -[float] -=== Prerequisites - -* You must have a {ref}/modules-remote-clusters.html[remote cluster]. -* Leader indices must meet {ref}/ccr-requirements.html[these requirements]. -* The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster. -Refer to {ref}/ccr-overview.html[this document] for more information. - -[float] -=== Required permissions - -The `manage` and `manage_ccr` cluster privileges are required to access *Cross-Cluster Replication*. - -You can add these privileges in *Stack Management > Security > Roles*. - -[float] -[[configure-replication]] -=== Configure replication - -Replication requires a leader index, the index being replicated, and a -follower index, which will contain the leader index's replicated data. -The follower index is passive in that it can read requests and searches, -but cannot accept direct writes. Only the leader index is active for direct writes. - -You can configure follower indices in two ways: - -* Create specific follower indices -* Create follower indices from an auto-follow pattern - -[float] -==== Create specific follower indices - -To replicate data from existing indices, or set up local followers on a case-by-case basis, -go to *Follower indices*. When you create the follower index, you must reference the -remote cluster and the leader index that you created in the remote cluster. - -[role="screenshot"] -image::images/follower_indices.png[][UI for adding follower indices] - -[float] -==== Create follower indices from an auto-follow pattern - -To automatically detect and follow new indices when they are created on a remote cluster, -go to *Auto-follow patterns*. Creating an auto-follow pattern is useful when you have -time series data, like event logs, on the remote cluster that is created or rolled over on a daily basis. - -When creating the pattern, you must reference the remote cluster that you -connected to your local cluster. You must also specify a collection of index patterns -that match the indices you want to automatically follow. - -Once you configure an -auto-follow pattern, any time a new index with a name that matches the pattern is -created in the remote cluster, a follower index is automatically configured in the local cluster. - -[role="screenshot"] -image::images/auto_follow_pattern.png[UI for adding an auto-follow pattern] - -[float] -[[manage-replication]] -=== Manage replication - -Use the list views in *Cross-Cluster Replication* to monitor whether the replication is active and -pause and resume replication. You can also edit and remove the follower indices and auto-follow patterns. - -For an example of cross-cluster replication, -refer to https://www.elastic.co/blog/bi-directional-replication-with-elasticsearch-cross-cluster-replication-ccr[Bi-directional replication with Elasticsearch cross-cluster replication]. diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc deleted file mode 100644 index 92e0fa822b056..0000000000000 --- a/docs/management/managing-remote-clusters.asciidoc +++ /dev/null @@ -1,50 +0,0 @@ -[[working-remote-clusters]] -== Remote Clusters - -Use *Remote Clusters* to establish a unidirectional -connection from your cluster to other clusters. This functionality is -required for {ref}/xpack-ccr.html[cross-cluster replication] and -{ref}/modules-cross-cluster-search.html[cross-cluster search]. - -To get started, open the menu, then go to *Stack Management > Data > Remote Clusters*. - -[role="screenshot"] -image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button] - -[float] -=== Required permissions - -The `manage` cluster privilege is required to access *Remote Clusters*. - -You can add this privilege in *Stack Management > Security > Roles*. - -[float] -[[managing-remote-clusters]] -=== Add a remote cluster - -A {ref}/modules-remote-clusters.html[remote cluster] connection works by configuring a remote cluster and -connecting to a limited number of nodes, called {ref}/modules-remote-clusters.html#sniff-mode[seed nodes], -in that cluster. -Alternatively, you can define a single proxy address for the remote cluster. - -By default, a cross-cluster request, such as a cross-cluster search or -replication request, fails if any cluster in the request is unavailable. -To skip a cluster when its unavailable, -set *Skip if unavailable* to true. - -Once you add a remote cluster, you can configure <> -to reproduce indices in the remote cluster on a local cluster. - -[role="screenshot"] -image::images/add_remote_cluster.png[][UI for adding a remote cluster] - -To create an index pattern to search across clusters, -use the same syntax that you’d use in a raw cross-cluster search request in {es}: :. -See <> for examples. - -[float] -[[manage-remote-clusters]] -=== Manage remote clusters - -From the *Remote Clusters* list view, you can drill down into each cluster and -view its status. You can also edit and delete a cluster. diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 42d1d89145d79..5067bc08bec99 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -100,3 +100,15 @@ This content has moved to the <> page. == TSVB This page was deleted. See <>. + +[role="exclude",id="managing-cross-cluster-replication"] +== Cross-Cluster Replication + +This content has moved. See +{ref}/ccr-getting-started.html[Set up cross-cluster replication]. + +[role="exclude",id="working-remote-clusters"] +== Remote clusters + +This content has moved. See +{ref}/ccr-getting-started.html#ccr-getting-started-remote-cluster[Connect to a remote cluster]. diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 6c8632efa9cc0..917821ad09e2f 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -33,7 +33,7 @@ For more information, see |=== | `monitoring.enabled` | Set to `true` (default) to enable the {monitor-features} in {kib}. Unlike the - `monitoring.ui.enabled` setting, when this setting is `false`, the + <> setting, when this setting is `false`, the monitoring back-end does not run and {kib} stats are not sent to the monitoring cluster. @@ -44,7 +44,7 @@ a|`monitoring.cluster_alerts.` | `monitoring.ui.elasticsearch.hosts` | Specifies the location of the {es} cluster where your monitoring data is stored. - By default, this is the same as `elasticsearch.hosts`. This setting enables + By default, this is the same as <>. This setting enables you to use a single {kib} instance to search and visualize data in your production cluster as well as monitor data sent to a dedicated monitoring cluster. @@ -58,7 +58,7 @@ a|`monitoring.cluster_alerts.` cluster uses the authenticated user's credentials, which must be the same on both the {es} monitoring cluster and the {es} production cluster. + + - If not set, {kib} uses the value of the `elasticsearch.username` setting. + If not set, {kib} uses the value of the <> setting. | `monitoring.ui.elasticsearch.password` | Specifies the password used by {kib} monitoring to establish a persistent connection @@ -69,11 +69,11 @@ a|`monitoring.cluster_alerts.` cluster uses the authenticated user's credentials, which must be the same on both the {es} monitoring cluster and the {es} production cluster. + + - If not set, {kib} uses the value of the `elasticsearch.password` setting. + If not set, {kib} uses the value of the <> setting. | `monitoring.ui.elasticsearch.pingTimeout` | Specifies the time in milliseconds to wait for {es} to respond to internal - health checks. By default, it matches the `elasticsearch.pingTimeout` setting, + health checks. By default, it matches the <> setting, which has a default value of `30000`. |=== @@ -112,7 +112,7 @@ about configuring {kib}, see | Specifies the number of log entries to display in *{stack-monitor-app}*. Defaults to `10`. The maximum value is `50`. -| `monitoring.ui.enabled` +|[[monitoring-ui-enabled]] `monitoring.ui.enabled` | Set to `false` to hide *{stack-monitor-app}*. The monitoring back-end continues to run as an agent for sending {kib} stats to the monitoring cluster. Defaults to `true`. diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 3489dcd018293..adfc3964d4204 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -20,7 +20,7 @@ You can configure `xpack.reporting` settings in your `kibana.yml` to: | [[xpack-enable-reporting]]`xpack.reporting.enabled` {ess-icon} | Set to `false` to disable the {report-features}. -| `xpack.reporting.encryptionKey` {ess-icon} +|[[xpack-reporting-encryptionKey]] `xpack.reporting.encryptionKey` {ess-icon} | Set to an alphanumeric, at least 32 characters long text string. By default, {kib} will generate a random key when it starts, which will cause pending reports to fail after restart. Configure this setting to preserve the same key across multiple restarts and multiple instances of {kib}. @@ -53,20 +53,20 @@ proxy host requires that the {kib} server has network access to the proxy. [cols="2*<"] |=== | `xpack.reporting.kibanaServer.port` - | The port for accessing {kib}, if different from the `server.port` value. + | The port for accessing {kib}, if different from the <> value. | `xpack.reporting.kibanaServer.protocol` | The protocol for accessing {kib}, typically `http` or `https`. -| `xpack.reporting.kibanaServer.hostname` - | The hostname for accessing {kib}, if different from the `server.host` value. +|[[xpack-kibanaServer-hostname]] `xpack.reporting.kibanaServer.hostname` + | The hostname for accessing {kib}, if different from the <> value. |=== [NOTE] ============ Reporting authenticates requests on the Kibana page only when the hostname matches the -`xpack.reporting.kibanaServer.hostname` setting. Therefore Reporting would fail if the +<> setting. Therefore Reporting would fail if the set value redirects to another server. For that reason, `"0"` is an invalid setting because, in the Reporting browser, it becomes an automatic redirect to `"0.0.0.0"`. ============ @@ -97,8 +97,8 @@ reports, you might need to change the following settings. [NOTE] ============ Running multiple instances of {kib} in a cluster for load balancing of -reporting requires identical values for `xpack.reporting.encryptionKey` and, if -security is enabled, `xpack.security.encryptionKey`. +reporting requires identical values for <> and, if +security is enabled, <>. ============ [cols="2*<"] @@ -177,7 +177,7 @@ available, but there will likely be errors in the visualizations in the report. [[reporting-chromium-settings]] ==== Chromium settings -When `xpack.reporting.capture.browser.type` is set to `chromium` (default) you can also specify the following settings. +When <> is set to `chromium` (default) you can also specify the following settings. [cols="2*<"] |=== @@ -229,10 +229,10 @@ a| `xpack.reporting.capture.browser` See OWASP: https://www.owasp.org/index.php/CSV_Injection Defaults to `true`. -| `xpack.reporting.csv.enablePanelActionDownload` - | Enables CSV export from a saved search on a dashboard. This action is available in the dashboard - panel menu for the saved search. - Defaults to `true`. +| `xpack.reporting.csv` `.enablePanelActionDownload` + | Enables CSV export from a saved search on a dashboard. This action is available in the dashboard panel menu for the saved search. + *Note:* This setting exists for backwards compatibility, but is unused and hardcoded to `true`. CSV export from a saved search on a dashboard + is enabled when Reporting is enabled. |=== @@ -246,7 +246,7 @@ a| `xpack.reporting.capture.browser` | Reporting uses a weekly index in {es} to store the reporting job and the report content. The index is automatically created if it does not already exist. Configure this to a unique value, beginning with `.reporting-`, for every - {kib} instance that has a unique `kibana.index` setting. Defaults to `.reporting`. + {kib} instance that has a unique <> setting. Defaults to `.reporting`. | `xpack.reporting.roles.allow` | Specifies the roles in addition to superusers that can use reporting. diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index b6eecc6ea9f04..00e5f973f7d87 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -190,26 +190,26 @@ You can configure the following settings in the `kibana.yml` file. | `xpack.security.cookieName` | Sets the name of the cookie used for the session. The default value is `"sid"`. -| `xpack.security.encryptionKey` +|[[xpack-security-encryptionKey]] `xpack.security.encryptionKey` | An arbitrary string of 32 characters or more that is used to encrypt session information. Do **not** expose this key to users of {kib}. By default, a value is automatically generated in memory. If you use that default behavior, all sessions are invalidated when {kib} restarts. In addition, high-availability deployments of {kib} will behave unexpectedly if this setting isn't the same for all instances of {kib}. -| `xpack.security.secureCookies` +|[[xpack-security-secureCookies]] `xpack.security.secureCookies` | Sets the `secure` flag of the session cookie. The default value is `false`. It - is automatically set to `true` if `server.ssl.enabled` is set to `true`. Set + is automatically set to `true` if <> is set to `true`. Set this to `true` if SSL is configured outside of {kib} (for example, you are routing requests through a load balancer or proxy). | `xpack.security.sameSiteCookies` {ess-icon} | Sets the `SameSite` attribute of the session cookie. This allows you to declare whether your cookie should be restricted to a first-party or same-site context. Valid values are `Strict`, `Lax`, `None`. - This is *not set* by default, which modern browsers will treat as `Lax`. If you use Kibana embedded in an iframe in modern browsers, you might need to set it to `None`. Setting this value to `None` requires cookies to be sent over a secure connection by setting `xpack.security.secureCookies: true`. + This is *not set* by default, which modern browsers will treat as `Lax`. If you use Kibana embedded in an iframe in modern browsers, you might need to set it to `None`. Setting this value to `None` requires cookies to be sent over a secure connection by setting <>: `true`. -| `xpack.security.session.idleTimeout` {ess-icon} - | Ensures that user sessions will expire after a period of inactivity. This and `xpack.security.session.lifespan` are both +|[[xpack-session-idleTimeout]] `xpack.security.session.idleTimeout` {ess-icon} + | Ensures that user sessions will expire after a period of inactivity. This and <> are both highly recommended. By default, this setting is not set. 2+a| @@ -218,9 +218,9 @@ highly recommended. By default, this setting is not set. The format is a string of `[ms\|s\|m\|h\|d\|w\|M\|Y]` (e.g. '20m', '24h', '7d', '1w'). ============ -| `xpack.security.session.lifespan` {ess-icon} +|[[xpack-session-lifespan]] `xpack.security.session.lifespan` {ess-icon} | Ensures that user sessions will expire after the defined time period. This behavior also known as an "absolute timeout". If -this is _not_ set, user sessions could stay active indefinitely. This and `xpack.security.session.idleTimeout` are both highly +this is _not_ set, user sessions could stay active indefinitely. This and <> are both highly recommended. By default, this setting is not set. 2+a| diff --git a/docs/settings/telemetry-settings.asciidoc b/docs/settings/telemetry-settings.asciidoc index 89c018a86eca6..0329e2f010e80 100644 --- a/docs/settings/telemetry-settings.asciidoc +++ b/docs/settings/telemetry-settings.asciidoc @@ -19,7 +19,7 @@ See our https://www.elastic.co/legal/privacy-statement[Privacy Statement] to lea [cols="2*<"] |=== -| `telemetry.enabled` +|[[telemetry-enabled]] `telemetry.enabled` | Set to `true` to send cluster statistics to Elastic. Reporting your cluster statistics helps us improve your user experience. Your data is never shared with anyone. Set to `false` to disable statistics reporting from any @@ -31,16 +31,16 @@ See our https://www.elastic.co/legal/privacy-statement[Privacy Statement] to lea it is behind a firewall and falls back to `'browser'` to send it from users' browsers when they are navigating through {kib}. Defaults to `'server'`. -| `telemetry.optIn` +|[[telemetry-optIn]] `telemetry.optIn` | Set to `true` to automatically opt into reporting cluster statistics. You can also opt out through *Advanced Settings* in {kib}. Defaults to `true`. | `telemetry.allowChangingOptInStatus` - | Set to `true` to allow overwriting the `telemetry.optIn` setting via the {kib} UI. Defaults to `true`. + + | Set to `true` to allow overwriting the <> setting via the {kib} UI. Defaults to `true`. + |=== [NOTE] ============ -When `false`, `telemetry.optIn` must be `true`. To disable telemetry and not allow users to change that parameter, use `telemetry.enabled`. +When `false`, <> must be `true`. To disable telemetry and not allow users to change that parameter, use <>. ============ diff --git a/docs/setup/secure-settings.asciidoc b/docs/setup/secure-settings.asciidoc index 10380eb5d8fa4..840a18ac03bab 100644 --- a/docs/setup/secure-settings.asciidoc +++ b/docs/setup/secure-settings.asciidoc @@ -19,7 +19,7 @@ bin/kibana-keystore create ---------------------------------------------------------------- The file `kibana.keystore` will be created in the directory defined by the -`path.data` configuration setting. +<> configuration setting. [float] [[list-settings]] diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index f03022e9e9f00..903bb59cef380 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -20,11 +20,11 @@ which may cause a delay before pages start being served. Set to `false` to disable Console. *Default: `true`* | `cpu.cgroup.path.override:` - | *deprecated* This setting has been renamed to `ops.cGroupOverrides.cpuPath` + | *deprecated* This setting has been renamed to <> and the old name will no longer be supported as of 8.0. | `cpuacct.cgroup.path.override:` - | *deprecated* This setting has been renamed to `ops.cGroupOverrides.cpuAcctPath` + | *deprecated* This setting has been renamed to <> and the old name will no longer be supported as of 8.0. | `csp.rules:` @@ -33,7 +33,7 @@ that disables certain unnecessary and potentially insecure capabilities in the browser. It is strongly recommended that you keep the default CSP rules that ship with {kib}. -| `csp.strict:` +|[[csp-strict]] `csp.strict:` | Blocks {kib} access to any browser that does not enforce even rudimentary CSP rules. In practice, this disables support for older, less safe browsers like Internet Explorer. @@ -43,44 +43,39 @@ For more information, refer to <>. | `csp.warnLegacyBrowsers:` | Shows a warning message after loading {kib} to any browser that does not enforce even rudimentary CSP rules, though {kib} is still accessible. This -configuration is effectively ignored when `csp.strict` is enabled. +configuration is effectively ignored when <> is enabled. *Default: `true`* | `elasticsearch.customHeaders:` | Header names and values to send to {es}. Any custom headers cannot be overwritten by client-side headers, regardless of the -`elasticsearch.requestHeadersWhitelist` configuration. *Default: `{}`* +<> configuration. *Default: `{}`* -| `elasticsearch.hosts:` +|[[elasticsearch-hosts]] `elasticsearch.hosts:` | The URLs of the {es} instances to use for all your queries. All nodes listed here must be on the same cluster. *Default: `[ "http://localhost:9200" ]`* -+ + To enable SSL/TLS for outbound connections to {es}, use the `https` protocol in this setting. | `elasticsearch.logQueries:` - | Log queries sent to {es}. Requires `logging.verbose` set to `true`. + | Log queries sent to {es}. Requires <> set to `true`. This is useful for seeing the query DSL generated by applications that currently do not have an inspector, for example Timelion and Monitoring. *Default: `false`* -| `elasticsearch.pingTimeout:` +|[[elasticsearch-pingTimeout]] `elasticsearch.pingTimeout:` | Time in milliseconds to wait for {es} to respond to pings. -*Default: the value of the `elasticsearch.requestTimeout` setting* - -| `elasticsearch.preserveHost:` - | When the value is `true`, {kib} uses the hostname specified in the -`server.host` setting. When the value is `false`, {kib} uses -the hostname of the host that connects to this {kib} instance. *Default: `true`* +*Default: the value of the <> setting* -| `elasticsearch.requestHeadersWhitelist:` +|[[elasticsearch-requestHeadersWhitelist]] `elasticsearch.requestHeadersWhitelist:` | List of {kib} client-side headers to send to {es}. To send *no* client-side headers, set this value to [] (an empty list). Removing the `authorization` header from being whitelisted means that you cannot use <> in {kib}. *Default: `[ 'authorization' ]`* -| `elasticsearch.requestTimeout:` +|[[elasticsearch-requestTimeout]] `elasticsearch.requestTimeout:` | Time in milliseconds to wait for responses from the back end or {es}. This value must be a positive integer. *Default: `30000`* @@ -99,7 +94,7 @@ nodes. *Default: `false`* | Update the list of {es} nodes immediately following a connection fault. *Default: `false`* -| `elasticsearch.ssl.alwaysPresentCertificate:` +|[[elasticsearch-ssl-alwaysPresentCertificate]] `elasticsearch.ssl.alwaysPresentCertificate:` | Controls {kib} behavior in regard to presenting a client certificate when requested by {es}. This setting applies to all outbound SSL/TLS connections to {es}, including requests that are proxied for end users. *Default: `false`* @@ -109,7 +104,7 @@ to {es}, including requests that are proxied for end users. *Default: `false`* [WARNING] ============ When {es} uses certificates to authenticate end users with a PKI realm -and `elasticsearch.ssl.alwaysPresentCertificate` is `true`, +and <> is `true`, proxied requests may be executed as the identity that is tied to the {kib} server. ============ @@ -117,7 +112,7 @@ server. [cols="2*<"] |=== -| `elasticsearch.ssl.certificate:` and `elasticsearch.ssl.key:` +|[[elasticsearch-ssl-cert-key]] `elasticsearch.ssl.certificate:` and `elasticsearch.ssl.key:` | Paths to a PEM-encoded X.509 client certificate and its corresponding private key. These are used by {kib} to authenticate itself when making outbound SSL/TLS connections to {es}. For this setting to take effect, the @@ -129,46 +124,48 @@ be set to `"required"` or `"optional"` to request a client certificate from [NOTE] ============ -These settings cannot be used in conjunction with `elasticsearch.ssl.keystore.path`. +These settings cannot be used in conjunction with +<>. ============ [cols="2*<"] |=== -| `elasticsearch.ssl.certificateAuthorities:` +|[[elasticsearch-ssl-certificateAuthorities]] `elasticsearch.ssl.certificateAuthorities:` | Paths to one or more PEM-encoded X.509 certificate authority (CA) certificates, which make up a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. -+ + In addition to this setting, trusted certificates may be specified via -`elasticsearch.ssl.keystore.path` and/or `elasticsearch.ssl.truststore.path`. +<> and/or +<>. | `elasticsearch.ssl.keyPassphrase:` | The password that decrypts the private key that is specified -via `elasticsearch.ssl.key`. This value is optional, as the key may not be +via <>. This value is optional, as the key may not be encrypted. -| `elasticsearch.ssl.keystore.path:` +|[[elasticsearch-ssl-keystore-path]] `elasticsearch.ssl.keystore.path:` | Path to a PKCS#12 keystore that contains an X.509 client certificate and it's corresponding private key. These are used by {kib} to authenticate itself when making outbound SSL/TLS connections to {es}. For this setting, you must also set the `xpack.security.http.ssl.client_authentication` setting in {es} to `"required"` or `"optional"` to request a client certificate from {kib}. -+ + If the keystore contains any additional certificates, they are used as a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. In addition to this setting, trusted certificates may be specified via -`elasticsearch.ssl.certificateAuthorities` and/or -`elasticsearch.ssl.truststore.path`. +<> and/or +<>. |=== [NOTE] ============ This setting cannot be used in conjunction with -`elasticsearch.ssl.certificate` or `elasticsearch.ssl.key`. +<> or <>. ============ [cols="2*<"] @@ -176,24 +173,24 @@ This setting cannot be used in conjunction with | `elasticsearch.ssl.keystore.password:` | The password that decrypts the keystore specified via -`elasticsearch.ssl.keystore.path`. If the keystore has no password, leave this +<>. If the keystore has no password, leave this as blank. If the keystore has an empty password, set this to `""`. -| `elasticsearch.ssl.truststore.path:`:: +|[[elasticsearch-ssl-truststore-path]] `elasticsearch.ssl.truststore.path:` | Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates, which make up a trusted certificate chain for {es}. This chain is used by {kib} to establish trust when making outbound SSL/TLS connections to {es}. -+ + In addition to this setting, trusted certificates may be specified via -`elasticsearch.ssl.certificateAuthorities` and/or -`elasticsearch.ssl.keystore.path`. +<> and/or +<>. |`elasticsearch.ssl.truststore.password:` | The password that decrypts the trust store specified via -`elasticsearch.ssl.truststore.path`. If the trust store has no password, -leave this as blank. If the trust store has an empty password, set this to `""`. +<>. If the trust store +has no password, leave this as blank. If the trust store has an empty password, set this to `""`. | `elasticsearch.ssl.verificationMode:` | Controls the verification of the server certificate that {kib} receives when @@ -202,11 +199,7 @@ making an outbound SSL/TLS connection to {es}. Valid values are `"full"`, using `"certificate"` skips hostname verification, and using `"none"` skips verification entirely. *Default: `"full"`* -| `elasticsearch.startupTimeout:` - | Time in milliseconds to wait for {es} at {kib} startup before retrying. -*Default: `5000`* - -| `elasticsearch.username:` and `elasticsearch.password:` +|[[elasticsearch-user-passwd]] `elasticsearch.username:` and `elasticsearch.password:` | If your {es} is protected with basic authentication, these settings provide the username and password that the {kib} server uses to perform maintenance on the {kib} index at startup. {kib} users still need to authenticate with @@ -220,7 +213,7 @@ on the {kib} index at startup. {kib} users still need to authenticate with Please use the `defaultRoute` advanced setting instead. The default application to load. *Default: `"home"`* -| `kibana.index:` +|[[kibana-index]] `kibana.index:` | {kib} uses an index in {es} to store saved searches, visualizations, and dashboards. {kib} creates a new index if the index doesn’t already exist. If you configure a custom index, the name must be lowercase, and conform to the @@ -236,7 +229,7 @@ This value must be a whole number greater than zero. *Default: `"1000"`* suggestions. This value must be a whole number greater than zero. *Default: `"100000"`* -| `logging.dest:` +|[[logging-dest]] `logging.dest:` | Enables you to specify a file where {kib} stores log output. *Default: `stdout`* @@ -244,7 +237,7 @@ suggestions. This value must be a whole number greater than zero. | Logs output as JSON. When set to `true`, the logs are formatted as JSON strings that include timestamp, log level, context, message text, and any other metadata that may be associated with the log message. -When `logging.dest.stdout` is set, and there is no interactive terminal ("TTY"), +When <> is set, and there is no interactive terminal ("TTY"), this setting defaults to `true`. *Default: `false`* | `logging.quiet:` @@ -271,7 +264,7 @@ The following example shows a valid logging rotate configuration: | `logging.rotate.enabled:` | experimental[] Set the value of this setting to `true` to -enable log rotation. If you do not have a `logging.dest` set that is different from `stdout` +enable log rotation. If you do not have a <> set that is different from `stdout` that feature would not take any effect. *Default: `false`* | `logging.rotate.everyBytes:` @@ -286,9 +279,9 @@ option has to be in the range of 2 to 1024 files. *Default: `7`* | `logging.rotate.pollingInterval:` | experimental[] The number of milliseconds for the polling strategy in case -the `logging.rotate.usePolling` is enabled. `logging.rotate.usePolling` must be in the 5000 to 3600000 millisecond range. *Default: `10000`* +the <> is enabled. `logging.rotate.usePolling` must be in the 5000 to 3600000 millisecond range. *Default: `10000`* -| `logging.rotate.usePolling:` +|[[logging-rotate-usePolling]] `logging.rotate.usePolling:` | experimental[] By default we try to understand the best way to monitoring the log file and warning about it. Please be aware there are some systems where watch api is not accurate. In those cases, in order to get the feature working, the `polling` method could be used enabling that option. *Default: `false`* @@ -298,24 +291,25 @@ the `polling` method could be used enabling that option. *Default: `false`* suppress all logging output. *Default: `false`* | `logging.timezone` - | Set to the canonical timezone ID -(for example, `America/Los_Angeles`) to log events using that timezone. For a -list of timezones, refer to https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. *Default: `UTC`* + | Set to the canonical time zone ID +(for example, `America/Los_Angeles`) to log events using that time zone. +For possible values, refer to +https://en.wikipedia.org/wiki/List_of_tz_database_time_zones[database time zones]. *Default: `UTC`* -| [[logging-verbose]] `logging.verbose:` {ece-icon} +| [[logging-verbose]] `logging.verbose:` {ess-icon} | Set to `true` to log all events, including system usage information and all requests. *Default: `false`* -| `map.includeElasticMapsService:` {ess-icon} +| [[regionmap-ES-map]] `map.includeElasticMapsService:` {ess-icon} | Set to `false` to disable connections to Elastic Maps Service. -When `includeElasticMapsService` is turned off, only the vector layers configured by `map.regionmap` -and the tile layer configured by `map.tilemap.url` are available in <>. *Default: `true`* +When `includeElasticMapsService` is turned off, only the vector layers configured by <> +and the tile layer configured by <> are available in <>. *Default: `true`* | `map.proxyElasticMapsServiceInMaps:` | Set to `true` to proxy all <> Elastic Maps Service requests through the {kib} server. *Default: `false`* -| [[regionmap-settings]] `map.regionmap:` {ess-icon} {ece-icon} +| [[regionmap-settings]] `map.regionmap:` {ess-icon} | Specifies additional vector layers for use in <> visualizations. Each layer object points to an external vector file that contains a geojson @@ -345,16 +339,10 @@ map.regionmap: [cols="2*<"] |=== -| [[regionmap-ES-map]] `map.includeElasticMapsService:` {ece-icon} - | Turns on or off whether layers from the Elastic Maps Service should be included in the vector -layer option list. By turning this off, -only the layers that are configured here will be included. The default is `true`. -This also affects whether tile-service from the Elastic Maps Service will be available. - -| [[regionmap-attribution]] `map.regionmap.layers[].attribution:` {ess-icon} {ece-icon} +| [[regionmap-attribution]] `map.regionmap.layers[].attribution:` {ess-icon} | Optional. References the originating source of the geojson file. -| [[regionmap-fields]] `map.regionmap.layers[].fields[]:` {ess-icon} {ece-icon} +| [[regionmap-fields]] `map.regionmap.layers[].fields[]:` {ess-icon} | Mandatory. Each layer can contain multiple fields to indicate what properties from the geojson features you wish to expose. The following shows how to define multiple @@ -380,11 +368,11 @@ map.regionmap: [cols="2*<"] |=== -| [[regionmap-field-description]] `map.regionmap.layers[].fields[].description:` {ess-icon} {ece-icon} +| [[regionmap-field-description]] `map.regionmap.layers[].fields[].description:` {ess-icon} | Mandatory. The human readable text that is shown under the Options tab when building the Region Map visualization. -| [[regionmap-field-name]] `map.regionmap.layers[].fields[].name:` {ess-icon} {ece-icon} +| [[regionmap-field-name]] `map.regionmap.layers[].fields[].name:` {ess-icon} | Mandatory. This value is used to do an inner-join between the document stored in {es} and the geojson file. For example, if the field in the geojson is @@ -392,30 +380,30 @@ called `Location` and has city names, there must be a field in {es} that holds the same values that {kib} can then use to lookup for the geoshape data. -| [[regionmap-name]] `map.regionmap.layers[].name:` {ess-icon} {ece-icon} +| [[regionmap-name]] `map.regionmap.layers[].name:` {ess-icon} | Mandatory. A description of the map being provided. -| [[regionmap-url]] `map.regionmap.layers[].url:` {ess-icon} {ece-icon} +| [[regionmap-url]] `map.regionmap.layers[].url:` {ess-icon} | Mandatory. The location of the geojson file as provided by a webserver. -| [[tilemap-settings]] `map.tilemap.options.attribution:` {ess-icon} {ece-icon} +| [[tilemap-settings]] `map.tilemap.options.attribution:` {ess-icon} | The map attribution string. *Default: `"© [Elastic Maps Service](https://www.elastic.co/elastic-maps-service)"`* -| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` {ess-icon} {ece-icon} +| [[tilemap-max-zoom]] `map.tilemap.options.maxZoom:` {ess-icon} | The maximum zoom level. *Default: `10`* -| [[tilemap-min-zoom]] `map.tilemap.options.minZoom:` {ess-icon} {ece-icon} +| [[tilemap-min-zoom]] `map.tilemap.options.minZoom:` {ess-icon} | The minimum zoom level. *Default: `1`* -| [[tilemap-subdomains]] `map.tilemap.options.subdomains:` {ess-icon} {ece-icon} +| [[tilemap-subdomains]] `map.tilemap.options.subdomains:` {ess-icon} | An array of subdomains used by the tile service. Specify the position of the subdomain the URL with the token `{s}`. -| [[tilemap-url]] `map.tilemap.url:` {ess-icon} {ece-icon} +| [[tilemap-url]] `map.tilemap.url:` {ess-icon} | The URL to the tileservice that {kib} uses to display map tiles in tilemap visualizations. By default, {kib} reads this URL from an external metadata service, but users can @@ -427,7 +415,7 @@ override this parameter to use their own Tile Map Service. For example: system for the {kib} UI notification center. Set to `false` to disable the newsfeed system. *Default: `true`* -| `path.data:` +|[[path-data]] `path.data:` | The path where {kib} stores persistent data not saved in {es}. *Default: `data`* @@ -438,17 +426,17 @@ not saved in {es}. *Default: `data`* | Set the interval in milliseconds to sample system and process performance metrics. The minimum value is 100. *Default: `5000`* -| `ops.cGroupOverrides.cpuPath:` +|[[ops-cGroupOverrides-cpuPath]] `ops.cGroupOverrides.cpuPath:` | Override for cgroup cpu path when mounted in a manner that is inconsistent with `/proc/self/cgroup`. -| `ops.cGroupOverrides.cpuAcctPath:` +|[[ops-cGroupOverrides-cpuAcctPath]] `ops.cGroupOverrides.cpuAcctPath:` | Override for cgroup cpuacct path when mounted in a manner that is inconsistent with `/proc/self/cgroup`. -| `server.basePath:` +|[[server-basePath]] `server.basePath:` | Enables you to specify a path to mount {kib} at if you are -running behind a proxy. Use the `server.rewriteBasePath` setting to tell {kib} +running behind a proxy. Use the <> setting to tell {kib} if it should remove the basePath from requests it receives, and to prevent a deprecation warning at startup. This setting cannot end in a slash (`/`). @@ -458,19 +446,19 @@ deprecation warning at startup. This setting cannot end in a slash (`/`). | `server.compression.referrerWhitelist:` | Specifies an array of trusted hostnames, such as the {kib} host, or a reverse proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request `Referer` header. -This setting may not be used when `server.compression.enabled` is set to `false`. *Default: `none`* +This setting may not be used when <> is set to `false`. *Default: `none`* | `server.customResponseHeaders:` {ess-icon} | Header names and values to send on all responses to the client from the {kib} server. *Default: `{}`* -| `server.host:` +|[[server-host]] `server.host:` | This setting specifies the host of the back end server. To allow remote users to connect, set the value to the IP address or DNS name of the {kib} server. *Default: `"localhost"`* | `server.keepaliveTimeout:` | The number of milliseconds to wait for additional data before restarting -the `server.socketTimeout` counter. *Default: `"120000"`* +the <> counter. *Default: `"120000"`* | `server.maxPayloadBytes:` | The maximum payload size in bytes @@ -480,28 +468,28 @@ for incoming server requests. *Default: `1048576`* | A human-readable display name that identifies this {kib} instance. *Default: `"your-hostname"`* -| `server.port:` +|[[server-port]] `server.port:` | {kib} is served by a back end server. This setting specifies the port to use. *Default: `5601`* -| `server.requestId.allowFromAnyIp:` +|[[server-requestId-allowFromAnyIp]] `server.requestId.allowFromAnyIp:` | Sets whether or not the X-Opaque-Id header should be trusted from any IP address for identifying requests in logs and forwarded to Elasticsearch. | `server.requestId.ipAllowlist:` - | A list of IPv4 and IPv6 address which the `X-Opaque-Id` header should be trusted from. Normally this would be set to the IP addresses of the load balancers or reverse-proxy that end users use to access Kibana. If any are set, `server.requestId.allowFromAnyIp` must also be set to `false.` + | A list of IPv4 and IPv6 address which the `X-Opaque-Id` header should be trusted from. Normally this would be set to the IP addresses of the load balancers or reverse-proxy that end users use to access Kibana. If any are set, <> must also be set to `false.` -| `server.rewriteBasePath:` +|[[server-rewriteBasePath]] `server.rewriteBasePath:` | Specifies whether {kib} should -rewrite requests that are prefixed with `server.basePath` or require that they +rewrite requests that are prefixed with <> or require that they are rewritten by your reverse proxy. In {kib} 6.3 and earlier, the default is `false`. In {kib} 7.x, the setting is deprecated. In {kib} 8.0 and later, the default is `true`. *Default: `deprecated`* -| `server.socketTimeout:` +|[[server-socketTimeout]] `server.socketTimeout:` | The number of milliseconds to wait before closing an inactive socket. *Default: `"120000"`* -| `server.ssl.certificate:` and `server.ssl.key:` +|[[server-ssl-cert-key]] `server.ssl.certificate:` and `server.ssl.key:` | Paths to a PEM-encoded X.509 server certificate and its corresponding private key. These are used by {kib} to establish trust when receiving inbound SSL/TLS connections from users. @@ -509,18 +497,18 @@ are used by {kib} to establish trust when receiving inbound SSL/TLS connections [NOTE] ============ -These settings cannot be used in conjunction with `server.ssl.keystore.path`. +These settings cannot be used in conjunction with <>. ============ [cols="2*<"] |=== -| `server.ssl.certificateAuthorities:` +|[[server-ssl-certificateAuthorities]] `server.ssl.certificateAuthorities:` | Paths to one or more PEM-encoded X.509 certificate authority (CA) certificates which make up a trusted certificate chain for {kib}. This chain is used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. If PKI authentication is enabled, this chain is also used by {kib} to verify client certificates from end users. -+ -In addition to this setting, trusted certificates may be specified via `server.ssl.keystore.path` and/or `server.ssl.truststore.path`. + +In addition to this setting, trusted certificates may be specified via <> and/or <>. | `server.ssl.cipherSuites:` | Details on the format, and the valid options, are available via the @@ -533,53 +521,53 @@ connections. Valid values are `"required"`, `"optional"`, and `"none"`. Using `" client presents a certificate, using `"optional"` will allow a client to present a certificate if it has one, and using `"none"` will prevent a client from presenting a certificate. *Default: `"none"`* -| `server.ssl.enabled:` +|[[server-ssl-enabled]] `server.ssl.enabled:` | Enables SSL/TLS for inbound connections to {kib}. When set to `true`, a certificate and its -corresponding private key must be provided. These can be specified via `server.ssl.keystore.path` or the combination of -`server.ssl.certificate` and `server.ssl.key`. *Default: `false`* +corresponding private key must be provided. These can be specified via <> or the combination of +<> and <>. *Default: `false`* | `server.ssl.keyPassphrase:` - | The password that decrypts the private key that is specified via `server.ssl.key`. This value + | The password that decrypts the private key that is specified via <>. This value is optional, as the key may not be encrypted. -| `server.ssl.keystore.path:` +|[[server-ssl-keystore-path]] `server.ssl.keystore.path:` | Path to a PKCS#12 keystore that contains an X.509 server certificate and its corresponding private key. If the keystore contains any additional certificates, those will be used as a trusted certificate chain for {kib}. All of these are used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. The certificate chain is also used by {kib} to verify client certificates from end users when PKI authentication is enabled. -+ -In addition to this setting, trusted certificates may be specified via `server.ssl.certificateAuthorities` and/or -`server.ssl.truststore.path`. + +In addition to this setting, trusted certificates may be specified via <> and/or +<>. |=== [NOTE] ============ -This setting cannot be used in conjunction with `server.ssl.certificate` or `server.ssl.key` +This setting cannot be used in conjunction with <> or <> ============ [cols="2*<"] |=== | `server.ssl.keystore.password:` - | The password that will be used to decrypt the keystore specified via `server.ssl.keystore.path`. If the + | The password that will be used to decrypt the keystore specified via <>. If the keystore has no password, leave this unset. If the keystore has an empty password, set this to `""`. -| `server.ssl.truststore.path:` +|[[server-ssl-truststore-path]] `server.ssl.truststore.path:` | Path to a PKCS#12 trust store that contains one or more X.509 certificate authority (CA) certificates which make up a trusted certificate chain for {kib}. This chain is used by {kib} to establish trust when receiving inbound SSL/TLS connections from end users. If PKI authentication is enabled, this chain is also used by {kib} to verify client certificates from end users. -+ -In addition to this setting, trusted certificates may be specified via `server.ssl.certificateAuthorities` and/or -`server.ssl.keystore.path`. + +In addition to this setting, trusted certificates may be specified via <> and/or +<>. | `server.ssl.truststore.password:` - | The password that will be used to decrypt the trust store specified via `server.ssl.truststore.path`. If + | The password that will be used to decrypt the trust store specified via <>. If the trust store has no password, leave this unset. If the trust store has an empty password, set this to `""`. | `server.ssl.redirectHttpFromPort:` | {kib} binds to this port and redirects -all http requests to https over the port configured as `server.port`. +all http requests to https over the port configured as <>. | `server.ssl.supportedProtocols:` | An array of supported protocols with versions. @@ -588,7 +576,7 @@ Valid protocols: `TLSv1`, `TLSv1.1`, `TLSv1.2`. *Default: TLSv1.1, TLSv1.2* | [[settings-xsrf-whitelist]] `server.xsrf.whitelist:` | It is not recommended to disable protections for arbitrary API endpoints. Instead, supply the `kbn-xsrf` header. -The `server.xsrf.whitelist` setting requires the following format: +The <> setting requires the following format: |=== @@ -608,18 +596,18 @@ The `server.xsrf.whitelist` setting requires the following format: setting this to `true` enables unauthenticated users to access the {kib} server status API and status page. *Default: `false`* -| `telemetry.allowChangingOptInStatus` +|[[telemetry-allowChangingOptInStatus]] `telemetry.allowChangingOptInStatus` | When `true`, users are able to change the telemetry setting at a later time in <>. When `false`, -{kib} looks at the value of `telemetry.optIn` to determine whether to send -telemetry data or not. `telemetry.allowChangingOptInStatus` and `telemetry.optIn` +{kib} looks at the value of <> to determine whether to send +telemetry data or not. <> and <> cannot be `false` at the same time. *Default: `true`*. -| `telemetry.optIn` +|[[settings-telemetry-optIn]] `telemetry.optIn` | When `true`, telemetry data is sent to Elastic. When `false`, collection of telemetry data is disabled. To enable telemetry and prevent users from disabling it, -set `telemetry.allowChangingOptInStatus` to `false` and `telemetry.optIn` to `true`. +set <> to `false` and <> to `true`. *Default: `true`* | `telemetry.enabled` diff --git a/docs/user/alerting/action-types.asciidoc b/docs/user/alerting/action-types.asciidoc index be31458ff39fa..af80b17f8605f 100644 --- a/docs/user/alerting/action-types.asciidoc +++ b/docs/user/alerting/action-types.asciidoc @@ -11,10 +11,19 @@ a| <> | Send email from your server. +a| <> + +| Create an incident in IBM Resilient. + a| <> | Index data into Elasticsearch. +a| <> + +| Create an incident in Jira. + + a| <> | Send an event in PagerDuty. @@ -53,10 +62,12 @@ before {kib} starts. If you preconfigure a connector, you can also <>. include::action-types/email.asciidoc[] +include::action-types/resilient.asciidoc[] include::action-types/index.asciidoc[] +include::action-types/jira.asciidoc[] include::action-types/pagerduty.asciidoc[] include::action-types/server-log.asciidoc[] +include::action-types/servicenow.asciidoc[] include::action-types/slack.asciidoc[] include::action-types/webhook.asciidoc[] include::action-types/pre-configured-connectors.asciidoc[] -include::action-types/servicenow.asciidoc[] diff --git a/docs/user/alerting/action-types/jira.asciidoc b/docs/user/alerting/action-types/jira.asciidoc new file mode 100644 index 0000000000000..48bd6c8501b9f --- /dev/null +++ b/docs/user/alerting/action-types/jira.asciidoc @@ -0,0 +1,77 @@ +[role="xpack"] +[[jira-action-type]] +=== Jira action + +The Jira action type uses the https://developer.atlassian.com/cloud/jira/platform/rest/v2/[REST API v2] to create Jira issues. + +[float] +[[jira-connector-configuration]] +==== Connector configuration + +Jira connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. +URL:: Jira instance URL. +Project key:: Jira project key. +Email (or username):: The account email (or username) for HTTP Basic authentication. +API token (or password):: Jira API authentication token (or password) for HTTP Basic authentication. + +[float] +[[Preconfigured-jira-configuration]] +==== Preconfigured action type + +[source,text] +-- + my-jira: + name: preconfigured-jira-action-type + actionTypeId: .jira + config: + apiUrl: https://elastic.atlassian.net + projectKey: ES + secrets: + email: testuser + apiToken: tokenkeystorevalue +-- + +`config` defines the action type specific to the configuration and contains the following properties: + +[cols="2*<"] +|=== + +| `apiUrl` +| An address that corresponds to *URL*. + +| `projectKey` +| A key that corresponds to *Project Key*. + +|=== + +`secrets` defines sensitive information for the action type: + +[cols="2*<"] +|=== + +| `email` +| A string that corresponds to *Email*. + +| `apiToken` +| A string that corresponds to *API Token*. Should be stored in the <>. + +|=== + +[[jira-action-configuration]] +==== Action configuration + +Jira actions have the following configuration properties: + +Issue type:: The type of the issue. +Priority:: The priority of the incident. +Labels:: The labels of the incident. +Title:: A title for the issue, used for searching the contents of the knowledge base. +Description:: The details about the incident. +Additional comments:: Additional information for the client, such as how to troubleshoot the issue. + +[[configuring-jira]] +==== Configuring and testing Jira + +Jira offers free https://www.atlassian.com/software/jira/free[Instances], which you can use to test incidents. diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc index 2c9add5233c91..9301224e6df48 100644 --- a/docs/user/alerting/action-types/pagerduty.asciidoc +++ b/docs/user/alerting/action-types/pagerduty.asciidoc @@ -36,7 +36,7 @@ This is required to encrypt parameters that must be secured, for example PagerDu If you have security enabled: * You must have -application privileges to access Metrics, APM, Uptime, or SIEM. +application privileges to access Metrics, APM, Uptime, or Security. * If you are using a self-managed deployment with security, you must have Transport Security Layer (TLS) enabled for communication <>. Alerts uses API keys to secure background alert checks and actions, diff --git a/docs/user/alerting/action-types/resilient.asciidoc b/docs/user/alerting/action-types/resilient.asciidoc new file mode 100644 index 0000000000000..b5ddb76d49b0c --- /dev/null +++ b/docs/user/alerting/action-types/resilient.asciidoc @@ -0,0 +1,76 @@ +[role="xpack"] +[[resilient-action-type]] +=== IBM Resilient action + +The IBM Resilient action type uses the https://developer.ibm.com/security/resilient/rest/[RESILIENT REST v2] to create IBM Resilient incidents. + +[float] +[[resilient-connector-configuration]] +==== Connector configuration + +IBM Resilient connectors have the following configuration properties: + +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. +URL:: IBM Resilient instance URL. +Organization ID:: IBM Resilient organization ID. +API key ID:: The authentication key ID for HTTP Basic authentication. +API key secret:: The authentication key secret for HTTP Basic authentication. + +[float] +[[Preconfigured-resilient-configuration]] +==== Preconfigured action type + +[source,text] +-- + my-resilient: + name: preconfigured-resilient-action-type + actionTypeId: .resilient + config: + apiUrl: https://elastic.resilient.net + orgId: ES + secrets: + apiKeyId: testuser + apiKeySecret: tokenkeystorevalue +-- + +`config` defines the action type specific to the configuration and contains the following properties: + +[cols="2*<"] +|=== + +| `apiUrl` +| An address that corresponds to *URL*. + +| `orgId` +| An ID that corresponds to *Organization ID*. + +|=== + +`secrets` defines sensitive information for the action type: + +[cols="2*<"] +|=== + +| `apiKeyId` +| A string that corresponds to *API key ID*. + +| `apiKeySecret` +| A string that corresponds to *API Key secret*. Should be stored in the <>. + +|=== + +[[resilient-action-configuration]] +==== Action configuration + +IBM Resilient actions have the following configuration properties: + +Incident types:: The incident types of the incident. +Severity code:: The severity of the incident. +Name:: A name for the issue, used for searching the contents of the knowledge base. +Description:: The details about the incident. +Additional comments:: Additional information for the client, such as how to troubleshoot the issue. + +[[configuring-resilient]] +==== Configuring and testing IBM Resilient + +IBM Resilient offers https://www.ibm.com/security/intelligent-orchestration/resilient[Instances], which you can use to test incidents. diff --git a/docs/user/alerting/action-types/servicenow.asciidoc b/docs/user/alerting/action-types/servicenow.asciidoc index 32f828aea2357..0acb92bcdb5ee 100644 --- a/docs/user/alerting/action-types/servicenow.asciidoc +++ b/docs/user/alerting/action-types/servicenow.asciidoc @@ -10,7 +10,7 @@ The ServiceNow action type uses the https://developer.servicenow.com/app.do#!/re ServiceNow connectors have the following configuration properties: -Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. URL:: ServiceNow instance URL. Username:: Username for HTTP Basic authentication. Password:: Password for HTTP Basic authentication. @@ -37,7 +37,7 @@ Password:: Password for HTTP Basic authentication. |=== | `apiUrl` -| An address that corresponds to *Sender*. +| An address that corresponds to *URL*. |=== @@ -47,7 +47,7 @@ Password:: Password for HTTP Basic authentication. |=== | `username` -| A string that corresponds to *User*. +| A string that corresponds to *Username*. | `password` | A string that corresponds to *Password*. Should be stored in the <>. @@ -62,7 +62,7 @@ ServiceNow actions have the following configuration properties: Urgency:: The extent to which the incident resolution can delay. Severity:: The severity of the incident. Impact:: The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question. -Short description:: A short description of the incident, used for searching the contents of the knowledge base. +Short description:: A short description for the incident, used for searching the contents of the knowledge base. Description:: The details about the incident. Additional comments:: Additional information for the client, such as how to troubleshoot the issue. diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 6bc085b0f78b9..bdb72b1658cd2 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -6,7 +6,7 @@ beta[] -- -Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with <>, <>, <>, <>, can be centrally managed from the <> UI, and provides a set of built-in <> and <> for you to use. +Alerting allows you to detect complex conditions within different {kib} apps and trigger actions when those conditions are met. Alerting is integrated with <>, <>, <>, <>, can be centrally managed from the <> UI, and provides a set of built-in <> and <> for you to use. image::images/alerting-overview.png[Alerts and actions UI] @@ -148,7 +148,7 @@ Functionally, {kib} alerting differs in that: * {kib} alerts tracks and persists the state of each detected condition through *alert instances*. This makes it possible to mute and throttle individual instances, and detect changes in state such as resolution. * Actions are linked to *alert instances* in {kib} alerting. Actions are fired for each occurrence of a detected condition, rather than for the entire alert. -At a higher level, {kib} alerts allow rich integrations across use cases like <>, <>, <>, and <>. +At a higher level, {kib} alerts allow rich integrations across use cases like <>, <>, <>, and <>. Pre-packaged *alert types* simplify setup, hide the details complex domain-specific detections, while providing a consistent interface across {kib}. [float] @@ -171,7 +171,7 @@ To access alerting in a space, a user must have access to one of the following f * <> * <> -* <> +* <> * <> See <> for more information on configuring roles that provide access to these features. diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index d05a727016455..7f201d2c39e89 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -2,7 +2,7 @@ [[defining-alerts]] == Defining alerts -{kib} alerts can be created in a variety of apps including <>, <>, <>, <> and from <> UI. While alerting details may differ from app to app, they share a common interface for defining and configuring alerts that this section describes in more detail. +{kib} alerts can be created in a variety of apps including <>, <>, <>, <> and from <> UI. While alerting details may differ from app to app, they share a common interface for defining and configuring alerts that this section describes in more detail. [float] === Alert flyout diff --git a/docs/user/dashboard/url-drilldown.asciidoc b/docs/user/dashboard/url-drilldown.asciidoc index e6daf89d72718..ee879256a1fae 100644 --- a/docs/user/dashboard/url-drilldown.asciidoc +++ b/docs/user/dashboard/url-drilldown.asciidoc @@ -238,3 +238,14 @@ Tip: Consider using <> helper for date formatting. | Aggregation field behind the selected range, if available. |=== + +[float] +[[disable]] +==== Disable URL drilldown + +You can disable URL drilldown feature on your {kib} instance by disabling the plugin: + +["source","yml"] +----------- +url_drilldown.enabled: false +----------- diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index bc96463f6efba..e0d550a15a907 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -58,12 +58,12 @@ years of historical data in combination with your raw data. | {ref}/transforms.html[Transforms] |Use transforms to pivot existing {es} indices into summarized or entity-centric indices. -| <> +| {ref}/ccr-getting-started.html[Cross-Cluster Replication] |Replicate indices on a remote cluster and copy them to a follower index on a local cluster. This is important for disaster recovery. It also keeps data local for faster queries. -| <> +| {ref}/ccr-getting-started.html#ccr-getting-started-remote-cluster[Remote Clusters] |Manage your remote clusters for use with cross-cluster search and cross-cluster replication. You can add and remove remote clusters, and check their connectivity. |=== @@ -180,8 +180,6 @@ include::{kib-repo-dir}/management/alerting/connector-management.asciidoc[] include::{kib-repo-dir}/management/managing-beats.asciidoc[] -include::{kib-repo-dir}/management/managing-ccr.asciidoc[] - include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[] include::{kib-repo-dir}/management/index-lifecycle-policies/create-policy.asciidoc[] @@ -200,8 +198,6 @@ include::{kib-repo-dir}/management/managing-licenses.asciidoc[] include::{kib-repo-dir}/management/numeral.asciidoc[] -include::{kib-repo-dir}/management/managing-remote-clusters.asciidoc[] - include::{kib-repo-dir}/management/rollups/create_and_manage_rollups.asciidoc[] include::{kib-repo-dir}/management/managing-saved-objects.asciidoc[] diff --git a/docs/user/monitoring/viewing-metrics.asciidoc b/docs/user/monitoring/viewing-metrics.asciidoc index f35caea025cdd..0c48e3b7d011d 100644 --- a/docs/user/monitoring/viewing-metrics.asciidoc +++ b/docs/user/monitoring/viewing-metrics.asciidoc @@ -13,13 +13,19 @@ At a minimum, you must have monitoring data for the {es} production cluster. Once that data exists, {kib} can display monitoring data for other products in the cluster. +TIP: If you use a separate monitoring cluster to store the monitoring data, it +is strongly recommended that you use a separate {kib} instance to view it. If +you log in to {kib} using SAML, Kerberos, PKI, OpenID Connect, or token +authentication providers, a dedicated {kib} instance is *required*. The security +tokens that are used in these contexts are cluster-specific, therefore you +cannot use a single {kib} instance to connect to both production and monitoring +clusters. For more information about the recommended configuration, see +{ref}/monitoring-overview.html[Monitoring overview]. + . Identify where to retrieve monitoring data from. + -- -The cluster that contains the monitoring data is referred to -as the _monitoring cluster_. - -TIP: If the monitoring data is stored on a *dedicated* monitoring cluster, it is +If the monitoring data is stored on a dedicated monitoring cluster, it is accessible even when the cluster you're monitoring is not. If you have at least a gold license, you can send data from multiple clusters to the same monitoring cluster and view them all through the same instance of {kib}. diff --git a/docs/user/reporting/reporting-troubleshooting.asciidoc b/docs/user/reporting/reporting-troubleshooting.asciidoc index 82f0aa7ca0f19..1f07b0b57d8c7 100644 --- a/docs/user/reporting/reporting-troubleshooting.asciidoc +++ b/docs/user/reporting/reporting-troubleshooting.asciidoc @@ -18,8 +18,11 @@ Having trouble? Here are solutions to common problems you might encounter while [float] [[reporting-diagnostics]] -=== Reporting Diagnostics -Reporting comes with a built-in utility to try to automatically find common issues. When Kibana is running, navigate to the Report Listing page, and click the "Run reporting diagnostics..." button. This will open up a diagnostic tool that checks various parts of the Kibana deployment to come up with any relevant recommendations. +=== Reporting diagnostics +Reporting comes with a built-in utility to try to automatically find common issues. +When {kib} is running, navigate to the Report Listing page, and click *Run reporting diagnostics*. +This will open up a diagnostic tool that checks various parts of the {kib} deployment and +come up with any relevant recommendations. [float] [[reporting-troubleshooting-system-dependencies]] diff --git a/docs/user/security/api-keys/index.asciidoc b/docs/user/security/api-keys/index.asciidoc index c93d7caec1b7d..7cf1b964082d9 100644 --- a/docs/user/security/api-keys/index.asciidoc +++ b/docs/user/security/api-keys/index.asciidoc @@ -72,9 +72,9 @@ The response should look something like this: "api_key" : "FD6P5UA4QCWlZZQhYF3YGw" } -Now, you can use the API key to request {kib} roles. You will need -to base64-encode the `id` and `api_key` provided in the response -and add it to your request as an authorization header. For example: +Now, you can use the API key to request {kib} roles. You'll need to send a request with a +`Authorization` header with a value having the prefix `ApiKey` followed by the credentials, +where credentials is the base64 encoding of `id` and `api_key` joined by a colon. For example: [source,js] curl --location --request GET 'http://localhost:5601/api/security/role' \ diff --git a/examples/alerting_example/common/constants.ts b/examples/alerting_example/common/constants.ts deleted file mode 100644 index 5884eb3220519..0000000000000 --- a/examples/alerting_example/common/constants.ts +++ /dev/null @@ -1,34 +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 const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; - -// always firing -export const DEFAULT_INSTANCES_TO_GENERATE = 5; - -// Astros -export enum Craft { - OuterSpace = 'Outer Space', - ISS = 'ISS', -} -export enum Operator { - AreAbove = 'Are above', - AreBelow = 'Are below', - AreExactly = 'Are exactly', -} diff --git a/examples/alerting_example/public/alert_types/always_firing.tsx b/examples/alerting_example/public/alert_types/always_firing.tsx deleted file mode 100644 index 130519308d3c3..0000000000000 --- a/examples/alerting_example/public/alert_types/always_firing.tsx +++ /dev/null @@ -1,83 +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 React, { Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFieldNumber, EuiFormRow } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { AlertTypeModel } from '../../../../x-pack/plugins/triggers_actions_ui/public'; -import { DEFAULT_INSTANCES_TO_GENERATE } from '../../common/constants'; - -interface AlwaysFiringParamsProps { - alertParams: { instances?: number }; - setAlertParams: (property: string, value: any) => void; - errors: { [key: string]: string[] }; -} - -export function getAlertType(): AlertTypeModel { - return { - id: 'example.always-firing', - name: 'Always Fires', - iconClass: 'bolt', - alertParamsExpression: AlwaysFiringExpression, - validate: (alertParams: AlwaysFiringParamsProps['alertParams']) => { - const { instances } = alertParams; - const validationResult = { - errors: { - instances: new Array(), - }, - }; - if (instances && instances < 0) { - validationResult.errors.instances.push( - i18n.translate('AlertingExample.addAlert.error.invalidRandomInstances', { - defaultMessage: 'instances must be equal or greater than zero.', - }) - ); - } - return validationResult; - }, - requiresAppContext: false, - }; -} - -export const AlwaysFiringExpression: React.FunctionComponent = ({ - alertParams, - setAlertParams, -}) => { - const { instances = DEFAULT_INSTANCES_TO_GENERATE } = alertParams; - return ( - - - - - { - setAlertParams('instances', event.target.valueAsNumber); - }} - /> - - - - - ); -}; diff --git a/examples/alerting_example/public/alert_types/index.ts b/examples/alerting_example/public/alert_types/index.ts deleted file mode 100644 index db9f855b573e8..0000000000000 --- a/examples/alerting_example/public/alert_types/index.ts +++ /dev/null @@ -1,33 +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 { registerNavigation as registerPeopleInSpaceNavigation } from './astros'; -import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; -import { SanitizedAlert } from '../../../../x-pack/plugins/alerts/common'; -import { PluginSetupContract as AlertingSetup } from '../../../../x-pack/plugins/alerts/public'; - -export function registerNavigation(alerts: AlertingSetup) { - // register default navigation - alerts.registerDefaultNavigation( - ALERTING_EXAMPLE_APP_ID, - (alert: SanitizedAlert) => `/alert/${alert.id}` - ); - - registerPeopleInSpaceNavigation(alerts); -} diff --git a/examples/alerting_example/public/application.tsx b/examples/alerting_example/public/application.tsx deleted file mode 100644 index 23e9d19441002..0000000000000 --- a/examples/alerting_example/public/application.tsx +++ /dev/null @@ -1,114 +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 React from 'react'; -import ReactDOM from 'react-dom'; -import { BrowserRouter as Router, Route, RouteComponentProps } from 'react-router-dom'; -import { EuiPage } from '@elastic/eui'; -import { - AppMountParameters, - CoreStart, - IUiSettingsClient, - DocLinksStart, - ToastsSetup, - ApplicationStart, -} from '../../../src/core/public'; -import { DataPublicPluginStart } from '../../../src/plugins/data/public'; -import { ChartsPluginStart } from '../../../src/plugins/charts/public'; - -import { Page } from './components/page'; -import { DocumentationPage } from './components/documentation'; -import { ViewAlertPage } from './components/view_alert'; -import { TriggersAndActionsUIPublicPluginStart } from '../../../x-pack/plugins/triggers_actions_ui/public'; -import { AlertingExamplePublicStartDeps } from './plugin'; -import { ViewPeopleInSpaceAlertPage } from './components/view_astros_alert'; - -export interface AlertingExampleComponentParams { - application: CoreStart['application']; - http: CoreStart['http']; - basename: string; - triggers_actions_ui: TriggersAndActionsUIPublicPluginStart; - data: DataPublicPluginStart; - charts: ChartsPluginStart; - uiSettings: IUiSettingsClient; - docLinks: DocLinksStart; - toastNotifications: ToastsSetup; - capabilities: ApplicationStart['capabilities']; -} - -const AlertingExampleApp = (deps: AlertingExampleComponentParams) => { - const { basename, http } = deps; - return ( - - - ( - - - - )} - /> - ) => { - return ( - - - - ); - }} - /> - ) => { - return ( - - - - ); - }} - /> - - - ); -}; - -export const renderApp = ( - { application, notifications, http, uiSettings, docLinks }: CoreStart, - deps: AlertingExamplePublicStartDeps, - { appBasePath, element }: AppMountParameters -) => { - ReactDOM.render( - , - element - ); - - return () => ReactDOM.unmountComponentAtNode(element); -}; diff --git a/examples/alerting_example/public/components/create_alert.tsx b/examples/alerting_example/public/components/create_alert.tsx deleted file mode 100644 index 72e3835b100fe..0000000000000 --- a/examples/alerting_example/public/components/create_alert.tsx +++ /dev/null @@ -1,77 +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 React, { useState } from 'react'; - -import { EuiIcon, EuiFlexItem, EuiCard, EuiFlexGroup } from '@elastic/eui'; - -import { - AlertsContextProvider, - AlertAdd, -} from '../../../../x-pack/plugins/triggers_actions_ui/public'; -import { AlertingExampleComponentParams } from '../application'; -import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; - -export const CreateAlert = ({ - http, - // eslint-disable-next-line @typescript-eslint/naming-convention - triggers_actions_ui, - charts, - uiSettings, - docLinks, - data, - toastNotifications, - capabilities, -}: AlertingExampleComponentParams) => { - const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); - - return ( - - - } - title={`Create Alert`} - description="Create an new Alert based on one of our example Alert Types ." - onClick={() => setAlertFlyoutVisibility(true)} - /> - - - - - - - - ); -}; diff --git a/examples/alerting_example/public/components/documentation.tsx b/examples/alerting_example/public/components/documentation.tsx deleted file mode 100644 index 17cc34959b010..0000000000000 --- a/examples/alerting_example/public/components/documentation.tsx +++ /dev/null @@ -1,67 +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 React from 'react'; - -import { - EuiText, - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiPageHeader, - EuiPageHeaderSection, - EuiTitle, - EuiSpacer, -} from '@elastic/eui'; -import { CreateAlert } from './create_alert'; -import { AlertingExampleComponentParams } from '../application'; - -export const DocumentationPage = (deps: AlertingExampleComponentParams) => ( - - - - -

Welcome to the Alerting plugin example

-
-
-
- - - - -

Documentation links

-
-
-
- - -

Plugin Structure

-

- This example solution has both `server` and a `public` plugins. The `server` handles - registration of example the AlertTypes, while the `public` handles creation of, and - navigation for, these alert types. -

-
- - -
-
-
-); diff --git a/examples/alerting_example/public/components/page.tsx b/examples/alerting_example/public/components/page.tsx deleted file mode 100644 index 99076c7ddcedf..0000000000000 --- a/examples/alerting_example/public/components/page.tsx +++ /dev/null @@ -1,74 +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 React from 'react'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { - EuiPageBody, - EuiPageContent, - EuiPageContentBody, - EuiPageHeader, - EuiPageHeaderSection, - EuiTitle, - EuiBreadcrumbs, - EuiSpacer, -} from '@elastic/eui'; - -type PageProps = RouteComponentProps & { - title: string; - children: React.ReactNode; - crumb?: string; - isHome?: boolean; -}; - -export const Page = withRouter(({ title, crumb, children, isHome = false, history }: PageProps) => { - const breadcrumbs: Array<{ - text: string; - onClick?: () => void; - }> = [ - { - text: crumb ?? title, - }, - ]; - if (!isHome) { - breadcrumbs.splice(0, 0, { - text: 'Home', - onClick: () => { - history.push(`/`); - }, - }); - } - return ( - - - - -

{title}

-
-
-
- - - - {children} - -
- ); -}); diff --git a/examples/alerting_example/public/index.ts b/examples/alerting_example/public/index.ts deleted file mode 100644 index 4a2bfc79903c3..0000000000000 --- a/examples/alerting_example/public/index.ts +++ /dev/null @@ -1,22 +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 { AlertingExamplePlugin } from './plugin'; - -export const plugin = () => new AlertingExamplePlugin(); diff --git a/examples/alerting_example/public/plugin.tsx b/examples/alerting_example/public/plugin.tsx deleted file mode 100644 index 3f972fa9fe2ee..0000000000000 --- a/examples/alerting_example/public/plugin.tsx +++ /dev/null @@ -1,86 +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 { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; -import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerts/public'; -import { ChartsPluginStart } from '../../../src/plugins/charts/public'; -import { TriggersAndActionsUIPublicPluginSetup } from '../../../x-pack/plugins/triggers_actions_ui/public'; -import { DataPublicPluginStart } from '../../../src/plugins/data/public'; -import { getAlertType as getAlwaysFiringAlertType } from './alert_types/always_firing'; -import { getAlertType as getPeopleInSpaceAlertType } from './alert_types/astros'; -import { registerNavigation } from './alert_types'; -import { DeveloperExamplesSetup } from '../../developer_examples/public'; - -export type Setup = void; -export type Start = void; - -export interface AlertingExamplePublicSetupDeps { - alerts: AlertingSetup; - triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; - developerExamples: DeveloperExamplesSetup; -} - -export interface AlertingExamplePublicStartDeps { - alerts: AlertingSetup; - triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; - charts: ChartsPluginStart; - data: DataPublicPluginStart; -} - -export class AlertingExamplePlugin implements Plugin { - public setup( - core: CoreSetup, - // eslint-disable-next-line @typescript-eslint/naming-convention - { alerts, triggers_actions_ui, developerExamples }: AlertingExamplePublicSetupDeps - ) { - core.application.register({ - id: 'AlertingExample', - title: 'Alerting Example', - navLinkStatus: AppNavLinkStatus.hidden, - async mount(params: AppMountParameters) { - const [coreStart, depsStart] = await core.getStartServices(); - const { renderApp } = await import('./application'); - return renderApp(coreStart, depsStart, params); - }, - }); - - triggers_actions_ui.alertTypeRegistry.register(getAlwaysFiringAlertType()); - triggers_actions_ui.alertTypeRegistry.register(getPeopleInSpaceAlertType()); - - registerNavigation(alerts); - - developerExamples.register({ - appId: 'AlertingExample', - title: 'Alerting', - description: `This alerting example walks you through how to set up a new alert.`, - links: [ - { - label: 'README', - href: 'https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerting', - iconType: 'logoGithub', - size: 's', - target: '_blank', - }, - ], - }); - } - - public start() {} - public stop() {} -} diff --git a/examples/alerting_example/server/alert_types/always_firing.ts b/examples/alerting_example/server/alert_types/always_firing.ts deleted file mode 100644 index b89e5da089336..0000000000000 --- a/examples/alerting_example/server/alert_types/always_firing.ts +++ /dev/null @@ -1,47 +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 uuid from 'uuid'; -import { range } from 'lodash'; -import { AlertType } from '../../../../x-pack/plugins/alerts/server'; -import { DEFAULT_INSTANCES_TO_GENERATE, ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; - -export const alertType: AlertType = { - id: 'example.always-firing', - name: 'Always firing', - actionGroups: [{ id: 'default', name: 'default' }], - defaultActionGroupId: 'default', - async executor({ services, params: { instances = DEFAULT_INSTANCES_TO_GENERATE }, state }) { - const count = (state.count ?? 0) + 1; - - range(instances) - .map(() => ({ id: uuid.v4() })) - .forEach((instance: { id: string }) => { - services - .alertInstanceFactory(instance.id) - .replaceState({ triggerdOnCycle: count }) - .scheduleActions('default'); - }); - - return { - count, - }; - }, - producer: ALERTING_EXAMPLE_APP_ID, -}; diff --git a/examples/alerting_example/server/alert_types/astros.ts b/examples/alerting_example/server/alert_types/astros.ts deleted file mode 100644 index 1ccf8af4ce623..0000000000000 --- a/examples/alerting_example/server/alert_types/astros.ts +++ /dev/null @@ -1,80 +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 axios from 'axios'; -import { AlertType } from '../../../../x-pack/plugins/alerts/server'; -import { Operator, Craft, ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; - -interface PeopleInSpace { - people: Array<{ - craft: string; - name: string; - }>; - number: number; -} - -function getOperator(op: string) { - switch (op) { - case Operator.AreAbove: - return (left: number, right: number) => left > right; - case Operator.AreBelow: - return (left: number, right: number) => left < right; - case Operator.AreExactly: - return (left: number, right: number) => left === right; - default: - return () => { - throw new Error( - `Invalid Operator "${op}" [${Operator.AreAbove},${Operator.AreBelow},${Operator.AreExactly}]` - ); - }; - } -} - -function getCraftFilter(craft: string) { - return (person: { craft: string; name: string }) => - craft === Craft.OuterSpace ? true : craft === person.craft; -} - -export const alertType: AlertType = { - id: 'example.people-in-space', - name: 'People In Space Right Now', - actionGroups: [{ id: 'default', name: 'default' }], - defaultActionGroupId: 'default', - async executor({ services, params }) { - const { outerSpaceCapacity, craft: craftToTriggerBy, op } = params; - - const response = await axios.get('http://api.open-notify.org/astros.json'); - const { - data: { number: peopleInSpace, people = [] }, - } = response; - - const peopleInCraft = people.filter(getCraftFilter(craftToTriggerBy)); - - if (getOperator(op)(peopleInCraft.length, outerSpaceCapacity)) { - peopleInCraft.forEach(({ craft, name }) => { - services.alertInstanceFactory(name).replaceState({ craft }).scheduleActions('default'); - }); - } - - return { - peopleInSpace, - }; - }, - producer: ALERTING_EXAMPLE_APP_ID, -}; diff --git a/examples/alerting_example/server/index.ts b/examples/alerting_example/server/index.ts deleted file mode 100644 index 32e9b181ebb54..0000000000000 --- a/examples/alerting_example/server/index.ts +++ /dev/null @@ -1,23 +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 { PluginInitializer } from 'kibana/server'; -import { AlertingExamplePlugin } from './plugin'; - -export const plugin: PluginInitializer = () => new AlertingExamplePlugin(); diff --git a/examples/alerting_example/server/plugin.ts b/examples/alerting_example/server/plugin.ts deleted file mode 100644 index 4141b48ffeeaf..0000000000000 --- a/examples/alerting_example/server/plugin.ts +++ /dev/null @@ -1,86 +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 { Plugin, CoreSetup } from 'kibana/server'; -import { i18n } from '@kbn/i18n'; -import { DEFAULT_APP_CATEGORIES } from '../../../src/core/server'; -import { PluginSetupContract as AlertingSetup } from '../../../x-pack/plugins/alerts/server'; -import { PluginSetupContract as FeaturesPluginSetup } from '../../../x-pack/plugins/features/server'; - -import { alertType as alwaysFiringAlert } from './alert_types/always_firing'; -import { alertType as peopleInSpaceAlert } from './alert_types/astros'; -import { INDEX_THRESHOLD_ID } from '../../../x-pack/plugins/alerting_builtins/server'; -import { ALERTING_EXAMPLE_APP_ID } from '../common/constants'; - -// this plugin's dependendencies -export interface AlertingExampleDeps { - alerts: AlertingSetup; - features: FeaturesPluginSetup; -} - -export class AlertingExamplePlugin implements Plugin { - public setup(core: CoreSetup, { alerts, features }: AlertingExampleDeps) { - alerts.registerType(alwaysFiringAlert); - alerts.registerType(peopleInSpaceAlert); - - features.registerKibanaFeature({ - id: ALERTING_EXAMPLE_APP_ID, - name: i18n.translate('alertsExample.featureRegistry.alertsExampleFeatureName', { - defaultMessage: 'Alerts Example', - }), - app: [], - management: { - insightsAndAlerting: ['triggersActions'], - }, - category: DEFAULT_APP_CATEGORIES.management, - alerting: [alwaysFiringAlert.id, peopleInSpaceAlert.id, INDEX_THRESHOLD_ID], - privileges: { - all: { - alerting: { - all: [alwaysFiringAlert.id, peopleInSpaceAlert.id, INDEX_THRESHOLD_ID], - }, - savedObject: { - all: [], - read: [], - }, - management: { - insightsAndAlerting: ['triggersActions'], - }, - ui: [], - }, - read: { - alerting: { - read: [alwaysFiringAlert.id, peopleInSpaceAlert.id, INDEX_THRESHOLD_ID], - }, - savedObject: { - all: [], - read: [], - }, - management: { - insightsAndAlerting: ['triggersActions'], - }, - ui: [], - }, - }, - }); - } - - public start() {} - public stop() {} -} diff --git a/examples/alerting_example/tsconfig.json b/examples/alerting_example/tsconfig.json deleted file mode 100644 index 214e4b78a9a70..0000000000000 --- a/examples/alerting_example/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "outDir": "./target" - }, - "include": [ - "index.ts", - "public/**/*.ts", - "public/**/*.tsx", - "server/**/*.ts", - "common/**/*.ts", - "../../typings/**/*", - ], - "exclude": [], - "references": [ - { "path": "../../src/core/tsconfig.json" } - ] -} diff --git a/examples/bfetch_explorer/tsconfig.json b/examples/bfetch_explorer/tsconfig.json index 86b35c5e4943f..253fdd9ee6c89 100644 --- a/examples/bfetch_explorer/tsconfig.json +++ b/examples/bfetch_explorer/tsconfig.json @@ -13,6 +13,7 @@ ], "exclude": [], "references": [ - { "path": "../../src/core/tsconfig.json" } + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, ] } diff --git a/examples/embeddable_examples/public/book/book_embeddable.tsx b/examples/embeddable_examples/public/book/book_embeddable.tsx index 48b81c27e8b8d..65ec22a2759e2 100644 --- a/examples/embeddable_examples/public/book/book_embeddable.tsx +++ b/examples/embeddable_examples/public/book/book_embeddable.tsx @@ -89,6 +89,7 @@ export class BookEmbeddable } else { this.updateOutput({ attributes: this.attributes, + defaultTitle: this.attributes.title, hasMatch: getHasMatch(this.input.search, this.attributes), }); } @@ -125,6 +126,7 @@ export class BookEmbeddable this.updateOutput({ attributes: this.attributes, + defaultTitle: this.attributes.title, hasMatch: getHasMatch(this.input.search, this.attributes), }); } diff --git a/examples/embeddable_examples/tsconfig.json b/examples/embeddable_examples/tsconfig.json index 78098339c16f5..a922c6defcd4c 100644 --- a/examples/embeddable_examples/tsconfig.json +++ b/examples/embeddable_examples/tsconfig.json @@ -14,6 +14,7 @@ ], "exclude": [], "references": [ - { "path": "../../src/core/tsconfig.json" } + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, ] } diff --git a/examples/search_examples/public/components/app.tsx b/examples/search_examples/public/components/app.tsx index 704d31d42e640..ab0ce185f0602 100644 --- a/examples/search_examples/public/components/app.tsx +++ b/examples/search_examples/public/components/app.tsx @@ -56,6 +56,8 @@ import { IndexPatternSelect, IndexPattern, IndexPatternField, + isCompleteResponse, + isErrorResponse, } from '../../../../src/plugins/data/public'; interface SearchExamplesAppDeps { @@ -144,7 +146,7 @@ export const SearchExamplesApp = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { setTimeTook(response.rawResponse.took); const avgResult: number | undefined = response.rawResponse.aggregations ? response.rawResponse.aggregations[1].value @@ -162,7 +164,7 @@ export const SearchExamplesApp = ({ text: mountReactNode(message), }); searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { // TODO: Make response error status clearer notifications.toasts.addWarning('An error has occurred'); searchSubscription$.unsubscribe(); diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index a1116ddbd759b..169982544e6e8 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -20,15 +20,16 @@ import { ISearchStrategy, PluginStart } from '../../../src/plugins/data/server'; import { IMyStrategyResponse, IMyStrategyRequest } from '../common'; -export const mySearchStrategyProvider = (data: PluginStart): ISearchStrategy => { +export const mySearchStrategyProvider = ( + data: PluginStart +): ISearchStrategy => { const es = data.search.getSearchStrategy('es'); return { search: async (context, request, options): Promise => { - request.debug = true; const esSearchRes = await es.search(context, request, options); return { ...esSearchRes, - cool: (request as IMyStrategyRequest).get_cool ? 'YES' : 'NOPE', + cool: request.get_cool ? 'YES' : 'NOPE', }; }, cancel: async (context, id) => { diff --git a/examples/state_containers_examples/tsconfig.json b/examples/state_containers_examples/tsconfig.json index 6cfb9f9dc2321..442fa08022dc4 100644 --- a/examples/state_containers_examples/tsconfig.json +++ b/examples/state_containers_examples/tsconfig.json @@ -14,6 +14,8 @@ ], "exclude": [], "references": [ - { "path": "../../src/core/tsconfig.json" } + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_utils/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, ] } diff --git a/examples/ui_action_examples/tsconfig.json b/examples/ui_action_examples/tsconfig.json index 86b35c5e4943f..253fdd9ee6c89 100644 --- a/examples/ui_action_examples/tsconfig.json +++ b/examples/ui_action_examples/tsconfig.json @@ -13,6 +13,7 @@ ], "exclude": [], "references": [ - { "path": "../../src/core/tsconfig.json" } + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, ] } diff --git a/examples/ui_actions_explorer/tsconfig.json b/examples/ui_actions_explorer/tsconfig.json index 782b9cd57fa7b..b4449819b25a6 100644 --- a/examples/ui_actions_explorer/tsconfig.json +++ b/examples/ui_actions_explorer/tsconfig.json @@ -12,6 +12,7 @@ ], "exclude": [], "references": [ - { "path": "../../src/core/tsconfig.json" } + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, ] } diff --git a/kibana.d.ts b/kibana.d.ts index b707405ffbeaf..50f8b8690d944 100644 --- a/kibana.d.ts +++ b/kibana.d.ts @@ -28,7 +28,6 @@ export { Public, Server }; /** * All exports from TS ambient definitions (where types are added for JS source in a .d.ts file). */ -import * as LegacyKibanaPluginSpec from './src/legacy/plugin_discovery/plugin_spec/plugin_spec_options'; import * as LegacyKibanaServer from './src/legacy/server/kbn_server'; /** @@ -39,8 +38,4 @@ export namespace Legacy { export type Request = LegacyKibanaServer.Request; export type ResponseToolkit = LegacyKibanaServer.ResponseToolkit; export type Server = LegacyKibanaServer.Server; - - export type InitPluginFunction = LegacyKibanaPluginSpec.InitPluginFunction; - export type UiExports = LegacyKibanaPluginSpec.UiExports; - export type PluginSpecOptions = LegacyKibanaPluginSpec.PluginSpecOptions; } diff --git a/package.json b/package.json index 1f2749ea44a90..5cd0c276ef76c 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "kbn:watch": "node scripts/kibana --dev --logging.json=false", "build:types": "rm -rf ./target/types && tsc --p tsconfig.types.json", "docs:acceptApiChanges": "node --max-old-space-size=6144 scripts/check_published_api_changes.js --accept", - "kbn:bootstrap": "node scripts/build_ts_refs && node scripts/register_git_hook", + "kbn:bootstrap": "node scripts/build_ts_refs --project tsconfig.refs.json && node scripts/register_git_hook", "spec_to_console": "node scripts/spec_to_console", "backport-skip-ci": "backport --prDescription \"[skip-ci]\"", "storybook": "node scripts/storybook", @@ -118,7 +118,7 @@ "@babel/core": "^7.11.1", "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", - "@elastic/elasticsearch": "7.9.0-rc.2", + "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "^2.5.0", @@ -127,6 +127,7 @@ "@hapi/good-squeeze": "5.2.1", "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", + "@kbn/apm-config-loader": "1.0.0", "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", @@ -138,6 +139,8 @@ "@kbn/telemetry-tools": "1.0.0", "@kbn/test-subj-selector": "0.2.1", "@kbn/ui-framework": "1.0.0", + "@kbn/ace": "1.0.0", + "@kbn/monaco": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", "@types/yauzl": "^2.9.1", "JSONStream": "1.3.5", @@ -150,9 +153,9 @@ "boom": "^7.2.0", "chalk": "^2.4.2", "check-disk-space": "^2.1.0", - "chokidar": "3.2.1", + "chokidar": "^3.4.2", "color": "1.0.3", - "commander": "3.0.2", + "commander": "^3.0.2", "core-js": "^3.6.4", "cypress-promise": "^1.1.0", "deep-freeze-strict": "^1.1.1", @@ -181,19 +184,19 @@ "json-stable-stringify": "^1.0.1", "json-stringify-safe": "5.0.1", "lodash": "^4.17.20", - "lru-cache": "4.1.5", + "lru-cache": "^4.1.5", "minimatch": "^3.0.4", "moment": "^2.24.0", "moment-timezone": "^0.5.27", - "mustache": "2.3.2", - "node-fetch": "2.6.1", + "mustache": "^2.3.2", + "node-fetch": "^2.6.1", "node-forge": "^0.10.0", "opn": "^5.5.0", "oppsy": "^2.0.0", "p-map": "^4.0.0", "pegjs": "0.10.0", "proxy-from-env": "1.0.0", - "query-string": "5.1.1", + "query-string": "^6.13.2", "re2": "^1.15.4", "react": "^16.12.0", "react-color": "^2.13.8", @@ -209,7 +212,7 @@ "rison-node": "1.0.2", "rxjs": "^6.5.5", "seedrandom": "^3.0.5", - "semver": "^5.5.0", + "semver": "^5.7.0", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -220,14 +223,14 @@ "uuid": "3.3.2", "vision": "^5.3.3", "whatwg-fetch": "^3.0.0", - "yauzl": "2.10.0" + "yauzl": "^2.10.0" }, "devDependencies": { "@babel/parser": "^7.11.2", "@babel/types": "^7.11.0", - "@elastic/apm-rum": "^5.5.0", + "@elastic/apm-rum": "^5.6.0", "@elastic/charts": "21.1.2", - "@elastic/ems-client": "7.9.3", + "@elastic/ems-client": "7.10.0", "@elastic/eslint-config-kibana": "0.15.0", "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/filesaver": "1.1.2", @@ -248,8 +251,11 @@ "@microsoft/api-documenter": "7.7.2", "@microsoft/api-extractor": "7.7.0", "@percy/agent": "^0.26.0", - "@testing-library/react": "^9.3.2", - "@testing-library/react-hooks": "^3.2.1", + "@testing-library/dom": "^7.24.2", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.0.4", + "@testing-library/react-hooks": "^3.4.1", + "@testing-library/user-event": "^12.1.6", "@types/accept": "3.1.1", "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", @@ -273,7 +279,7 @@ "@types/flot": "^0.0.31", "@types/getopts": "^2.0.1", "@types/getos": "^3.0.0", - "@types/glob": "^7.1.1", + "@types/glob": "^7.1.2", "@types/globby": "^8.0.0", "@types/graphql": "^0.13.2", "@types/h2o2": "^8.1.1", @@ -306,7 +312,7 @@ "@types/normalize-path": "^3.0.0", "@types/opn": "^5.1.0", "@types/pegjs": "^0.10.1", - "@types/pngjs": "^3.3.2", + "@types/pngjs": "^3.4.0", "@types/podium": "^1.0.0", "@types/prop-types": "^15.7.3", "@types/reach__router": "^1.2.6", @@ -328,15 +334,16 @@ "@types/styled-components": "^5.1.0", "@types/supertest": "^2.0.5", "@types/supertest-as-promised": "^2.0.38", + "@types/tapable": "^1.0.6", "@types/tar": "^4.0.3", - "@types/testing-library__dom": "^6.10.0", - "@types/testing-library__jest-dom": "^5.7.0", - "@types/testing-library__react": "^9.1.2", - "@types/testing-library__react-hooks": "^3.1.0", + "@types/testing-library__jest-dom": "^5.9.2", + "@types/testing-library__react-hooks": "^3.4.0", "@types/type-detect": "^4.0.1", "@types/uuid": "^3.4.4", "@types/vinyl": "^2.0.4", "@types/vinyl-fs": "^2.4.11", + "@types/webpack": "^4.41.21", + "@types/webpack-env": "^1.15.2", "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^3.10.0", "@typescript-eslint/parser": "^3.10.0", @@ -346,11 +353,11 @@ "angular-route": "^1.8.0", "angular-sortable-view": "^0.0.17", "archiver": "^3.1.1", - "axe-core": "^3.4.1", + "axe-core": "^4.0.2", "babel-eslint": "^10.0.3", "babel-jest": "^25.5.1", "babel-plugin-istanbul": "^6.0.0", - "backport": "5.5.1", + "backport": "5.6.0", "brace": "0.11.1", "chai": "3.5.0", "chance": "1.0.18", @@ -389,7 +396,7 @@ "fetch-mock": "^7.3.9", "fp-ts": "^2.3.1", "geckodriver": "^1.20.0", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "grunt": "1.0.4", "grunt-available-tasks": "^0.6.3", "grunt-cli": "^1.2.0", @@ -435,7 +442,7 @@ "multistream": "^2.1.1", "murmurhash3js": "3.0.1", "mutation-observer": "^1.0.3", - "ngreact": "0.5.1", + "ngreact": "^0.5.1", "nock": "12.0.3", "normalize-path": "^3.0.0", "nyc": "^15.0.1", @@ -471,10 +478,10 @@ "tree-kill": "^1.2.2", "typescript": "4.0.2", "ui-select": "0.19.8", - "vega": "^5.13.0", - "vega-lite": "^4.13.1", - "vega-schema-url-parser": "^1.1.0", - "vega-tooltip": "^0.12.0", + "vega": "^5.16.1", + "vega-lite": "^4.16.8", + "vega-schema-url-parser": "^2.1.0", + "vega-tooltip": "^0.24.2", "vinyl-fs": "^3.0.3", "xml2js": "^0.4.22", "xmlbuilder": "13.0.2", diff --git a/packages/kbn-ace/README.md b/packages/kbn-ace/README.md new file mode 100644 index 0000000000000..54c422a72c6f8 --- /dev/null +++ b/packages/kbn-ace/README.md @@ -0,0 +1,5 @@ +# @kbn/ace + +Contains all Kibana-specific brace related code. Excluding the code that still inside of Console because that code is only used inside of console at the moment. + +This package enables plugins to use this functionality and import it as needed -- behind an async import so that brace does not bloat the JS code needed for first page load of Kibana. diff --git a/packages/kbn-ace/package.json b/packages/kbn-ace/package.json new file mode 100644 index 0000000000000..6f1cee764adf1 --- /dev/null +++ b/packages/kbn-ace/package.json @@ -0,0 +1,20 @@ +{ + "name": "@kbn/ace", + "version": "1.0.0", + "private": true, + "main": "./target/index.js", + "license": "Apache-2.0", + "scripts": { + "build": "node ./scripts/build.js", + "kbn:bootstrap": "yarn build --dev" + }, + "dependencies": { + "brace": "0.11.1" + }, + "devDependencies": { + "@kbn/dev-utils": "1.0.0", + "@kbn/babel-preset": "1.0.0", + "raw-loader": "^3.1.0", + "typescript": "4.0.2" + } +} diff --git a/packages/kbn-ace/scripts/build.js b/packages/kbn-ace/scripts/build.js new file mode 100644 index 0000000000000..2f570ffba1fc6 --- /dev/null +++ b/packages/kbn-ace/scripts/build.js @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const path = require('path'); +const del = require('del'); +const fs = require('fs'); +const supportsColor = require('supports-color'); +const { run } = require('@kbn/dev-utils'); + +const TARGET_BUILD_DIR = path.resolve(__dirname, '../target'); +const ROOT_DIR = path.resolve(__dirname, '../'); +const WORKER_PATH_SECTION = 'ace/modes/x_json/worker/x_json.ace.worker.js'; + +run( + async ({ procRunner, log }) => { + log.info('Deleting old output'); + + await del(TARGET_BUILD_DIR); + + const cwd = ROOT_DIR; + const env = { ...process.env }; + + if (supportsColor.stdout) { + env.FORCE_COLOR = 'true'; + } + + await procRunner.run('tsc ', { + cmd: 'tsc', + args: [], + wait: true, + env, + cwd, + }); + + log.success('Copying worker file to target.'); + + fs.copyFileSync( + path.resolve(__dirname, '..', 'src', WORKER_PATH_SECTION), + path.resolve(__dirname, '..', 'target', WORKER_PATH_SECTION) + ); + + log.success('Complete'); + }, + { + flags: { + boolean: ['dev'], + }, + } +); diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/index.ts b/packages/kbn-ace/src/ace/modes/index.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/index.ts rename to packages/kbn-ace/src/ace/modes/index.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts b/packages/kbn-ace/src/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts rename to packages/kbn-ace/src/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/index.ts b/packages/kbn-ace/src/ace/modes/lexer_rules/index.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/index.ts rename to packages/kbn-ace/src/ace/modes/lexer_rules/index.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/script_highlight_rules.ts b/packages/kbn-ace/src/ace/modes/lexer_rules/script_highlight_rules.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/script_highlight_rules.ts rename to packages/kbn-ace/src/ace/modes/lexer_rules/script_highlight_rules.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/x_json_highlight_rules.ts b/packages/kbn-ace/src/ace/modes/lexer_rules/x_json_highlight_rules.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/lexer_rules/x_json_highlight_rules.ts rename to packages/kbn-ace/src/ace/modes/lexer_rules/x_json_highlight_rules.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/index.ts b/packages/kbn-ace/src/ace/modes/x_json/index.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/index.ts rename to packages/kbn-ace/src/ace/modes/x_json/index.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/index.ts b/packages/kbn-ace/src/ace/modes/x_json/worker/index.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/index.ts rename to packages/kbn-ace/src/ace/modes/x_json/worker/index.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/worker.d.ts b/packages/kbn-ace/src/ace/modes/x_json/worker/worker.d.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/worker.d.ts rename to packages/kbn-ace/src/ace/modes/x_json/worker/worker.d.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/x_json.ace.worker.js b/packages/kbn-ace/src/ace/modes/x_json/worker/x_json.ace.worker.js similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/worker/x_json.ace.worker.js rename to packages/kbn-ace/src/ace/modes/x_json/worker/x_json.ace.worker.js diff --git a/src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/x_json.ts b/packages/kbn-ace/src/ace/modes/x_json/x_json.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/ace/modes/x_json/x_json.ts rename to packages/kbn-ace/src/ace/modes/x_json/x_json.ts diff --git a/packages/kbn-ace/src/index.ts b/packages/kbn-ace/src/index.ts new file mode 100644 index 0000000000000..62a6dbb948997 --- /dev/null +++ b/packages/kbn-ace/src/index.ts @@ -0,0 +1,27 @@ +/* + * 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 { + ElasticsearchSqlHighlightRules, + ScriptHighlightRules, + XJsonHighlightRules, + addXJsonToRules, + XJsonMode, + installXJsonMode, +} from './ace/modes'; diff --git a/packages/kbn-ace/tsconfig.json b/packages/kbn-ace/tsconfig.json new file mode 100644 index 0000000000000..6d3f433c6a6d1 --- /dev/null +++ b/packages/kbn-ace/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "declaration": true, + "sourceMap": true, + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-ace/yarn.lock b/packages/kbn-ace/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-ace/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-apm-config-loader/README.md b/packages/kbn-apm-config-loader/README.md new file mode 100644 index 0000000000000..51623dc745f2c --- /dev/null +++ b/packages/kbn-apm-config-loader/README.md @@ -0,0 +1,13 @@ +# @kbn/apm-config-loader + +Configuration loader for the APM instrumentation script. + +This module is only meant to be used by the APM instrumentation script (`src/apm.js`) +to load the required configuration options from the `kibana.yaml` configuration file with +default values. + +### Why not just use @kbn-config? + +`@kbn/config` is the recommended way to load and read the kibana configuration file, +however in the specific case of APM, we want to only need the minimal dependencies +before loading `elastic-apm-node` to avoid losing instrumentation on the already loaded modules. \ No newline at end of file diff --git a/packages/kbn-apm-config-loader/__fixtures__/config.yml b/packages/kbn-apm-config-loader/__fixtures__/config.yml new file mode 100644 index 0000000000000..b0706d8ff8ea0 --- /dev/null +++ b/packages/kbn-apm-config-loader/__fixtures__/config.yml @@ -0,0 +1,11 @@ +pid: + enabled: true + file: '/var/run/kibana.pid' + obj: { val: 3 } + arr: [1] + empty_obj: {} + empty_arr: [] +obj: { val: 3 } +arr: [1, 2] +empty_obj: {} +empty_arr: [] diff --git a/packages/kbn-apm-config-loader/__fixtures__/config_flat.yml b/packages/kbn-apm-config-loader/__fixtures__/config_flat.yml new file mode 100644 index 0000000000000..a687a9a9088bf --- /dev/null +++ b/packages/kbn-apm-config-loader/__fixtures__/config_flat.yml @@ -0,0 +1,6 @@ +pid.enabled: true +pid.file: '/var/run/kibana.pid' +pid.obj: { val: 3 } +pid.arr: [1, 2] +pid.empty_obj: {} +pid.empty_arr: [] diff --git a/packages/kbn-apm-config-loader/__fixtures__/en_var_ref_config.yml b/packages/kbn-apm-config-loader/__fixtures__/en_var_ref_config.yml new file mode 100644 index 0000000000000..761f6a43ba452 --- /dev/null +++ b/packages/kbn-apm-config-loader/__fixtures__/en_var_ref_config.yml @@ -0,0 +1,5 @@ +foo: 1 +bar: "pre-${KBN_ENV_VAR1}-mid-${KBN_ENV_VAR2}-post" + +elasticsearch: + requestHeadersWhitelist: ["${KBN_ENV_VAR1}", "${KBN_ENV_VAR2}"] diff --git a/packages/kbn-apm-config-loader/__fixtures__/one.yml b/packages/kbn-apm-config-loader/__fixtures__/one.yml new file mode 100644 index 0000000000000..ccef51b546194 --- /dev/null +++ b/packages/kbn-apm-config-loader/__fixtures__/one.yml @@ -0,0 +1,9 @@ +foo: 1 +bar: true +xyz: ['1', '2'] +empty_arr: [] +abc: + def: test + qwe: 1 + zyx: { val: 1 } +pom.bom: 3 diff --git a/packages/kbn-apm-config-loader/__fixtures__/two.yml b/packages/kbn-apm-config-loader/__fixtures__/two.yml new file mode 100644 index 0000000000000..a2ec41265d50f --- /dev/null +++ b/packages/kbn-apm-config-loader/__fixtures__/two.yml @@ -0,0 +1,10 @@ +foo: 2 +baz: bonkers +xyz: ['3', '4'] +arr: [1] +empty_arr: [] +abc: + ghi: test2 + qwe: 2 + zyx: {} +pom.mob: 4 diff --git a/packages/kbn-apm-config-loader/package.json b/packages/kbn-apm-config-loader/package.json new file mode 100644 index 0000000000000..1982ccdeda0ff --- /dev/null +++ b/packages/kbn-apm-config-loader/package.json @@ -0,0 +1,23 @@ +{ + "name": "@kbn/apm-config-loader", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "version": "1.0.0", + "license": "Apache-2.0", + "private": true, + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": { + "@elastic/safer-lodash-set": "0.0.0", + "@kbn/utils": "1.0.0", + "js-yaml": "3.13.1", + "lodash": "^4.17.20" + }, + "devDependencies": { + "typescript": "4.0.2", + "tsd": "^0.7.4" + } +} diff --git a/packages/kbn-apm-config-loader/src/config.test.mocks.ts b/packages/kbn-apm-config-loader/src/config.test.mocks.ts new file mode 100644 index 0000000000000..a0422665a55d2 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config.test.mocks.ts @@ -0,0 +1,66 @@ +/* + * 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 { join } from 'path'; +const childProcessModule = jest.requireActual('child_process'); +const fsModule = jest.requireActual('fs'); + +export const mockedRootDir = '/root'; + +export const packageMock = { + raw: {} as any, +}; +jest.doMock(join(mockedRootDir, 'package.json'), () => packageMock.raw, { virtual: true }); + +export const devConfigMock = { + raw: {} as any, +}; +jest.doMock(join(mockedRootDir, 'config', 'apm.dev.js'), () => devConfigMock.raw, { + virtual: true, +}); + +export const gitRevExecMock = jest.fn(); +jest.doMock('child_process', () => ({ + ...childProcessModule, + execSync: (command: string, options: any) => { + if (command.startsWith('git rev-parse')) { + return gitRevExecMock(command, options); + } + return childProcessModule.execSync(command, options); + }, +})); + +export const readUuidFileMock = jest.fn(); +jest.doMock('fs', () => ({ + ...fsModule, + readFileSync: (path: string, options: any) => { + if (path.endsWith('uuid')) { + return readUuidFileMock(path, options); + } + return fsModule.readFileSync(path, options); + }, +})); + +export const resetAllMocks = () => { + packageMock.raw = {}; + devConfigMock.raw = {}; + gitRevExecMock.mockReset(); + readUuidFileMock.mockReset(); + jest.resetModules(); +}; diff --git a/packages/kbn-apm-config-loader/src/config.test.ts b/packages/kbn-apm-config-loader/src/config.test.ts new file mode 100644 index 0000000000000..fe6247673e312 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config.test.ts @@ -0,0 +1,158 @@ +/* + * 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 { + packageMock, + mockedRootDir, + gitRevExecMock, + devConfigMock, + readUuidFileMock, + resetAllMocks, +} from './config.test.mocks'; + +import { ApmConfiguration } from './config'; + +describe('ApmConfiguration', () => { + beforeEach(() => { + packageMock.raw = { + version: '8.0.0', + build: { + sha: 'sha', + }, + }; + }); + + afterEach(() => { + resetAllMocks(); + }); + + it('sets the correct service name', () => { + packageMock.raw = { + version: '9.2.1', + }; + const config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('myservice').serviceName).toBe('myservice-9_2_1'); + }); + + it('sets the git revision from `git rev-parse` command in non distribution mode', () => { + gitRevExecMock.mockReturnValue('some-git-rev'); + const config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName').globalLabels.git_rev).toBe('some-git-rev'); + }); + + it('sets the git revision from `pkg.build.sha` in distribution mode', () => { + gitRevExecMock.mockReturnValue('dev-sha'); + packageMock.raw = { + version: '9.2.1', + build: { + sha: 'distribution-sha', + }, + }; + const config = new ApmConfiguration(mockedRootDir, {}, true); + expect(config.getConfig('serviceName').globalLabels.git_rev).toBe('distribution-sha'); + }); + + it('reads the kibana uuid from the uuid file', () => { + readUuidFileMock.mockReturnValue('instance-uuid'); + const config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName').globalLabels.kibana_uuid).toBe('instance-uuid'); + }); + + it('uses the uuid from the kibana config if present', () => { + readUuidFileMock.mockReturnValue('uuid-from-file'); + const kibanaConfig = { + server: { + uuid: 'uuid-from-config', + }, + }; + const config = new ApmConfiguration(mockedRootDir, kibanaConfig, false); + expect(config.getConfig('serviceName').globalLabels.kibana_uuid).toBe('uuid-from-config'); + }); + + it('uses the correct default config depending on the `isDistributable` parameter', () => { + let config = new ApmConfiguration(mockedRootDir, {}, false); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + serverUrl: expect.any(String), + secretToken: expect.any(String), + }) + ); + + config = new ApmConfiguration(mockedRootDir, {}, true); + expect(Object.keys(config.getConfig('serviceName'))).not.toContain('serverUrl'); + }); + + it('loads the configuration from the kibana config file', () => { + const kibanaConfig = { + elastic: { + apm: { + active: true, + serverUrl: 'https://url', + secretToken: 'secret', + }, + }, + }; + const config = new ApmConfiguration(mockedRootDir, kibanaConfig, true); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + active: true, + serverUrl: 'https://url', + secretToken: 'secret', + }) + ); + }); + + it('loads the configuration from the dev config is present', () => { + devConfigMock.raw = { + active: true, + serverUrl: 'https://dev-url.co', + }; + const config = new ApmConfiguration(mockedRootDir, {}, true); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + active: true, + serverUrl: 'https://dev-url.co', + }) + ); + }); + + it('respect the precedence of the dev config', () => { + const kibanaConfig = { + elastic: { + apm: { + active: true, + serverUrl: 'https://url', + secretToken: 'secret', + }, + }, + }; + devConfigMock.raw = { + active: true, + serverUrl: 'https://dev-url.co', + }; + const config = new ApmConfiguration(mockedRootDir, kibanaConfig, true); + expect(config.getConfig('serviceName')).toEqual( + expect.objectContaining({ + active: true, + serverUrl: 'https://dev-url.co', + secretToken: 'secret', + }) + ); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts new file mode 100644 index 0000000000000..aab82c6c06a58 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config.ts @@ -0,0 +1,139 @@ +/* + * 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 { join } from 'path'; +import { merge, get } from 'lodash'; +import { execSync } from 'child_process'; +// deep import to avoid loading the whole package +import { getDataPath } from '@kbn/utils/target/path'; +import { readFileSync } from 'fs'; +import { ApmAgentConfig } from './types'; + +const getDefaultConfig = (isDistributable: boolean): ApmAgentConfig => { + if (isDistributable) { + return { + active: false, + globalLabels: {}, + }; + } + return { + active: false, + serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443', + // The secretToken below is intended to be hardcoded in this file even though + // it makes it public. This is not a security/privacy issue. Normally we'd + // instead disable the need for a secretToken in the APM Server config where + // the data is transmitted to, but due to how it's being hosted, it's easier, + // for now, to simply leave it in. + secretToken: 'R0Gjg46pE9K9wGestd', + globalLabels: {}, + breakdownMetrics: true, + centralConfig: false, + logUncaughtExceptions: true, + }; +}; + +export class ApmConfiguration { + private baseConfig?: any; + private kibanaVersion: string; + private pkgBuild: Record; + + constructor( + private readonly rootDir: string, + private readonly rawKibanaConfig: Record, + private readonly isDistributable: boolean + ) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { version, build } = require(join(this.rootDir, 'package.json')); + this.kibanaVersion = version.replace(/\./g, '_'); + this.pkgBuild = build; + } + + public getConfig(serviceName: string): ApmAgentConfig { + return { + ...this.getBaseConfig(), + serviceName: `${serviceName}-${this.kibanaVersion}`, + }; + } + + private getBaseConfig() { + if (!this.baseConfig) { + const apmConfig = merge( + getDefaultConfig(this.isDistributable), + this.getConfigFromKibanaConfig(), + this.getDevConfig() + ); + + const rev = this.getGitRev(); + if (rev !== null) { + apmConfig.globalLabels.git_rev = rev; + } + + const uuid = this.getKibanaUuid(); + if (uuid) { + apmConfig.globalLabels.kibana_uuid = uuid; + } + this.baseConfig = apmConfig; + } + + return this.baseConfig; + } + + private getConfigFromKibanaConfig(): ApmAgentConfig { + return get(this.rawKibanaConfig, 'elastic.apm', {}); + } + + private getKibanaUuid() { + // try to access the `server.uuid` value from the config file first. + // if not manually defined, we will then read the value from the `{DATA_FOLDER}/uuid` file. + // note that as the file is created by the platform AFTER apm init, the file + // will not be present at first startup, but there is nothing we can really do about that. + if (get(this.rawKibanaConfig, 'server.uuid')) { + return this.rawKibanaConfig.server.uuid; + } + + const dataPath: string = get(this.rawKibanaConfig, 'path.data') || getDataPath(); + try { + const filename = join(dataPath, 'uuid'); + return readFileSync(filename, 'utf-8'); + } catch (e) {} // eslint-disable-line no-empty + } + + private getDevConfig(): ApmAgentConfig { + try { + const apmDevConfigPath = join(this.rootDir, 'config', 'apm.dev.js'); + return require(apmDevConfigPath); + } catch (e) { + return {}; + } + } + + private getGitRev() { + if (this.isDistributable) { + return this.pkgBuild.sha; + } + try { + return execSync('git rev-parse --short HEAD', { + encoding: 'utf-8' as BufferEncoding, + stdio: ['ignore', 'pipe', 'ignore'], + }).trim(); + } catch (e) { + return null; + } + } +} diff --git a/packages/kbn-apm-config-loader/src/config_loader.test.mocks.ts b/packages/kbn-apm-config-loader/src/config_loader.test.mocks.ts new file mode 100644 index 0000000000000..74b50d9daf632 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config_loader.test.mocks.ts @@ -0,0 +1,45 @@ +/* + * 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 getConfigurationFilePathsMock = jest.fn(); +jest.doMock('./utils/get_config_file_paths', () => ({ + getConfigurationFilePaths: getConfigurationFilePathsMock, +})); + +export const getConfigFromFilesMock = jest.fn(); +jest.doMock('./utils/read_config', () => ({ + getConfigFromFiles: getConfigFromFilesMock, +})); + +export const applyConfigOverridesMock = jest.fn(); +jest.doMock('./utils/apply_config_overrides', () => ({ + applyConfigOverrides: applyConfigOverridesMock, +})); + +export const ApmConfigurationMock = jest.fn(); +jest.doMock('./config', () => ({ + ApmConfiguration: ApmConfigurationMock, +})); + +export const resetAllMocks = () => { + getConfigurationFilePathsMock.mockReset(); + getConfigFromFilesMock.mockReset(); + applyConfigOverridesMock.mockReset(); + ApmConfigurationMock.mockReset(); +}; diff --git a/packages/kbn-apm-config-loader/src/config_loader.test.ts b/packages/kbn-apm-config-loader/src/config_loader.test.ts new file mode 100644 index 0000000000000..da59237de231e --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config_loader.test.ts @@ -0,0 +1,75 @@ +/* + * 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 { + ApmConfigurationMock, + applyConfigOverridesMock, + getConfigFromFilesMock, + getConfigurationFilePathsMock, + resetAllMocks, +} from './config_loader.test.mocks'; + +import { loadConfiguration } from './config_loader'; + +describe('loadConfiguration', () => { + const argv = ['some', 'arbitrary', 'args']; + const rootDir = '/root/dir'; + const isDistributable = false; + + afterEach(() => { + resetAllMocks(); + }); + + it('calls `getConfigurationFilePaths` with the correct arguments', () => { + loadConfiguration(argv, rootDir, isDistributable); + expect(getConfigurationFilePathsMock).toHaveBeenCalledTimes(1); + expect(getConfigurationFilePathsMock).toHaveBeenCalledWith(argv); + }); + + it('calls `getConfigFromFiles` with the correct arguments', () => { + const configPaths = ['/path/to/config', '/path/to/other/config']; + getConfigurationFilePathsMock.mockReturnValue(configPaths); + + loadConfiguration(argv, rootDir, isDistributable); + expect(getConfigFromFilesMock).toHaveBeenCalledTimes(1); + expect(getConfigFromFilesMock).toHaveBeenCalledWith(configPaths); + }); + + it('calls `applyConfigOverrides` with the correct arguments', () => { + const config = { server: { uuid: 'uuid' } }; + getConfigFromFilesMock.mockReturnValue(config); + + loadConfiguration(argv, rootDir, isDistributable); + expect(applyConfigOverridesMock).toHaveBeenCalledTimes(1); + expect(applyConfigOverridesMock).toHaveBeenCalledWith(config, argv); + }); + + it('creates and return an `ApmConfiguration` instance', () => { + const apmInstance = { apmInstance: true }; + ApmConfigurationMock.mockImplementation(() => apmInstance); + + const config = { server: { uuid: 'uuid' } }; + getConfigFromFilesMock.mockReturnValue(config); + + const instance = loadConfiguration(argv, rootDir, isDistributable); + expect(ApmConfigurationMock).toHaveBeenCalledTimes(1); + expect(ApmConfigurationMock).toHaveBeenCalledWith(rootDir, config, isDistributable); + expect(instance).toBe(apmInstance); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/config_loader.ts b/packages/kbn-apm-config-loader/src/config_loader.ts new file mode 100644 index 0000000000000..edddd445b9b7a --- /dev/null +++ b/packages/kbn-apm-config-loader/src/config_loader.ts @@ -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 { getConfigurationFilePaths, getConfigFromFiles, applyConfigOverrides } from './utils'; +import { ApmConfiguration } from './config'; + +/** + * Load the APM configuration. + * + * @param argv the `process.argv` arguments + * @param rootDir The root directory of kibana (where the sources and the `package.json` file are) + * @param production true for production builds, false otherwise + */ +export const loadConfiguration = ( + argv: string[], + rootDir: string, + isDistributable: boolean +): ApmConfiguration => { + const configPaths = getConfigurationFilePaths(argv); + const rawConfiguration = getConfigFromFiles(configPaths); + applyConfigOverrides(rawConfiguration, argv); + return new ApmConfiguration(rootDir, rawConfiguration, isDistributable); +}; diff --git a/packages/kbn-apm-config-loader/src/index.ts b/packages/kbn-apm-config-loader/src/index.ts new file mode 100644 index 0000000000000..0d9c057c7cf89 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { loadConfiguration } from './config_loader'; +export type { ApmConfiguration } from './config'; +export type { ApmAgentConfig } from './types'; diff --git a/packages/kbn-apm-config-loader/src/types.ts b/packages/kbn-apm-config-loader/src/types.ts new file mode 100644 index 0000000000000..172edfe0af009 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/types.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// There is an (incomplete) `AgentConfigOptions` type declared in node_modules/elastic-apm-node/index.d.ts +// but it's not exported, and using ts tricks to retrieve the type via Parameters[0] +// causes errors in the generated .d.ts file because of esModuleInterop and the fact that the apm module +// is just exporting an instance of the `ApmAgent` type. +export type ApmAgentConfig = Record; diff --git a/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap b/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap new file mode 100644 index 0000000000000..afdce4e76d3f5 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`different cwd() resolves relative files based on the cwd 1`] = ` +Object { + "abc": Object { + "def": "test", + "qwe": 1, + "zyx": Object { + "val": 1, + }, + }, + "bar": true, + "empty_arr": Array [], + "foo": 1, + "pom": Object { + "bom": 3, + }, + "xyz": Array [ + "1", + "2", + ], +} +`; + +exports[`reads and merges multiple yaml files from file system and parses to json 1`] = ` +Object { + "abc": Object { + "def": "test", + "ghi": "test2", + "qwe": 2, + "zyx": Object {}, + }, + "arr": Array [ + 1, + ], + "bar": true, + "baz": "bonkers", + "empty_arr": Array [], + "foo": 2, + "pom": Object { + "bom": 3, + "mob": 4, + }, + "xyz": Array [ + "3", + "4", + ], +} +`; + +exports[`reads single yaml from file system and parses to json 1`] = ` +Object { + "arr": Array [ + 1, + 2, + ], + "empty_arr": Array [], + "empty_obj": Object {}, + "obj": Object { + "val": 3, + }, + "pid": Object { + "arr": Array [ + 1, + ], + "empty_arr": Array [], + "empty_obj": Object {}, + "enabled": true, + "file": "/var/run/kibana.pid", + "obj": Object { + "val": 3, + }, + }, +} +`; + +exports[`returns a deep object 1`] = ` +Object { + "pid": Object { + "arr": Array [ + 1, + 2, + ], + "empty_arr": Array [], + "empty_obj": Object {}, + "enabled": true, + "file": "/var/run/kibana.pid", + "obj": Object { + "val": 3, + }, + }, +} +`; + +exports[`should inject an environment variable value when setting a value with \${ENV_VAR} 1`] = ` +Object { + "bar": "pre-val1-mid-val2-post", + "elasticsearch": Object { + "requestHeadersWhitelist": Array [ + "val1", + "val2", + ], + }, + "foo": 1, +} +`; + +exports[`should throw an exception when referenced environment variable in a config value does not exist 1`] = `"Unknown environment variable referenced in config : KBN_ENV_VAR1"`; diff --git a/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.test.ts b/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.test.ts new file mode 100644 index 0000000000000..1d86f7e1f6e8a --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { applyConfigOverrides } from './apply_config_overrides'; + +describe('applyConfigOverrides', () => { + it('overrides `server.uuid` when provided as a command line argument', () => { + const config: Record = { + server: { + uuid: 'from-config', + }, + }; + const argv = ['--server.uuid', 'from-argv']; + + applyConfigOverrides(config, argv); + + expect(config.server.uuid).toEqual('from-argv'); + }); + + it('overrides `path.data` when provided as a command line argument', () => { + const config: Record = { + path: { + data: '/from/config', + }, + }; + const argv = ['--path.data', '/from/argv']; + + applyConfigOverrides(config, argv); + + expect(config.path.data).toEqual('/from/argv'); + }); + + it('properly set the overridden properties even if the parent object is not present in the config', () => { + const config: Record = {}; + const argv = ['--server.uuid', 'from-argv', '--path.data', '/data-path']; + + applyConfigOverrides(config, argv); + + expect(config.server.uuid).toEqual('from-argv'); + expect(config.path.data).toEqual('/data-path'); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.ts b/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.ts new file mode 100644 index 0000000000000..6a3bf95f9954d --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/apply_config_overrides.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { set } from '@elastic/safer-lodash-set'; +import { getArgValue } from './read_argv'; + +/** + * Manually applies the specific configuration overrides we need to load the APM config. + * Currently, only these are needed: + * - server.uuid + * - path.data + */ +export const applyConfigOverrides = (config: Record, argv: string[]) => { + const serverUuid = getArgValue(argv, '--server.uuid'); + if (serverUuid) { + set(config, 'server.uuid', serverUuid); + } + const dataPath = getArgValue(argv, '--path.data'); + if (dataPath) { + set(config, 'path.data', dataPath); + } +}; diff --git a/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.test.ts b/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.test.ts new file mode 100644 index 0000000000000..5a520fbeef316 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.test.ts @@ -0,0 +1,156 @@ +/* + * 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 { ensureDeepObject } from './ensure_deep_object'; + +test('flat object', () => { + const obj = { + 'foo.a': 1, + 'foo.b': 2, + }; + + expect(ensureDeepObject(obj)).toEqual({ + foo: { + a: 1, + b: 2, + }, + }); +}); + +test('deep object', () => { + const obj = { + foo: { + a: 1, + b: 2, + }, + }; + + expect(ensureDeepObject(obj)).toEqual({ + foo: { + a: 1, + b: 2, + }, + }); +}); + +test('flat within deep object', () => { + const obj = { + foo: { + b: 2, + 'bar.a': 1, + }, + }; + + expect(ensureDeepObject(obj)).toEqual({ + foo: { + b: 2, + bar: { + a: 1, + }, + }, + }); +}); + +test('flat then flat object', () => { + const obj = { + 'foo.bar': { + b: 2, + 'quux.a': 1, + }, + }; + + expect(ensureDeepObject(obj)).toEqual({ + foo: { + bar: { + b: 2, + quux: { + a: 1, + }, + }, + }, + }); +}); + +test('full with empty array', () => { + const obj = { + a: 1, + b: [], + }; + + expect(ensureDeepObject(obj)).toEqual({ + a: 1, + b: [], + }); +}); + +test('full with array of primitive values', () => { + const obj = { + a: 1, + b: [1, 2, 3], + }; + + expect(ensureDeepObject(obj)).toEqual({ + a: 1, + b: [1, 2, 3], + }); +}); + +test('full with array of full objects', () => { + const obj = { + a: 1, + b: [{ c: 2 }, { d: 3 }], + }; + + expect(ensureDeepObject(obj)).toEqual({ + a: 1, + b: [{ c: 2 }, { d: 3 }], + }); +}); + +test('full with array of flat objects', () => { + const obj = { + a: 1, + b: [{ 'c.d': 2 }, { 'e.f': 3 }], + }; + + expect(ensureDeepObject(obj)).toEqual({ + a: 1, + b: [{ c: { d: 2 } }, { e: { f: 3 } }], + }); +}); + +test('flat with flat and array of flat objects', () => { + const obj = { + a: 1, + 'b.c': 2, + d: [3, { 'e.f': 4 }, { 'g.h': 5 }], + }; + + expect(ensureDeepObject(obj)).toEqual({ + a: 1, + b: { c: 2 }, + d: [3, { e: { f: 4 } }, { g: { h: 5 } }], + }); +}); + +test('array composed of flat objects', () => { + const arr = [{ 'c.d': 2 }, { 'e.f': 3 }]; + + expect(ensureDeepObject(arr)).toEqual([{ c: { d: 2 } }, { e: { f: 3 } }]); +}); diff --git a/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.ts b/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.ts new file mode 100644 index 0000000000000..6eaaef983355c --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/ensure_deep_object.ts @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const separator = '.'; + +/** + * Recursively traverses through the object's properties and expands ones with + * dot-separated names into nested objects (eg. { a.b: 'c'} -> { a: { b: 'c' }). + * @param obj Object to traverse through. + * @returns Same object instance with expanded properties. + */ +export function ensureDeepObject(obj: any): any { + if (obj == null || typeof obj !== 'object') { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map((item) => ensureDeepObject(item)); + } + + return Object.keys(obj).reduce((fullObject, propertyKey) => { + const propertyValue = obj[propertyKey]; + if (!propertyKey.includes(separator)) { + fullObject[propertyKey] = ensureDeepObject(propertyValue); + } else { + walk(fullObject, propertyKey.split(separator), propertyValue); + } + + return fullObject; + }, {} as any); +} + +function walk(obj: any, keys: string[], value: any) { + const key = keys.shift()!; + if (keys.length === 0) { + obj[key] = value; + return; + } + + if (obj[key] === undefined) { + obj[key] = {}; + } + + walk(obj[key], keys, ensureDeepObject(value)); +} diff --git a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts new file mode 100644 index 0000000000000..c18069f21180b --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts @@ -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 { resolve, join } from 'path'; +import { getConfigPath } from '@kbn/utils'; +import { getConfigurationFilePaths } from './get_config_file_paths'; + +describe('getConfigurationFilePaths', () => { + const cwd = process.cwd(); + + it('retrieve the config file paths from the command line arguments', () => { + const argv = ['--config', './relative-path', '-c', '/absolute-path']; + + expect(getConfigurationFilePaths(argv)).toEqual([ + resolve(cwd, join('.', 'relative-path')), + '/absolute-path', + ]); + }); + + it('fallbacks to `getConfigPath` value', () => { + expect(getConfigurationFilePaths([])).toEqual([getConfigPath()]); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts new file mode 100644 index 0000000000000..262f0d1c8b3f5 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts @@ -0,0 +1,37 @@ +/* + * 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 { resolve } from 'path'; +// deep import to avoid loading the whole package +import { getConfigPath } from '@kbn/utils/target/path'; +import { getArgValues } from './read_argv'; + +/** + * Return the configuration files that needs to be loaded. + * + * This mimics the behavior of the `src/cli/serve/serve.js` cli script by reading + * `-c` and `--config` options from process.argv, and fallbacks to `@kbn/utils`'s `getConfigPath()` + */ +export const getConfigurationFilePaths = (argv: string[]): string[] => { + const rawPaths = getArgValues(argv, ['-c', '--config']); + if (rawPaths.length) { + return rawPaths.map((path) => resolve(process.cwd(), path)); + } + return [getConfigPath()]; +}; diff --git a/packages/kbn-apm-config-loader/src/utils/index.ts b/packages/kbn-apm-config-loader/src/utils/index.ts new file mode 100644 index 0000000000000..03a44e31a44d5 --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { getConfigFromFiles } from './read_config'; +export { getConfigurationFilePaths } from './get_config_file_paths'; +export { applyConfigOverrides } from './apply_config_overrides'; diff --git a/packages/kbn-apm-config-loader/src/utils/read_argv.test.ts b/packages/kbn-apm-config-loader/src/utils/read_argv.test.ts new file mode 100644 index 0000000000000..282810e71681e --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/read_argv.test.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 { getArgValue, getArgValues } from './read_argv'; + +describe('getArgValues', () => { + it('retrieve the arg value from the provided argv arguments', () => { + const argValues = getArgValues( + ['--config', 'my-config', '--foo', '-b', 'bar', '--config', 'other-config', '--baz'], + '--config' + ); + expect(argValues).toEqual(['my-config', 'other-config']); + }); + + it('accept aliases', () => { + const argValues = getArgValues( + ['--config', 'my-config', '--foo', '-b', 'bar', '-c', 'other-config', '--baz'], + ['--config', '-c'] + ); + expect(argValues).toEqual(['my-config', 'other-config']); + }); + + it('returns an empty array when the arg is not found', () => { + const argValues = getArgValues( + ['--config', 'my-config', '--foo', '-b', 'bar', '-c', 'other-config', '--baz'], + '--unicorn' + ); + expect(argValues).toEqual([]); + }); + + it('ignores the flag when no value is provided', () => { + const argValues = getArgValues( + ['-c', 'my-config', '--foo', '-b', 'bar', '--config'], + ['--config', '-c'] + ); + expect(argValues).toEqual(['my-config']); + }); +}); + +describe('getArgValue', () => { + it('retrieve the first arg value from the provided argv arguments', () => { + const argValues = getArgValue( + ['--config', 'my-config', '--foo', '-b', 'bar', '--config', 'other-config', '--baz'], + '--config' + ); + expect(argValues).toEqual('my-config'); + }); + + it('accept aliases', () => { + const argValues = getArgValue( + ['-c', 'my-config', '--foo', '-b', 'bar', '--config', 'other-config', '--baz'], + ['--config', '-c'] + ); + expect(argValues).toEqual('my-config'); + }); + + it('returns undefined the arg is not found', () => { + const argValues = getArgValue( + ['--config', 'my-config', '--foo', '-b', 'bar', '-c', 'other-config', '--baz'], + '--unicorn' + ); + expect(argValues).toBeUndefined(); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/utils/read_argv.ts b/packages/kbn-apm-config-loader/src/utils/read_argv.ts new file mode 100644 index 0000000000000..9a74d5344a0fc --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/read_argv.ts @@ -0,0 +1,36 @@ +/* + * 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 getArgValues = (argv: string[], flag: string | string[]): string[] => { + const flags = typeof flag === 'string' ? [flag] : flag; + const values: string[] = []; + for (let i = 0; i < argv.length; i++) { + if (flags.includes(argv[i]) && argv[i + 1]) { + values.push(argv[++i]); + } + } + return values; +}; + +export const getArgValue = (argv: string[], flag: string | string[]): string | undefined => { + const values = getArgValues(argv, flag); + if (values.length) { + return values[0]; + } +}; diff --git a/packages/kbn-apm-config-loader/src/utils/read_config.test.ts b/packages/kbn-apm-config-loader/src/utils/read_config.test.ts new file mode 100644 index 0000000000000..7320e5dcbd6ce --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/read_config.test.ts @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { relative, resolve } from 'path'; +import { getConfigFromFiles } from './read_config'; + +const fixtureFile = (name: string) => resolve(__dirname, '..', '..', '__fixtures__', name); + +test('reads single yaml from file system and parses to json', () => { + const config = getConfigFromFiles([fixtureFile('config.yml')]); + + expect(config).toMatchSnapshot(); +}); + +test('returns a deep object', () => { + const config = getConfigFromFiles([fixtureFile('config_flat.yml')]); + + expect(config).toMatchSnapshot(); +}); + +test('reads and merges multiple yaml files from file system and parses to json', () => { + const config = getConfigFromFiles([fixtureFile('one.yml'), fixtureFile('two.yml')]); + + expect(config).toMatchSnapshot(); +}); + +test('should inject an environment variable value when setting a value with ${ENV_VAR}', () => { + process.env.KBN_ENV_VAR1 = 'val1'; + process.env.KBN_ENV_VAR2 = 'val2'; + + const config = getConfigFromFiles([fixtureFile('en_var_ref_config.yml')]); + + delete process.env.KBN_ENV_VAR1; + delete process.env.KBN_ENV_VAR2; + + expect(config).toMatchSnapshot(); +}); + +test('should throw an exception when referenced environment variable in a config value does not exist', () => { + expect(() => + getConfigFromFiles([fixtureFile('en_var_ref_config.yml')]) + ).toThrowErrorMatchingSnapshot(); +}); + +describe('different cwd()', () => { + const originalCwd = process.cwd(); + const tempCwd = resolve(__dirname); + + beforeAll(() => process.chdir(tempCwd)); + afterAll(() => process.chdir(originalCwd)); + + test('resolves relative files based on the cwd', () => { + const relativePath = relative(tempCwd, fixtureFile('one.yml')); + const config = getConfigFromFiles([relativePath]); + + expect(config).toMatchSnapshot(); + }); + + test('fails to load relative paths, not found because of the cwd', () => { + const relativePath = relative(resolve(__dirname, '..', '..'), fixtureFile('one.yml')); + expect(() => getConfigFromFiles([relativePath])).toThrowError(/ENOENT/); + }); +}); diff --git a/packages/kbn-apm-config-loader/src/utils/read_config.ts b/packages/kbn-apm-config-loader/src/utils/read_config.ts new file mode 100644 index 0000000000000..825bfd60181bf --- /dev/null +++ b/packages/kbn-apm-config-loader/src/utils/read_config.ts @@ -0,0 +1,64 @@ +/* + * 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'; +import { safeLoad } from 'js-yaml'; + +import { set } from '@elastic/safer-lodash-set'; +import { isPlainObject } from 'lodash'; +import { ensureDeepObject } from './ensure_deep_object'; + +const readYaml = (path: string) => safeLoad(readFileSync(path, 'utf8')); + +function replaceEnvVarRefs(val: string) { + return val.replace(/\$\{(\w+)\}/g, (match, envVarName) => { + const envVarValue = process.env[envVarName]; + if (envVarValue !== undefined) { + return envVarValue; + } + + throw new Error(`Unknown environment variable referenced in config : ${envVarName}`); + }); +} + +function merge(target: Record, value: any, key?: string) { + if ((isPlainObject(value) || Array.isArray(value)) && Object.keys(value).length > 0) { + for (const [subKey, subVal] of Object.entries(value)) { + merge(target, subVal, key ? `${key}.${subKey}` : subKey); + } + } else if (key !== undefined) { + set(target, key, typeof value === 'string' ? replaceEnvVarRefs(value) : value); + } + + return target; +} + +/** @internal */ +export const getConfigFromFiles = (configFiles: readonly string[]): Record => { + let mergedYaml: Record = {}; + + for (const configFile of configFiles) { + const yaml = readYaml(configFile); + if (yaml !== null) { + mergedYaml = merge(mergedYaml, yaml); + } + } + + return ensureDeepObject(mergedYaml); +}; diff --git a/packages/kbn-apm-config-loader/tsconfig.json b/packages/kbn-apm-config-loader/tsconfig.json new file mode 100644 index 0000000000000..ba00ddfa6adb6 --- /dev/null +++ b/packages/kbn-apm-config-loader/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "./target", + "stripInternal": false, + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"], + "exclude": ["target"] +} diff --git a/packages/kbn-apm-config-loader/yarn.lock b/packages/kbn-apm-config-loader/yarn.lock new file mode 120000 index 0000000000000..3f82ebc9cdbae --- /dev/null +++ b/packages/kbn-apm-config-loader/yarn.lock @@ -0,0 +1 @@ +../../yarn.lock \ No newline at end of file diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index 2d9dbc3b7ab8f..062520f47f0f9 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -25,6 +25,6 @@ }, "devDependencies": { "typescript": "4.0.2", - "tsd": "^0.7.4" + "tsd": "^0.13.1" } } diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a3fe8178822aa..a85f5924f0ea2 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -12,7 +12,7 @@ "dependencies": { "@babel/core": "^7.11.1", "@kbn/utils": "1.0.0", - "axios": "^0.19.0", + "axios": "^0.19.2", "chalk": "^4.1.0", "cheerio": "0.22.0", "dedent": "^0.7.0", diff --git a/packages/kbn-es-archiver/package.json b/packages/kbn-es-archiver/package.json index 13b5662519b19..81c1747bb2727 100644 --- a/packages/kbn-es-archiver/package.json +++ b/packages/kbn-es-archiver/package.json @@ -4,8 +4,8 @@ "license": "Apache-2.0", "main": "target/index.js", "scripts": { - "kbn:bootstrap": "tsc", - "kbn:watch": "tsc --watch" + "kbn:bootstrap": "rm -rf target && tsc", + "kbn:watch": "rm -rf target && tsc --watch" }, "dependencies": { "@kbn/dev-utils": "1.0.0", diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index 52ef3fe05e751..40d34c5d710bb 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -1,23 +1,32 @@ { "name": "@kbn/es", - "main": "./src/index.js", + "main": "./target/index.js", "version": "1.0.0", "license": "Apache-2.0", "private": true, + "scripts": { + "kbn:bootstrap": "node scripts/build", + "kbn:watch": "node scripts/build --watch" + }, "dependencies": { "@elastic/elasticsearch": "7.9.0-rc.1", "@kbn/dev-utils": "1.0.0", - "abort-controller": "^2.0.3", + "abort-controller": "^3.0.0", "chalk": "^4.1.0", "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^4.0.2", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "glob": "^7.1.2", "node-fetch": "^2.6.1", - "simple-git": "^1.91.0", + "simple-git": "1.116.0", "tar-fs": "^2.1.0", "tree-kill": "^1.2.2", "yauzl": "^2.10.0" + }, + "devDependencies": { + "@kbn/babel-preset": "1.0.0", + "@babel/cli": "^7.10.5", + "del": "^5.1.0" } } diff --git a/packages/kbn-es/scripts/build.js b/packages/kbn-es/scripts/build.js new file mode 100644 index 0000000000000..50aad665c920b --- /dev/null +++ b/packages/kbn-es/scripts/build.js @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const { resolve } = require('path'); + +const del = require('del'); +const { run, withProcRunner } = require('@kbn/dev-utils'); + +const ROOT_DIR = resolve(__dirname, '..'); +const BUILD_DIR = resolve(ROOT_DIR, 'target'); + +run( + async ({ log, flags }) => { + await withProcRunner(log, async (proc) => { + log.info('Deleting old output'); + await del(BUILD_DIR); + + const cwd = ROOT_DIR; + + log.info(`Starting babel${flags.watch ? ' in watch mode' : ''}`); + await proc.run(`babel`, { + cmd: 'babel', + args: [ + 'src', + '--no-babelrc', + '--presets', + require.resolve('@kbn/babel-preset/node_preset'), + '--extensions', + '.ts,.js', + '--copy-files', + '--out-dir', + BUILD_DIR, + ...(flags.watch ? ['--watch'] : ['--quiet']), + ...(!flags['source-maps'] || !!process.env.CODE_COVERAGE + ? [] + : ['--source-maps', 'inline']), + ], + wait: true, + cwd, + }); + + log.success('Complete'); + }); + }, + { + description: 'Simple build tool for @kbn/es package', + flags: { + boolean: ['watch', 'source-maps'], + help: ` + --watch Run in watch mode + --source-maps Include sourcemaps + `, + }, + } +); diff --git a/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js b/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js index b860664443d1a..27e73e6c204e8 100644 --- a/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js +++ b/packages/kbn-es/src/integration_tests/__fixtures__/es_bin.js @@ -25,65 +25,67 @@ const { exitCode, start, ssl } = JSON.parse(process.argv[2]); const { createServer } = ssl ? require('https') : require('http'); const { ES_KEY_PATH, ES_CERT_PATH } = require('@kbn/dev-utils'); -process.exitCode = exitCode; +(function main() { + process.exitCode = exitCode; -if (!start) { - return; -} + if (!start) { + return; + } + + let serverUrl; + const server = createServer( + { + // Note: the integration uses the ES_P12_PATH, but that keystore contains + // the same key/cert as ES_KEY_PATH and ES_CERT_PATH + key: ssl ? fs.readFileSync(ES_KEY_PATH) : undefined, + cert: ssl ? fs.readFileSync(ES_CERT_PATH) : undefined, + }, + (req, res) => { + const url = new URL(req.url, serverUrl); + const send = (code, body) => { + res.writeHead(code, { 'content-type': 'application/json' }); + res.end(JSON.stringify(body)); + }; -let serverUrl; -const server = createServer( - { - // Note: the integration uses the ES_P12_PATH, but that keystore contains - // the same key/cert as ES_KEY_PATH and ES_CERT_PATH - key: ssl ? fs.readFileSync(ES_KEY_PATH) : undefined, - cert: ssl ? fs.readFileSync(ES_CERT_PATH) : undefined, - }, - (req, res) => { - const url = new URL(req.url, serverUrl); - const send = (code, body) => { - res.writeHead(code, { 'content-type': 'application/json' }); - res.end(JSON.stringify(body)); - }; + if (url.pathname === '/_xpack') { + return send(400, { + error: { + reason: 'foo bar', + }, + }); + } - if (url.pathname === '/_xpack') { - return send(400, { + return send(404, { error: { - reason: 'foo bar', + reason: 'not found', }, }); } + ); - return send(404, { - error: { - reason: 'not found', - }, - }); - } -); - -// setup server auto close after 1 second of silence -let serverCloseTimer; -const delayServerClose = () => { - clearTimeout(serverCloseTimer); - serverCloseTimer = setTimeout(() => server.close(), 1000); -}; -server.on('request', delayServerClose); -server.on('listening', delayServerClose); + // setup server auto close after 1 second of silence + let serverCloseTimer; + const delayServerClose = () => { + clearTimeout(serverCloseTimer); + serverCloseTimer = setTimeout(() => server.close(), 1000); + }; + server.on('request', delayServerClose); + server.on('listening', delayServerClose); -server.listen(0, '127.0.0.1', function () { - const { port, address: hostname } = server.address(); - serverUrl = new URL( - formatUrl({ - protocol: 'http:', - port, - hostname, - }) - ); + server.listen(0, '127.0.0.1', function () { + const { port, address: hostname } = server.address(); + serverUrl = new URL( + formatUrl({ + protocol: 'http:', + port, + hostname, + }) + ); - console.log( - `[o.e.h.AbstractHttpServerTransport] [computer] publish_address {127.0.0.1:${port}}, bound_addresses {[::1]:${port}}, {127.0.0.1:${port}}` - ); + console.log( + `[o.e.h.AbstractHttpServerTransport] [computer] publish_address {127.0.0.1:${port}}, bound_addresses {[::1]:${port}}, {127.0.0.1:${port}}` + ); - console.log('started'); -}); + console.log('started'); + }); +})(); diff --git a/packages/kbn-eslint-import-resolver-kibana/package.json b/packages/kbn-eslint-import-resolver-kibana/package.json index 332f7e8a20cc2..223c73e97908e 100755 --- a/packages/kbn-eslint-import-resolver-kibana/package.json +++ b/packages/kbn-eslint-import-resolver-kibana/package.json @@ -13,7 +13,7 @@ "debug": "^2.6.9", "eslint-import-resolver-node": "0.3.2", "eslint-import-resolver-webpack": "0.11.1", - "glob-all": "^3.1.0", + "glob-all": "^3.2.1", "lru-cache": "^4.1.5", "resolve": "^1.7.1", "webpack": "^4.41.5" diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index eccdff9060cbe..9e0ec50fb838e 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -19,7 +19,7 @@ "@types/intl-relativeformat": "^2.1.0", "@types/react-intl": "^2.3.15", "del": "^5.1.0", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "supports-color": "^7.0.0", "typescript": "4.0.2" }, diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index aef63229ebe96..430dac6cb2e00 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -11,7 +11,7 @@ "dependencies": { "@babel/runtime": "^7.11.2", "@kbn/i18n": "1.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "uuid": "3.3.2" }, "devDependencies": { @@ -25,12 +25,12 @@ "copy-webpack-plugin": "^6.0.2", "css-loader": "^3.4.2", "del": "^5.1.0", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "pegjs": "0.10.0", "sass-loader": "^8.0.2", "style-loader": "^1.1.3", "supports-color": "^7.0.0", - "url-loader": "2.2.0", + "url-loader": "^2.2.0", "webpack": "^4.41.5", "webpack-cli": "^3.3.10" } diff --git a/packages/kbn-monaco/package.json b/packages/kbn-monaco/package.json index ca133010fe230..fcea80c9b7110 100644 --- a/packages/kbn-monaco/package.json +++ b/packages/kbn-monaco/package.json @@ -18,7 +18,7 @@ "babel-loader": "^8.0.6", "css-loader": "^3.4.2", "del": "^5.1.0", - "raw-loader": "3.1.0", + "raw-loader": "^3.1.0", "supports-color": "^7.0.0", "typescript": "4.0.2", "webpack": "^4.41.5", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index b80d1365659dd..f90fcaec79fe0 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -27,7 +27,7 @@ "jest-diff": "^25.5.0", "json-stable-stringify": "^1.0.1", "loader-utils": "^1.2.3", - "node-sass": "^4.13.0", + "node-sass": "^4.13.1", "normalize-path": "^3.0.0", "postcss": "^7.0.32", "postcss-loader": "^3.0.0", diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 45598ff8831b0..b1ab1ebfe49f2 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -161,7 +161,8 @@ export class OptimizerConfig { Path.resolve(repoRoot, 'src/plugins'), ...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]), Path.resolve(repoRoot, 'plugins'), - ...(examples ? [Path.resolve('examples'), Path.resolve('x-pack/examples')] : []), + ...(examples ? [Path.resolve('examples')] : []), + ...(examples && !oss ? [Path.resolve('x-pack/examples')] : []), Path.resolve(repoRoot, '../kibana-extra'), ]; if (!pluginScanDirs.every((p) => Path.isAbsolute(p))) { diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 9f2c5654a8bd4..2edf1c999888e 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -216,7 +216,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: }, resolve: { - extensions: ['.js', '.ts', '.tsx', 'json'], + extensions: ['.js', '.ts', '.tsx', '.json'], mainFields: ['browser', 'main'], alias: { tinymath: require.resolve('tinymath/lib/tinymath.es5.js'), diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index f292387c12521..65b44b6965048 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -25,7 +25,7 @@ "devDependencies": { "@types/extract-zip": "^1.6.2", "@types/gulp-zip": "^4.0.1", - "@types/inquirer": "^6.5.0", + "@types/inquirer": "^7.3.1", "extract-zip": "^2.0.1", "typescript": "4.0.2" } diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 9a3bb1c687032..7dcfc1d778ea8 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__(503); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(496); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); @@ -103,10 +103,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(163); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(279); +/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(275); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(280); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(276); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -150,7 +150,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(127); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(496); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(489); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(143); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -8763,9 +8763,9 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(128); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(287); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(395); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(396); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(388); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(389); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8806,8 +8806,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(281); -/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(286); +/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(277); +/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(282); +/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(279); +/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(283); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8832,6 +8834,8 @@ __webpack_require__.r(__webpack_exports__); + + const BootstrapCommand = { description: 'Install dependencies and crosslink projects', name: 'bootstrap', @@ -8861,6 +8865,8 @@ const BootstrapCommand = { } } + const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); + await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__["linkProjectExecutables"])(projects, projectGraph); /** * At the end of the bootstrapping process we call all `kbn:bootstrap` scripts @@ -8869,7 +8875,7 @@ const BootstrapCommand = { * have to, as it will slow down the bootstrapping process. */ - const checksums = await Object(_utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__["getAllChecksums"])(kbn, _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"]); + const checksums = await Object(_utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__["getAllChecksums"])(kbn, _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"], yarnLock); const caches = new Map(); let cachedProjectCount = 0; @@ -8987,6 +8993,7 @@ async function linkProjectExecutables(projectsByName, projectGraph) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readFile", function() { return readFile; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "writeFile", function() { return writeFile; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "chmod", function() { return chmod; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mkdirp", function() { return mkdirp; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "unlink", function() { return unlink; }); @@ -9030,6 +9037,7 @@ __webpack_require__.r(__webpack_exports__); const lstat = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.lstat); const readFile = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFile); +const writeFile = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.writeFile); const symlink = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.symlink); const chmod = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_1___default.a.chmod); const cmdShim = Object(util__WEBPACK_IMPORTED_MODULE_4__["promisify"])(cmd_shim__WEBPACK_IMPORTED_MODULE_0___default.a); @@ -10823,7 +10831,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(162); /* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(163); -/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(279); +/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(275); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -23642,9 +23650,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(stream__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(227); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(236); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(232); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); +/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(267); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -23735,12 +23743,12 @@ function spawnStreaming(command, args, opts, { "use strict"; -const ansiStyles = __webpack_require__(228); -const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(232); +const ansiStyles = __webpack_require__(113); +const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(228); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(234); +} = __webpack_require__(230); const {isArray} = Array; @@ -23949,7 +23957,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(235); + template = __webpack_require__(231); } return template(chalk, parts.join('')); @@ -23970,10489 +23978,10739 @@ module.exports = chalk; /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const wrapAnsi16 = (fn, offset) => (...args) => { - const code = fn(...args); - return `\u001B[${code + offset}m`; -}; +const os = __webpack_require__(120); +const tty = __webpack_require__(121); +const hasFlag = __webpack_require__(229); -const wrapAnsi256 = (fn, offset) => (...args) => { - const code = fn(...args); - return `\u001B[${38 + offset};5;${code}m`; -}; +const {env} = process; -const wrapAnsi16m = (fn, offset) => (...args) => { - const rgb = fn(...args); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; -}; +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = 1; +} -const ansi2ansi = n => n; -const rgb2rgb = (r, g, b) => [r, g, b]; +if ('FORCE_COLOR' in env) { + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } +} -const setLazyProperty = (object, property, get) => { - Object.defineProperty(object, property, { - get: () => { - const value = get(); +function translateLevel(level) { + if (level === 0) { + return false; + } - Object.defineProperty(object, property, { - value, - enumerable: true, - configurable: true - }); + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} - return value; - }, - enumerable: true, - configurable: true - }); -}; +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { + return 0; + } -/** @type {typeof import('color-convert')} */ -let colorConvert; -const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { - if (colorConvert === undefined) { - colorConvert = __webpack_require__(229); + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; } - const offset = isBackground ? 10 : 0; - const styles = {}; + if (hasFlag('color=256')) { + return 2; + } - for (const [sourceSpace, suite] of Object.entries(colorConvert)) { - const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; - if (sourceSpace === targetSpace) { - styles[name] = wrap(identity, offset); - } else if (typeof suite === 'object') { - styles[name] = wrap(suite[targetSpace], offset); - } + if (haveStream && !streamIsTTY && forceColor === undefined) { + return 0; } - return styles; -}; + const min = forceColor || 0; -function assembleStyles() { - const codes = new Map(); - const styles = { - modifier: { - reset: [0, 0], - // 21 isn't widely supported and 22 does the same thing - bold: [1, 22], - dim: [2, 22], - italic: [3, 23], - underline: [4, 24], - inverse: [7, 27], - hidden: [8, 28], - strikethrough: [9, 29] - }, - color: { - black: [30, 39], - red: [31, 39], - green: [32, 39], - yellow: [33, 39], - blue: [34, 39], - magenta: [35, 39], - cyan: [36, 39], - white: [37, 39], + if (env.TERM === 'dumb') { + return min; + } - // Bright color - blackBright: [90, 39], - redBright: [91, 39], - greenBright: [92, 39], - yellowBright: [93, 39], - blueBright: [94, 39], - magentaBright: [95, 39], - cyanBright: [96, 39], - whiteBright: [97, 39] - }, - bgColor: { - bgBlack: [40, 49], - bgRed: [41, 49], - bgGreen: [42, 49], - bgYellow: [43, 49], - bgBlue: [44, 49], - bgMagenta: [45, 49], - bgCyan: [46, 49], - bgWhite: [47, 49], + if (process.platform === 'win32') { + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } - // Bright color - bgBlackBright: [100, 49], - bgRedBright: [101, 49], - bgGreenBright: [102, 49], - bgYellowBright: [103, 49], - bgBlueBright: [104, 49], - bgMagentaBright: [105, 49], - bgCyanBright: [106, 49], - bgWhiteBright: [107, 49] + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; } - }; - // Alias bright black as gray (and grey) - styles.color.gray = styles.color.blackBright; - styles.bgColor.bgGray = styles.bgColor.bgBlackBright; - styles.color.grey = styles.color.blackBright; - styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + return min; + } - for (const [groupName, group] of Object.entries(styles)) { - for (const [styleName, style] of Object.entries(group)) { - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } - group[styleName] = styles[styleName]; + if ('GITHUB_ACTIONS' in env) { + return 1; + } - codes.set(style[0], style[1]); + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default } + } - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); + if (/-256(color)?$/i.test(env.TERM)) { + return 2; } - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; + if ('COLORTERM' in env) { + return 1; + } - setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); - setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); - setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); - setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); - setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); - setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + return min; +} - return styles; +function getSupportLevel(stream) { + const level = supportsColor(stream, stream && stream.isTTY); + return translateLevel(level); } -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); +module.exports = { + supportsColor: getSupportLevel, + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) +}; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), /* 229 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(230); -const route = __webpack_require__(231); - -const convert = {}; - -const models = Object.keys(conversions); +"use strict"; -function wrapRaw(fn) { - const wrappedFn = function (...args) { - const arg0 = args[0]; - if (arg0 === undefined || arg0 === null) { - return arg0; - } - if (arg0.length > 1) { - args = arg0; - } +module.exports = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +}; - return fn(args); - }; - // Preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } +/***/ }), +/* 230 */ +/***/ (function(module, exports, __webpack_require__) { - return wrappedFn; -} +"use strict"; -function wrapRounded(fn) { - const wrappedFn = function (...args) { - const arg0 = args[0]; - if (arg0 === undefined || arg0 === null) { - return arg0; - } +const stringReplaceAll = (string, substring, replacer) => { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } - if (arg0.length > 1) { - args = arg0; - } + const substringLength = substring.length; + let endIndex = 0; + let returnValue = ''; + do { + returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; + endIndex = index + substringLength; + index = string.indexOf(substring, endIndex); + } while (index !== -1); - const result = fn(args); + returnValue += string.substr(endIndex); + return returnValue; +}; - // We're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (let len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } +const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { + let endIndex = 0; + let returnValue = ''; + do { + const gotCR = string[index - 1] === '\r'; + returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + endIndex = index + 1; + index = string.indexOf('\n', endIndex); + } while (index !== -1); - return result; - }; + returnValue += string.substr(endIndex); + return returnValue; +}; - // Preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } +module.exports = { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +}; - return wrappedFn; -} -models.forEach(fromModel => { - convert[fromModel] = {}; +/***/ }), +/* 231 */ +/***/ (function(module, exports, __webpack_require__) { - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); +"use strict"; - const routes = route(fromModel); - const routeModels = Object.keys(routes); +const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi; - routeModels.forEach(toModel => { - const fn = routes[toModel]; +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); +function unescape(c) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; -module.exports = convert; + if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); + } + if (u && bracket) { + return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); + } -/***/ }), -/* 230 */ -/***/ (function(module, exports, __webpack_require__) { + return ESCAPES.get(c) || c; +} -/* MIT license */ -/* eslint-disable no-mixed-operators */ -const cssKeywords = __webpack_require__(117); +function parseArguments(name, arguments_) { + const results = []; + const chunks = arguments_.trim().split(/\s*,\s*/g); + let matches; -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) + for (const chunk of chunks) { + const number = Number(chunk); + if (!Number.isNaN(number)) { + results.push(number); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } -const reverseKeywords = {}; -for (const key of Object.keys(cssKeywords)) { - reverseKeywords[cssKeywords[key]] = key; + return results; } -const convert = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} -}; - -module.exports = convert; +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; -// Hide .channels and .labels properties -for (const model of Object.keys(convert)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } + const results = []; + let matches; - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } } - const {channels, labels} = convert[model]; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); + return results; } -convert.rgb.hsl = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const min = Math.min(r, g, b); - const max = Math.max(r, g, b); - const delta = max - min; - let h; - let s; +function buildStyle(chalk, styles) { + const enabled = {}; - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } } - h = Math.min(h * 60, 360); - - if (h < 0) { - h += 360; - } + let current = chalk; + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } - const l = (min + max) / 2; + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; } - return [h, s * 100, l * 100]; -}; + return current; +} -convert.rgb.hsv = function (rgb) { - let rdif; - let gdif; - let bdif; - let h; - let s; +module.exports = (chalk, temporary) => { + const styles = []; + const chunks = []; + let chunk = []; - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const v = Math.max(r, g, b); - const diff = v - Math.min(r, g, b); - const diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; - - if (diff === 0) { - h = 0; - s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); - - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } + // eslint-disable-next-line max-params + temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); + } else if (style) { + const string = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(character); } - } - - return [ - h * 360, - s * 100, - v * 100 - ]; -}; + }); -convert.rgb.hwb = function (rgb) { - const r = rgb[0]; - const g = rgb[1]; - let b = rgb[2]; - const h = convert.rgb.hsl(rgb)[0]; - const w = 1 / 255 * Math.min(r, Math.min(g, b)); + chunks.push(chunk.join('')); - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + if (styles.length > 0) { + const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMessage); + } - return [h, w * 100, b * 100]; + return chunks.join(''); }; -convert.rgb.cmyk = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - - const k = Math.min(1 - r, 1 - g, 1 - b); - const c = (1 - r - k) / (1 - k) || 0; - const m = (1 - g - k) / (1 - k) || 0; - const y = (1 - b - k) / (1 - k) || 0; - - return [c * 100, m * 100, y * 100, k * 100]; -}; -function comparativeDistance(x, y) { - /* - See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - */ - return ( - ((x[0] - y[0]) ** 2) + - ((x[1] - y[1]) ** 2) + - ((x[2] - y[2]) ** 2) - ); -} +/***/ }), +/* 232 */ +/***/ (function(module, exports, __webpack_require__) { -convert.rgb.keyword = function (rgb) { - const reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } +"use strict"; - let currentClosestDistance = Infinity; - let currentClosestKeyword; +const path = __webpack_require__(4); +const childProcess = __webpack_require__(233); +const crossSpawn = __webpack_require__(234); +const stripFinalNewline = __webpack_require__(247); +const npmRunPath = __webpack_require__(248); +const onetime = __webpack_require__(249); +const makeError = __webpack_require__(251); +const normalizeStdio = __webpack_require__(256); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(257); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(258); +const {mergePromise, getSpawnedPromise} = __webpack_require__(265); +const {joinCommand, parseCommand} = __webpack_require__(266); - for (const keyword of Object.keys(cssKeywords)) { - const value = cssKeywords[keyword]; +const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; - // Compute comparative distance - const distance = comparativeDistance(rgb, value); +const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => { + const env = extendEnv ? {...process.env, ...envOption} : envOption; - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } + if (preferLocal) { + return npmRunPath.env({env, cwd: localDir, execPath}); } - return currentClosestKeyword; -}; - -convert.keyword.rgb = function (keyword) { - return cssKeywords[keyword]; + return env; }; -convert.rgb.xyz = function (rgb) { - let r = rgb[0] / 255; - let g = rgb[1] / 255; - let b = rgb[2] / 255; +const handleArgs = (file, args, options = {}) => { + const parsed = crossSpawn._parse(file, args, options); + file = parsed.command; + args = parsed.args; + options = parsed.options; - // Assume sRGB - r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); - g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); - b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); + options = { + maxBuffer: DEFAULT_MAX_BUFFER, + buffer: true, + stripFinalNewline: true, + extendEnv: true, + preferLocal: false, + localDir: options.cwd || process.cwd(), + execPath: process.execPath, + encoding: 'utf8', + reject: true, + cleanup: true, + all: false, + windowsHide: true, + ...options + }; - const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + options.env = getEnv(options); - return [x * 100, y * 100, z * 100]; -}; + options.stdio = normalizeStdio(options); -convert.rgb.lab = function (rgb) { - const xyz = convert.rgb.xyz(rgb); - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; + if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { + // #116 + args.unshift('/q'); + } - x /= 95.047; - y /= 100; - z /= 108.883; + return {file, args, options, parsed}; +}; - x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); +const handleOutput = (options, value, error) => { + if (typeof value !== 'string' && !Buffer.isBuffer(value)) { + // When `execa.sync()` errors, we normalize it to '' to mimic `execa()` + return error === undefined ? undefined : ''; + } - const l = (116 * y) - 16; - const a = 500 * (x - y); - const b = 200 * (y - z); + if (options.stripFinalNewline) { + return stripFinalNewline(value); + } - return [l, a, b]; + return value; }; -convert.hsl.rgb = function (hsl) { - const h = hsl[0] / 360; - const s = hsl[1] / 100; - const l = hsl[2] / 100; - let t2; - let t3; - let val; +const execa = (file, args, options) => { + const parsed = handleArgs(file, args, options); + const command = joinCommand(file, args); - if (s === 0) { - val = l * 255; - return [val, val, val]; + let spawned; + try { + spawned = childProcess.spawn(parsed.file, parsed.args, parsed.options); + } catch (error) { + // Ensure the returned error is always both a promise and a child process + const dummySpawned = new childProcess.ChildProcess(); + const errorPromise = Promise.reject(makeError({ + error, + stdout: '', + stderr: '', + all: '', + command, + parsed, + timedOut: false, + isCanceled: false, + killed: false + })); + return mergePromise(dummySpawned, errorPromise); } - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } + const spawnedPromise = getSpawnedPromise(spawned); + const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise); + const processDone = setExitHandler(spawned, parsed.options, timedPromise); - const t1 = 2 * l - t2; + const context = {isCanceled: false}; - const rgb = [0, 0, 0]; - for (let i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } + spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); + spawned.cancel = spawnedCancel.bind(null, spawned, context); - if (t3 > 1) { - t3--; - } + const handlePromise = async () => { + const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); + const stdout = handleOutput(parsed.options, stdoutResult); + const stderr = handleOutput(parsed.options, stderrResult); + const all = handleOutput(parsed.options, allResult); - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; - } + if (error || exitCode !== 0 || signal !== null) { + const returnedError = makeError({ + error, + exitCode, + signal, + stdout, + stderr, + all, + command, + parsed, + timedOut, + isCanceled: context.isCanceled, + killed: spawned.killed + }); - rgb[i] = val * 255; - } + if (!parsed.options.reject) { + return returnedError; + } - return rgb; -}; + throw returnedError; + } -convert.hsl.hsv = function (hsl) { - const h = hsl[0]; - let s = hsl[1] / 100; - let l = hsl[2] / 100; - let smin = s; - const lmin = Math.max(l, 0.01); + return { + command, + exitCode: 0, + stdout, + stderr, + all, + failed: false, + timedOut: false, + isCanceled: false, + killed: false + }; + }; - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - const v = (l + s) / 2; - const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + const handlePromiseOnce = onetime(handlePromise); - return [h, sv * 100, v * 100]; -}; + crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed); -convert.hsv.rgb = function (hsv) { - const h = hsv[0] / 60; - const s = hsv[1] / 100; - let v = hsv[2] / 100; - const hi = Math.floor(h) % 6; + handleInput(spawned, parsed.options.input); - const f = h - Math.floor(h); - const p = 255 * v * (1 - s); - const q = 255 * v * (1 - (s * f)); - const t = 255 * v * (1 - (s * (1 - f))); - v *= 255; + spawned.all = makeAllStream(spawned, parsed.options); - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } + return mergePromise(spawned, handlePromiseOnce); }; -convert.hsv.hsl = function (hsv) { - const h = hsv[0]; - const s = hsv[1] / 100; - const v = hsv[2] / 100; - const vmin = Math.max(v, 0.01); - let sl; - let l; - - l = (2 - s) * v; - const lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; +module.exports = execa; - return [h, sl * 100, l * 100]; -}; +module.exports.sync = (file, args, options) => { + const parsed = handleArgs(file, args, options); + const command = joinCommand(file, args); -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - const h = hwb[0] / 360; - let wh = hwb[1] / 100; - let bl = hwb[2] / 100; - const ratio = wh + bl; - let f; + validateInputSync(parsed.options); - // Wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; + let result; + try { + result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options); + } catch (error) { + throw makeError({ + error, + stdout: '', + stderr: '', + all: '', + command, + parsed, + timedOut: false, + isCanceled: false, + killed: false + }); } - const i = Math.floor(6 * h); - const v = 1 - bl; - f = 6 * h - i; + const stdout = handleOutput(parsed.options, result.stdout, result.error); + const stderr = handleOutput(parsed.options, result.stderr, result.error); - if ((i & 0x01) !== 0) { - f = 1 - f; - } + if (result.error || result.status !== 0 || result.signal !== null) { + const error = makeError({ + stdout, + stderr, + error: result.error, + signal: result.signal, + exitCode: result.status, + command, + parsed, + timedOut: result.error && result.error.code === 'ETIMEDOUT', + isCanceled: false, + killed: result.signal !== null + }); - const n = wh + f * (v - wh); // Linear interpolation + if (!parsed.options.reject) { + return error; + } - let r; - let g; - let b; - /* eslint-disable max-statements-per-line,no-multi-spaces */ - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; + throw error; } - /* eslint-enable max-statements-per-line,no-multi-spaces */ - return [r * 255, g * 255, b * 255]; + return { + command, + exitCode: 0, + stdout, + stderr, + failed: false, + timedOut: false, + isCanceled: false, + killed: false + }; }; -convert.cmyk.rgb = function (cmyk) { - const c = cmyk[0] / 100; - const m = cmyk[1] / 100; - const y = cmyk[2] / 100; - const k = cmyk[3] / 100; - - const r = 1 - Math.min(1, c * (1 - k) + k); - const g = 1 - Math.min(1, m * (1 - k) + k); - const b = 1 - Math.min(1, y * (1 - k) + k); +module.exports.command = (command, options) => { + const [file, ...args] = parseCommand(command); + return execa(file, args, options); +}; - return [r * 255, g * 255, b * 255]; +module.exports.commandSync = (command, options) => { + const [file, ...args] = parseCommand(command); + return execa.sync(file, args, options); }; -convert.xyz.rgb = function (xyz) { - const x = xyz[0] / 100; - const y = xyz[1] / 100; - const z = xyz[2] / 100; - let r; - let g; - let b; +module.exports.node = (scriptPath, args, options = {}) => { + if (args && !Array.isArray(args) && typeof args === 'object') { + options = args; + args = []; + } - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + const stdio = normalizeStdio.node(options); - // Assume sRGB - r = r > 0.0031308 - ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) - : r * 12.92; + const {nodePath = process.execPath, nodeOptions = process.execArgv} = options; - g = g > 0.0031308 - ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) - : g * 12.92; + return execa( + nodePath, + [ + ...nodeOptions, + scriptPath, + ...(Array.isArray(args) ? args : []) + ], + { + ...options, + stdin: undefined, + stdout: undefined, + stderr: undefined, + stdio, + shell: false + } + ); +}; - b = b > 0.0031308 - ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) - : b * 12.92; - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); +/***/ }), +/* 233 */ +/***/ (function(module, exports) { - return [r * 255, g * 255, b * 255]; -}; +module.exports = require("child_process"); -convert.xyz.lab = function (xyz) { - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; +/***/ }), +/* 234 */ +/***/ (function(module, exports, __webpack_require__) { - x /= 95.047; - y /= 100; - z /= 108.883; +"use strict"; - x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); - const l = (116 * y) - 16; - const a = 500 * (x - y); - const b = 200 * (y - z); - - return [l, a, b]; -}; +const cp = __webpack_require__(233); +const parse = __webpack_require__(235); +const enoent = __webpack_require__(246); -convert.lab.xyz = function (lab) { - const l = lab[0]; - const a = lab[1]; - const b = lab[2]; - let x; - let y; - let z; +function spawn(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; + // Spawn the child process + const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); - const y2 = y ** 3; - const x2 = x ** 3; - const z2 = z ** 3; - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + // Hook into child process "exit" event to emit an error if the command + // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + enoent.hookChildProcess(spawned, parsed); - x *= 95.047; - y *= 100; - z *= 108.883; + return spawned; +} - return [x, y, z]; -}; +function spawnSync(command, args, options) { + // Parse the arguments + const parsed = parse(command, args, options); -convert.lab.lch = function (lab) { - const l = lab[0]; - const a = lab[1]; - const b = lab[2]; - let h; + // Spawn the child process + const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); - const hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; + // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 + result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); - if (h < 0) { - h += 360; - } + return result; +} - const c = Math.sqrt(a * a + b * b); +module.exports = spawn; +module.exports.spawn = spawn; +module.exports.sync = spawnSync; - return [l, c, h]; -}; +module.exports._parse = parse; +module.exports._enoent = enoent; -convert.lch.lab = function (lch) { - const l = lch[0]; - const c = lch[1]; - const h = lch[2]; - const hr = h / 360 * 2 * Math.PI; - const a = c * Math.cos(hr); - const b = c * Math.sin(hr); +/***/ }), +/* 235 */ +/***/ (function(module, exports, __webpack_require__) { - return [l, a, b]; -}; +"use strict"; -convert.rgb.ansi16 = function (args, saturation = null) { - const [r, g, b] = args; - let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization - value = Math.round(value / 50); +const path = __webpack_require__(4); +const resolveCommand = __webpack_require__(236); +const escape = __webpack_require__(242); +const readShebang = __webpack_require__(243); - if (value === 0) { - return 30; - } +const isWin = process.platform === 'win32'; +const isExecutableRegExp = /\.(?:com|exe)$/i; +const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; - let ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); +function detectShebang(parsed) { + parsed.file = resolveCommand(parsed); - if (value === 2) { - ansi += 60; - } + const shebang = parsed.file && readShebang(parsed.file); - return ansi; -}; + if (shebang) { + parsed.args.unshift(parsed.file); + parsed.command = shebang; -convert.hsv.ansi16 = function (args) { - // Optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; + return resolveCommand(parsed); + } -convert.rgb.ansi256 = function (args) { - const r = args[0]; - const g = args[1]; - const b = args[2]; + return parsed.file; +} - // We use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; - } +function parseNonShell(parsed) { + if (!isWin) { + return parsed; + } - if (r > 248) { - return 231; - } + // Detect & add support for shebangs + const commandFile = detectShebang(parsed); - return Math.round(((r - 8) / 247) * 24) + 232; - } + // We don't need a shell if the command filename is an executable + const needsShell = !isExecutableRegExp.test(commandFile); - const ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); + // If a shell is required, use cmd.exe and take care of escaping everything correctly + // Note that `forceShell` is an hidden option used only in tests + if (parsed.options.forceShell || needsShell) { + // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/` + // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument + // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called, + // we need to double escape them + const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); - return ansi; -}; + // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar) + // This is necessary otherwise it will always fail with ENOENT in those cases + parsed.command = path.normalize(parsed.command); -convert.ansi16.rgb = function (args) { - let color = args % 10; + // Escape command & arguments + parsed.command = escape.command(parsed.command); + parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); - // Handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; - } + const shellCommand = [parsed.command].concat(parsed.args).join(' '); - color = color / 10.5 * 255; + parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; + parsed.command = process.env.comspec || 'cmd.exe'; + parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped + } - return [color, color, color]; - } + return parsed; +} - const mult = (~~(args > 50) + 1) * 0.5; - const r = ((color & 1) * mult) * 255; - const g = (((color >> 1) & 1) * mult) * 255; - const b = (((color >> 2) & 1) * mult) * 255; +function parse(command, args, options) { + // Normalize arguments, similar to nodejs + if (args && !Array.isArray(args)) { + options = args; + args = null; + } - return [r, g, b]; -}; + args = args ? args.slice(0) : []; // Clone array to avoid changing the original + options = Object.assign({}, options); // Clone object to avoid changing the original -convert.ansi256.rgb = function (args) { - // Handle greyscale - if (args >= 232) { - const c = (args - 232) * 10 + 8; - return [c, c, c]; - } + // Build our parsed object + const parsed = { + command, + args, + options, + file: undefined, + original: { + command, + args, + }, + }; - args -= 16; + // Delegate further parsing to shell or non-shell + return options.shell ? parsed : parseNonShell(parsed); +} - let rem; - const r = Math.floor(args / 36) / 5 * 255; - const g = Math.floor((rem = args % 36) / 6) / 5 * 255; - const b = (rem % 6) / 5 * 255; +module.exports = parse; - return [r, g, b]; -}; -convert.rgb.hex = function (args) { - const integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); +/***/ }), +/* 236 */ +/***/ (function(module, exports, __webpack_require__) { - const string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; +"use strict"; -convert.hex.rgb = function (args) { - const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; - } - let colorString = match[0]; +const path = __webpack_require__(4); +const which = __webpack_require__(237); +const pathKey = __webpack_require__(241)(); - if (match[0].length === 3) { - colorString = colorString.split('').map(char => { - return char + char; - }).join(''); - } +function resolveCommandAttempt(parsed, withoutPathExt) { + const cwd = process.cwd(); + const hasCustomCwd = parsed.options.cwd != null; + // Worker threads do not have process.chdir() + const shouldSwitchCwd = hasCustomCwd && process.chdir !== undefined; - const integer = parseInt(colorString, 16); - const r = (integer >> 16) & 0xFF; - const g = (integer >> 8) & 0xFF; - const b = integer & 0xFF; + // If a custom `cwd` was specified, we need to change the process cwd + // because `which` will do stat calls but does not support a custom cwd + if (shouldSwitchCwd) { + try { + process.chdir(parsed.options.cwd); + } catch (err) { + /* Empty */ + } + } - return [r, g, b]; -}; + let resolved; -convert.rgb.hcg = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const max = Math.max(Math.max(r, g), b); - const min = Math.min(Math.min(r, g), b); - const chroma = (max - min); - let grayscale; - let hue; + try { + resolved = which.sync(parsed.command, { + path: (parsed.options.env || process.env)[pathKey], + pathExt: withoutPathExt ? path.delimiter : undefined, + }); + } catch (e) { + /* Empty */ + } finally { + if (shouldSwitchCwd) { + process.chdir(cwd); + } + } - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } + // If we successfully resolved, ensure that an absolute path is returned + // Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it + if (resolved) { + resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved); + } - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma; - } + return resolved; +} - hue /= 6; - hue %= 1; +function resolveCommand(parsed) { + return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); +} - return [hue * 360, chroma * 100, grayscale * 100]; -}; +module.exports = resolveCommand; -convert.hsl.hcg = function (hsl) { - const s = hsl[1] / 100; - const l = hsl[2] / 100; - const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); +/***/ }), +/* 237 */ +/***/ (function(module, exports, __webpack_require__) { - let f = 0; - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } +const isWindows = process.platform === 'win32' || + process.env.OSTYPE === 'cygwin' || + process.env.OSTYPE === 'msys' - return [hsl[0], c * 100, f * 100]; -}; +const path = __webpack_require__(4) +const COLON = isWindows ? ';' : ':' +const isexe = __webpack_require__(238) -convert.hsv.hcg = function (hsv) { - const s = hsv[1] / 100; - const v = hsv[2] / 100; +const getNotFoundError = (cmd) => + Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) - const c = s * v; - let f = 0; +const getPathInfo = (cmd, opt) => { + const colon = opt.colon || COLON - if (c < 1.0) { - f = (v - c) / (1 - c); - } + // If it has a slash, then we don't bother searching the pathenv. + // just check the file itself, and that's it. + const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [''] + : ( + [ + // windows always checks the cwd first + ...(isWindows ? [process.cwd()] : []), + ...(opt.path || process.env.PATH || + /* istanbul ignore next: very unusual */ '').split(colon), + ] + ) + const pathExtExe = isWindows + ? opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM' + : '' + const pathExt = isWindows ? pathExtExe.split(colon) : [''] - return [hsv[0], c * 100, f * 100]; -}; + if (isWindows) { + if (cmd.indexOf('.') !== -1 && pathExt[0] !== '') + pathExt.unshift('') + } -convert.hcg.rgb = function (hcg) { - const h = hcg[0] / 360; - const c = hcg[1] / 100; - const g = hcg[2] / 100; + return { + pathEnv, + pathExt, + pathExtExe, + } +} - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } +const which = (cmd, opt, cb) => { + if (typeof opt === 'function') { + cb = opt + opt = {} + } + if (!opt) + opt = {} - const pure = [0, 0, 0]; - const hi = (h % 1) * 6; - const v = hi % 1; - const w = 1 - v; - let mg = 0; + const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) + const found = [] - /* eslint-disable max-statements-per-line */ - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; - } - /* eslint-enable max-statements-per-line */ + const step = i => new Promise((resolve, reject) => { + if (i === pathEnv.length) + return opt.all && found.length ? resolve(found) + : reject(getNotFoundError(cmd)) - mg = (1.0 - c) * g; + const ppRaw = pathEnv[i] + const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; + const pCmd = path.join(pathPart, cmd) + const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd + : pCmd -convert.hcg.hsv = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; + resolve(subStep(p, i, 0)) + }) - const v = c + g * (1.0 - c); - let f = 0; + const subStep = (p, i, ii) => new Promise((resolve, reject) => { + if (ii === pathExt.length) + return resolve(step(i + 1)) + const ext = pathExt[ii] + isexe(p + ext, { pathExt: pathExtExe }, (er, is) => { + if (!er && is) { + if (opt.all) + found.push(p + ext) + else + return resolve(p + ext) + } + return resolve(subStep(p, i, ii + 1)) + }) + }) - if (v > 0.0) { - f = c / v; - } + return cb ? step(0).then(res => cb(null, res), cb) : step(0) +} - return [hcg[0], f * 100, v * 100]; -}; +const whichSync = (cmd, opt) => { + opt = opt || {} -convert.hcg.hsl = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; + const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) + const found = [] - const l = g * (1.0 - c) + 0.5 * c; - let s = 0; + for (let i = 0; i < pathEnv.length; i ++) { + const ppRaw = pathEnv[i] + const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); - } + const pCmd = path.join(pathPart, cmd) + const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd + : pCmd - return [hcg[0], s * 100, l * 100]; -}; + for (let j = 0; j < pathExt.length; j ++) { + const cur = p + pathExt[j] + try { + const is = isexe.sync(cur, { pathExt: pathExtExe }) + if (is) { + if (opt.all) + found.push(cur) + else + return cur + } + } catch (ex) {} + } + } -convert.hcg.hwb = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; - const v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; -}; + if (opt.all && found.length) + return found -convert.hwb.hcg = function (hwb) { - const w = hwb[1] / 100; - const b = hwb[2] / 100; - const v = 1 - b; - const c = v - w; - let g = 0; + if (opt.nothrow) + return null - if (c < 1) { - g = (v - c) / (1 - c); - } + throw getNotFoundError(cmd) +} - return [hwb[0], c * 100, g * 100]; -}; +module.exports = which +which.sync = whichSync -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; - -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; - -convert.gray.hsl = function (args) { - return [0, 0, args[0]]; -}; +/***/ }), +/* 238 */ +/***/ (function(module, exports, __webpack_require__) { -convert.gray.hsv = convert.gray.hsl; +var fs = __webpack_require__(133) +var core +if (process.platform === 'win32' || global.TESTING_WINDOWS) { + core = __webpack_require__(239) +} else { + core = __webpack_require__(240) +} -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; +module.exports = isexe +isexe.sync = sync -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; +function isexe (path, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; + if (!cb) { + if (typeof Promise !== 'function') { + throw new TypeError('callback not provided') + } -convert.gray.hex = function (gray) { - const val = Math.round(gray[0] / 100 * 255) & 0xFF; - const integer = (val << 16) + (val << 8) + val; + return new Promise(function (resolve, reject) { + isexe(path, options || {}, function (er, is) { + if (er) { + reject(er) + } else { + resolve(is) + } + }) + }) + } - const string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; + core(path, options || {}, function (er, is) { + // ignore EACCES because that just means we aren't allowed to run it + if (er) { + if (er.code === 'EACCES' || options && options.ignoreErrors) { + er = null + is = false + } + } + cb(er, is) + }) +} -convert.rgb.gray = function (rgb) { - const val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; -}; +function sync (path, options) { + // my kingdom for a filtered catch + try { + return core.sync(path, options || {}) + } catch (er) { + if (options && options.ignoreErrors || er.code === 'EACCES') { + return false + } else { + throw er + } + } +} /***/ }), -/* 231 */ +/* 239 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(230); +module.exports = isexe +isexe.sync = sync -/* - This function routes a model to all other models. +var fs = __webpack_require__(133) - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). +function checkPathExt (path, options) { + var pathext = options.pathExt !== undefined ? + options.pathExt : process.env.PATHEXT - conversions that are not possible simply are not included. -*/ + if (!pathext) { + return true + } -function buildGraph() { - const graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - const models = Object.keys(conversions); + pathext = pathext.split(';') + if (pathext.indexOf('') !== -1) { + return true + } + for (var i = 0; i < pathext.length; i++) { + var p = pathext[i].toLowerCase() + if (p && path.substr(-p.length).toLowerCase() === p) { + return true + } + } + return false +} - for (let len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; - } +function checkStat (stat, path, options) { + if (!stat.isSymbolicLink() && !stat.isFile()) { + return false + } + return checkPathExt(path, options) +} - return graph; +function isexe (path, options, cb) { + fs.stat(path, function (er, stat) { + cb(er, er ? false : checkStat(stat, path, options)) + }) } -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - const graph = buildGraph(); - const queue = [fromModel]; // Unshift -> queue -> pop +function sync (path, options) { + return checkStat(fs.statSync(path), path, options) +} - graph[fromModel].distance = 0; - while (queue.length) { - const current = queue.pop(); - const adjacents = Object.keys(conversions[current]); +/***/ }), +/* 240 */ +/***/ (function(module, exports, __webpack_require__) { - for (let len = adjacents.length, i = 0; i < len; i++) { - const adjacent = adjacents[i]; - const node = graph[adjacent]; +module.exports = isexe +isexe.sync = sync - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } +var fs = __webpack_require__(133) - return graph; +function isexe (path, options, cb) { + fs.stat(path, function (er, stat) { + cb(er, er ? false : checkStat(stat, options)) + }) } -function link(from, to) { - return function (args) { - return to(from(args)); - }; +function sync (path, options) { + return checkStat(fs.statSync(path), options) } -function wrapConversion(toModel, graph) { - const path = [graph[toModel].parent, toModel]; - let fn = conversions[graph[toModel].parent][toModel]; +function checkStat (stat, options) { + return stat.isFile() && checkMode(stat, options) +} - let cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; - } +function checkMode (stat, options) { + var mod = stat.mode + var uid = stat.uid + var gid = stat.gid - fn.conversion = path; - return fn; + var myUid = options.uid !== undefined ? + options.uid : process.getuid && process.getuid() + var myGid = options.gid !== undefined ? + options.gid : process.getgid && process.getgid() + + var u = parseInt('100', 8) + var g = parseInt('010', 8) + var o = parseInt('001', 8) + var ug = u | g + + var ret = (mod & o) || + (mod & g) && gid === myGid || + (mod & u) && uid === myUid || + (mod & ug) && myUid === 0 + + return ret } -module.exports = function (fromModel) { - const graph = deriveBFS(fromModel); - const conversion = {}; - const models = Object.keys(graph); - for (let len = models.length, i = 0; i < len; i++) { - const toModel = models[i]; - const node = graph[toModel]; +/***/ }), +/* 241 */ +/***/ (function(module, exports, __webpack_require__) { - if (node.parent === null) { - // No possible conversion, or this node is the source model. - continue; - } +"use strict"; - conversion[toModel] = wrapConversion(toModel, graph); + +const pathKey = (options = {}) => { + const environment = options.env || process.env; + const platform = options.platform || process.platform; + + if (platform !== 'win32') { + return 'PATH'; } - return conversion; + return Object.keys(environment).find(key => key.toUpperCase() === 'PATH') || 'Path'; }; +module.exports = pathKey; +// TODO: Remove this for the next major release +module.exports.default = pathKey; /***/ }), -/* 232 */ +/* 242 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__(120); -const tty = __webpack_require__(121); -const hasFlag = __webpack_require__(233); -const {env} = process; +// See http://www.robvanderwoude.com/escapechars.php +const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = 1; -} +function escapeCommand(arg) { + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); -if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } + return arg; } -function translateLevel(level) { - if (level === 0) { - return false; - } +function escapeArgument(arg, doubleEscapeMetaChars) { + // Convert to string + arg = `${arg}`; - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} + // Algorithm below is based on https://qntm.org/cmd -function supportsColor(haveStream, streamIsTTY) { - if (forceColor === 0) { - return 0; - } + // Sequence of backslashes followed by a double quote: + // double up all the backslashes and escape the double quote + arg = arg.replace(/(\\*)"/g, '$1$1\\"'); - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } + // Sequence of backslashes followed by the end of the string + // (which will become a double quote later): + // double up all the backslashes + arg = arg.replace(/(\\*)$/, '$1$1'); - if (hasFlag('color=256')) { - return 2; - } + // All other backslashes occur literally - if (haveStream && !streamIsTTY && forceColor === undefined) { - return 0; - } + // Quote the whole thing: + arg = `"${arg}"`; - const min = forceColor || 0; + // Escape meta chars + arg = arg.replace(metaCharsRegExp, '^$1'); - if (env.TERM === 'dumb') { - return min; - } + // Double escape meta chars if necessary + if (doubleEscapeMetaChars) { + arg = arg.replace(metaCharsRegExp, '^$1'); + } - if (process.platform === 'win32') { - // Windows 10 build 10586 is the first Windows release that supports 256 colors. - // Windows 10 build 14931 is the first release that supports 16m/TrueColor. - const osRelease = os.release().split('.'); - if ( - Number(osRelease[0]) >= 10 && - Number(osRelease[2]) >= 10586 - ) { - return Number(osRelease[2]) >= 14931 ? 3 : 2; - } + return arg; +} - return 1; - } +module.exports.command = escapeCommand; +module.exports.argument = escapeArgument; - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; - } - return min; - } +/***/ }), +/* 243 */ +/***/ (function(module, exports, __webpack_require__) { - if ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; - } +"use strict"; - if ('GITHUB_ACTIONS' in env) { - return 1; - } - if (env.COLORTERM === 'truecolor') { - return 3; - } +const fs = __webpack_require__(133); +const shebangCommand = __webpack_require__(244); - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); +function readShebang(command) { + // Read the first 150 bytes from the file + const size = 150; + const buffer = Buffer.alloc(size); - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default - } - } + let fd; - if (/-256(color)?$/i.test(env.TERM)) { - return 2; - } + try { + fd = fs.openSync(command, 'r'); + fs.readSync(fd, buffer, 0, size, 0); + fs.closeSync(fd); + } catch (e) { /* Empty */ } - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } + // Attempt to extract shebang (null is returned if not a shebang) + return shebangCommand(buffer.toString()); +} - if ('COLORTERM' in env) { - return 1; +module.exports = readShebang; + + +/***/ }), +/* 244 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const shebangRegex = __webpack_require__(245); + +module.exports = (string = '') => { + const match = string.match(shebangRegex); + + if (!match) { + return null; } - return min; -} + const [path, argument] = match[0].replace(/#! ?/, '').split(' '); + const binary = path.split('/').pop(); -function getSupportLevel(stream) { - const level = supportsColor(stream, stream && stream.isTTY); - return translateLevel(level); -} + if (binary === 'env') { + return argument; + } -module.exports = { - supportsColor: getSupportLevel, - stdout: translateLevel(supportsColor(true, tty.isatty(1))), - stderr: translateLevel(supportsColor(true, tty.isatty(2))) + return argument ? `${binary} ${argument}` : binary; }; /***/ }), -/* 233 */ +/* 245 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -module.exports = (flag, argv = process.argv) => { - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const position = argv.indexOf(prefix + flag); - const terminatorPosition = argv.indexOf('--'); - return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); -}; +module.exports = /^#!(.*)/; /***/ }), -/* 234 */ +/* 246 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringReplaceAll = (string, substring, replacer) => { - let index = string.indexOf(substring); - if (index === -1) { - return string; - } +const isWin = process.platform === 'win32'; - const substringLength = substring.length; - let endIndex = 0; - let returnValue = ''; - do { - returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; - endIndex = index + substringLength; - index = string.indexOf(substring, endIndex); - } while (index !== -1); +function notFoundError(original, syscall) { + return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { + code: 'ENOENT', + errno: 'ENOENT', + syscall: `${syscall} ${original.command}`, + path: original.command, + spawnargs: original.args, + }); +} - returnValue += string.substr(endIndex); - return returnValue; -}; +function hookChildProcess(cp, parsed) { + if (!isWin) { + return; + } -const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { - let endIndex = 0; - let returnValue = ''; - do { - const gotCR = string[index - 1] === '\r'; - returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; - endIndex = index + 1; - index = string.indexOf('\n', endIndex); - } while (index !== -1); + const originalEmit = cp.emit; - returnValue += string.substr(endIndex); - return returnValue; -}; + cp.emit = function (name, arg1) { + // If emitting "exit" event and exit code is 1, we need to check if + // the command exists and emit an "error" instead + // See https://github.com/IndigoUnited/node-cross-spawn/issues/16 + if (name === 'exit') { + const err = verifyENOENT(arg1, parsed, 'spawn'); + + if (err) { + return originalEmit.call(cp, 'error', err); + } + } + + return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params + }; +} + +function verifyENOENT(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawn'); + } + + return null; +} + +function verifyENOENTSync(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, 'spawnSync'); + } + + return null; +} module.exports = { - stringReplaceAll, - stringEncaseCRLFWithFirstIndex + hookChildProcess, + verifyENOENT, + verifyENOENTSync, + notFoundError, }; /***/ }), -/* 235 */ +/* 247 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; -const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; -const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; -const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi; - -const ESCAPES = new Map([ - ['n', '\n'], - ['r', '\r'], - ['t', '\t'], - ['b', '\b'], - ['f', '\f'], - ['v', '\v'], - ['0', '\0'], - ['\\', '\\'], - ['e', '\u001B'], - ['a', '\u0007'] -]); - -function unescape(c) { - const u = c[0] === 'u'; - const bracket = c[1] === '{'; - if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { - return String.fromCharCode(parseInt(c.slice(1), 16)); - } +module.exports = input => { + const LF = typeof input === 'string' ? '\n' : '\n'.charCodeAt(); + const CR = typeof input === 'string' ? '\r' : '\r'.charCodeAt(); - if (u && bracket) { - return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); + if (input[input.length - 1] === LF) { + input = input.slice(0, input.length - 1); } - return ESCAPES.get(c) || c; -} - -function parseArguments(name, arguments_) { - const results = []; - const chunks = arguments_.trim().split(/\s*,\s*/g); - let matches; - - for (const chunk of chunks) { - const number = Number(chunk); - if (!Number.isNaN(number)) { - results.push(number); - } else if ((matches = chunk.match(STRING_REGEX))) { - results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character)); - } else { - throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); - } + if (input[input.length - 1] === CR) { + input = input.slice(0, input.length - 1); } - return results; -} - -function parseStyle(style) { - STYLE_REGEX.lastIndex = 0; - - const results = []; - let matches; - - while ((matches = STYLE_REGEX.exec(style)) !== null) { - const name = matches[1]; + return input; +}; - if (matches[2]) { - const args = parseArguments(name, matches[2]); - results.push([name].concat(args)); - } else { - results.push([name]); - } - } - return results; -} +/***/ }), +/* 248 */ +/***/ (function(module, exports, __webpack_require__) { -function buildStyle(chalk, styles) { - const enabled = {}; +"use strict"; - for (const layer of styles) { - for (const style of layer.styles) { - enabled[style[0]] = layer.inverse ? null : style.slice(1); - } - } +const path = __webpack_require__(4); +const pathKey = __webpack_require__(241); - let current = chalk; - for (const [styleName, styles] of Object.entries(enabled)) { - if (!Array.isArray(styles)) { - continue; - } +const npmRunPath = options => { + options = { + cwd: process.cwd(), + path: process.env[pathKey()], + execPath: process.execPath, + ...options + }; - if (!(styleName in current)) { - throw new Error(`Unknown Chalk style: ${styleName}`); - } + let previous; + let cwdPath = path.resolve(options.cwd); + const result = []; - current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; + while (previous !== cwdPath) { + result.push(path.join(cwdPath, 'node_modules/.bin')); + previous = cwdPath; + cwdPath = path.resolve(cwdPath, '..'); } - return current; -} + // Ensure the running `node` binary is used + const execPathDir = path.resolve(options.cwd, options.execPath, '..'); + result.push(execPathDir); -module.exports = (chalk, temporary) => { - const styles = []; - const chunks = []; - let chunk = []; + return result.concat(options.path).join(path.delimiter); +}; - // eslint-disable-next-line max-params - temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { - if (escapeCharacter) { - chunk.push(unescape(escapeCharacter)); - } else if (style) { - const string = chunk.join(''); - chunk = []; - chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); - styles.push({inverse, styles: parseStyle(style)}); - } else if (close) { - if (styles.length === 0) { - throw new Error('Found extraneous } in Chalk template literal'); - } +module.exports = npmRunPath; +// TODO: Remove this for the next major release +module.exports.default = npmRunPath; - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(character); - } - }); +module.exports.env = options => { + options = { + env: process.env, + ...options + }; - chunks.push(chunk.join('')); + const env = {...options.env}; + const path = pathKey({env}); - if (styles.length > 0) { - const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMessage); - } + options.path = env[path]; + env[path] = module.exports(options); - return chunks.join(''); + return env; }; /***/ }), -/* 236 */ +/* 249 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const path = __webpack_require__(4); -const childProcess = __webpack_require__(237); -const crossSpawn = __webpack_require__(238); -const stripFinalNewline = __webpack_require__(251); -const npmRunPath = __webpack_require__(252); -const onetime = __webpack_require__(253); -const makeError = __webpack_require__(255); -const normalizeStdio = __webpack_require__(260); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(261); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(262); -const {mergePromise, getSpawnedPromise} = __webpack_require__(269); -const {joinCommand, parseCommand} = __webpack_require__(270); - -const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; +const mimicFn = __webpack_require__(250); -const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => { - const env = extendEnv ? {...process.env, ...envOption} : envOption; +const calledFunctions = new WeakMap(); - if (preferLocal) { - return npmRunPath.env({env, cwd: localDir, execPath}); +const oneTime = (fn, options = {}) => { + if (typeof fn !== 'function') { + throw new TypeError('Expected a function'); } - return env; -}; - -const handleArgs = (file, args, options = {}) => { - const parsed = crossSpawn._parse(file, args, options); - file = parsed.command; - args = parsed.args; - options = parsed.options; - - options = { - maxBuffer: DEFAULT_MAX_BUFFER, - buffer: true, - stripFinalNewline: true, - extendEnv: true, - preferLocal: false, - localDir: options.cwd || process.cwd(), - execPath: process.execPath, - encoding: 'utf8', - reject: true, - cleanup: true, - all: false, - windowsHide: true, - ...options - }; + let ret; + let isCalled = false; + let callCount = 0; + const functionName = fn.displayName || fn.name || ''; - options.env = getEnv(options); + const onetime = function (...args) { + calledFunctions.set(onetime, ++callCount); - options.stdio = normalizeStdio(options); + if (isCalled) { + if (options.throw === true) { + throw new Error(`Function \`${functionName}\` can only be called once`); + } - if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { - // #116 - args.unshift('/q'); - } + return ret; + } - return {file, args, options, parsed}; -}; + isCalled = true; + ret = fn.apply(this, args); + fn = null; -const handleOutput = (options, value, error) => { - if (typeof value !== 'string' && !Buffer.isBuffer(value)) { - // When `execa.sync()` errors, we normalize it to '' to mimic `execa()` - return error === undefined ? undefined : ''; - } + return ret; + }; - if (options.stripFinalNewline) { - return stripFinalNewline(value); - } + mimicFn(onetime, fn); + calledFunctions.set(onetime, callCount); - return value; + return onetime; }; -const execa = (file, args, options) => { - const parsed = handleArgs(file, args, options); - const command = joinCommand(file, args); +module.exports = oneTime; +// TODO: Remove this for the next major release +module.exports.default = oneTime; - let spawned; - try { - spawned = childProcess.spawn(parsed.file, parsed.args, parsed.options); - } catch (error) { - // Ensure the returned error is always both a promise and a child process - const dummySpawned = new childProcess.ChildProcess(); - const errorPromise = Promise.reject(makeError({ - error, - stdout: '', - stderr: '', - all: '', - command, - parsed, - timedOut: false, - isCanceled: false, - killed: false - })); - return mergePromise(dummySpawned, errorPromise); +module.exports.callCount = fn => { + if (!calledFunctions.has(fn)) { + throw new Error(`The given function \`${fn.name}\` is not wrapped by the \`onetime\` package`); } - const spawnedPromise = getSpawnedPromise(spawned); - const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise); - const processDone = setExitHandler(spawned, parsed.options, timedPromise); - - const context = {isCanceled: false}; + return calledFunctions.get(fn); +}; - spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); - spawned.cancel = spawnedCancel.bind(null, spawned, context); - const handlePromise = async () => { - const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); - const stdout = handleOutput(parsed.options, stdoutResult); - const stderr = handleOutput(parsed.options, stderrResult); - const all = handleOutput(parsed.options, allResult); +/***/ }), +/* 250 */ +/***/ (function(module, exports, __webpack_require__) { - if (error || exitCode !== 0 || signal !== null) { - const returnedError = makeError({ - error, - exitCode, - signal, - stdout, - stderr, - all, - command, - parsed, - timedOut, - isCanceled: context.isCanceled, - killed: spawned.killed - }); +"use strict"; - if (!parsed.options.reject) { - return returnedError; - } - throw returnedError; - } +const mimicFn = (to, from) => { + for (const prop of Reflect.ownKeys(from)) { + Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop)); + } - return { - command, - exitCode: 0, - stdout, - stderr, - all, - failed: false, - timedOut: false, - isCanceled: false, - killed: false - }; - }; + return to; +}; - const handlePromiseOnce = onetime(handlePromise); +module.exports = mimicFn; +// TODO: Remove this for the next major release +module.exports.default = mimicFn; - crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed); - handleInput(spawned, parsed.options.input); +/***/ }), +/* 251 */ +/***/ (function(module, exports, __webpack_require__) { - spawned.all = makeAllStream(spawned, parsed.options); +"use strict"; - return mergePromise(spawned, handlePromiseOnce); -}; +const {signalsByName} = __webpack_require__(252); -module.exports = execa; +const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { + if (timedOut) { + return `timed out after ${timeout} milliseconds`; + } -module.exports.sync = (file, args, options) => { - const parsed = handleArgs(file, args, options); - const command = joinCommand(file, args); + if (isCanceled) { + return 'was canceled'; + } - validateInputSync(parsed.options); + if (errorCode !== undefined) { + return `failed with ${errorCode}`; + } - let result; - try { - result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options); - } catch (error) { - throw makeError({ - error, - stdout: '', - stderr: '', - all: '', - command, - parsed, - timedOut: false, - isCanceled: false, - killed: false - }); + if (signal !== undefined) { + return `was killed with ${signal} (${signalDescription})`; } - const stdout = handleOutput(parsed.options, result.stdout, result.error); - const stderr = handleOutput(parsed.options, result.stderr, result.error); + if (exitCode !== undefined) { + return `failed with exit code ${exitCode}`; + } - if (result.error || result.status !== 0 || result.signal !== null) { - const error = makeError({ - stdout, - stderr, - error: result.error, - signal: result.signal, - exitCode: result.status, - command, - parsed, - timedOut: result.error && result.error.code === 'ETIMEDOUT', - isCanceled: false, - killed: result.signal !== null - }); + return 'failed'; +}; - if (!parsed.options.reject) { - return error; - } +const makeError = ({ + stdout, + stderr, + all, + error, + signal, + exitCode, + command, + timedOut, + isCanceled, + killed, + parsed: {options: {timeout}} +}) => { + // `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`. + // We normalize them to `undefined` + exitCode = exitCode === null ? undefined : exitCode; + signal = signal === null ? undefined : signal; + const signalDescription = signal === undefined ? undefined : signalsByName[signal].description; - throw error; - } + const errorCode = error && error.code; - return { - command, - exitCode: 0, - stdout, - stderr, - failed: false, - timedOut: false, - isCanceled: false, - killed: false - }; -}; + const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}); + const execaMessage = `Command ${prefix}: ${command}`; + const isError = Object.prototype.toString.call(error) === '[object Error]'; + const shortMessage = isError ? `${execaMessage}\n${error.message}` : execaMessage; + const message = [shortMessage, stderr, stdout].filter(Boolean).join('\n'); -module.exports.command = (command, options) => { - const [file, ...args] = parseCommand(command); - return execa(file, args, options); -}; + if (isError) { + error.originalMessage = error.message; + error.message = message; + } else { + error = new Error(message); + } -module.exports.commandSync = (command, options) => { - const [file, ...args] = parseCommand(command); - return execa.sync(file, args, options); -}; + error.shortMessage = shortMessage; + error.command = command; + error.exitCode = exitCode; + error.signal = signal; + error.signalDescription = signalDescription; + error.stdout = stdout; + error.stderr = stderr; -module.exports.node = (scriptPath, args, options = {}) => { - if (args && !Array.isArray(args) && typeof args === 'object') { - options = args; - args = []; + if (all !== undefined) { + error.all = all; } - const stdio = normalizeStdio.node(options); + if ('bufferedData' in error) { + delete error.bufferedData; + } - const {nodePath = process.execPath, nodeOptions = process.execArgv} = options; + error.failed = true; + error.timedOut = Boolean(timedOut); + error.isCanceled = isCanceled; + error.killed = killed && !timedOut; - return execa( - nodePath, - [ - ...nodeOptions, - scriptPath, - ...(Array.isArray(args) ? args : []) - ], - { - ...options, - stdin: undefined, - stdout: undefined, - stderr: undefined, - stdio, - shell: false - } - ); + return error; }; +module.exports = makeError; -/***/ }), -/* 237 */ -/***/ (function(module, exports) { - -module.exports = require("child_process"); /***/ }), -/* 238 */ +/* 252 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(120); +var _signals=__webpack_require__(253); +var _realtime=__webpack_require__(255); -const cp = __webpack_require__(237); -const parse = __webpack_require__(239); -const enoent = __webpack_require__(250); -function spawn(command, args, options) { - // Parse the arguments - const parsed = parse(command, args, options); - // Spawn the child process - const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); +const getSignalsByName=function(){ +const signals=(0,_signals.getSignals)(); +return signals.reduce(getSignalByName,{}); +}; - // Hook into child process "exit" event to emit an error if the command - // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 - enoent.hookChildProcess(spawned, parsed); +const getSignalByName=function( +signalByNameMemo, +{name,number,description,supported,action,forced,standard}) +{ +return{ +...signalByNameMemo, +[name]:{name,number,description,supported,action,forced,standard}}; - return spawned; -} +}; -function spawnSync(command, args, options) { - // Parse the arguments - const parsed = parse(command, args, options); +const signalsByName=getSignalsByName();exports.signalsByName=signalsByName; - // Spawn the child process - const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); - // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 - result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); - return result; -} - -module.exports = spawn; -module.exports.spawn = spawn; -module.exports.sync = spawnSync; - -module.exports._parse = parse; -module.exports._enoent = enoent; +const getSignalsByNumber=function(){ +const signals=(0,_signals.getSignals)(); +const length=_realtime.SIGRTMAX+1; +const signalsA=Array.from({length},(value,number)=> +getSignalByNumber(number,signals)); -/***/ }), -/* 239 */ -/***/ (function(module, exports, __webpack_require__) { +return Object.assign({},...signalsA); +}; -"use strict"; +const getSignalByNumber=function(number,signals){ +const signal=findSignalByNumber(number,signals); +if(signal===undefined){ +return{}; +} -const path = __webpack_require__(4); -const resolveCommand = __webpack_require__(240); -const escape = __webpack_require__(246); -const readShebang = __webpack_require__(247); +const{name,description,supported,action,forced,standard}=signal; +return{ +[number]:{ +name, +number, +description, +supported, +action, +forced, +standard}}; -const isWin = process.platform === 'win32'; -const isExecutableRegExp = /\.(?:com|exe)$/i; -const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; -function detectShebang(parsed) { - parsed.file = resolveCommand(parsed); +}; - const shebang = parsed.file && readShebang(parsed.file); - if (shebang) { - parsed.args.unshift(parsed.file); - parsed.command = shebang; - return resolveCommand(parsed); - } +const findSignalByNumber=function(number,signals){ +const signal=signals.find(({name})=>_os.constants.signals[name]===number); - return parsed.file; +if(signal!==undefined){ +return signal; } -function parseNonShell(parsed) { - if (!isWin) { - return parsed; - } +return signals.find(signalA=>signalA.number===number); +}; - // Detect & add support for shebangs - const commandFile = detectShebang(parsed); +const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumber; +//# sourceMappingURL=main.js.map - // We don't need a shell if the command filename is an executable - const needsShell = !isExecutableRegExp.test(commandFile); +/***/ }), +/* 253 */ +/***/ (function(module, exports, __webpack_require__) { - // If a shell is required, use cmd.exe and take care of escaping everything correctly - // Note that `forceShell` is an hidden option used only in tests - if (parsed.options.forceShell || needsShell) { - // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/` - // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument - // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called, - // we need to double escape them - const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); +"use strict"; +Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(120); - // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar) - // This is necessary otherwise it will always fail with ENOENT in those cases - parsed.command = path.normalize(parsed.command); +var _core=__webpack_require__(254); +var _realtime=__webpack_require__(255); - // Escape command & arguments - parsed.command = escape.command(parsed.command); - parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); - const shellCommand = [parsed.command].concat(parsed.args).join(' '); - parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`]; - parsed.command = process.env.comspec || 'cmd.exe'; - parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped - } +const getSignals=function(){ +const realtimeSignals=(0,_realtime.getRealtimeSignals)(); +const signals=[..._core.SIGNALS,...realtimeSignals].map(normalizeSignal); +return signals; +};exports.getSignals=getSignals; - return parsed; -} -function parse(command, args, options) { - // Normalize arguments, similar to nodejs - if (args && !Array.isArray(args)) { - options = args; - args = null; - } - args = args ? args.slice(0) : []; // Clone array to avoid changing the original - options = Object.assign({}, options); // Clone object to avoid changing the original - // Build our parsed object - const parsed = { - command, - args, - options, - file: undefined, - original: { - command, - args, - }, - }; - // Delegate further parsing to shell or non-shell - return options.shell ? parsed : parseNonShell(parsed); -} -module.exports = parse; +const normalizeSignal=function({ +name, +number:defaultNumber, +description, +action, +forced=false, +standard}) +{ +const{ +signals:{[name]:constantSignal}}= +_os.constants; +const supported=constantSignal!==undefined; +const number=supported?constantSignal:defaultNumber; +return{name,number,description,supported,action,forced,standard}; +}; +//# sourceMappingURL=signals.js.map /***/ }), -/* 240 */ +/* 254 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +Object.defineProperty(exports,"__esModule",{value:true});exports.SIGNALS=void 0; +const SIGNALS=[ +{ +name:"SIGHUP", +number:1, +action:"terminate", +description:"Terminal closed", +standard:"posix"}, -const path = __webpack_require__(4); -const which = __webpack_require__(241); -const pathKey = __webpack_require__(245)(); +{ +name:"SIGINT", +number:2, +action:"terminate", +description:"User interruption with CTRL-C", +standard:"ansi"}, -function resolveCommandAttempt(parsed, withoutPathExt) { - const cwd = process.cwd(); - const hasCustomCwd = parsed.options.cwd != null; - // Worker threads do not have process.chdir() - const shouldSwitchCwd = hasCustomCwd && process.chdir !== undefined; +{ +name:"SIGQUIT", +number:3, +action:"core", +description:"User interruption with CTRL-\\", +standard:"posix"}, - // If a custom `cwd` was specified, we need to change the process cwd - // because `which` will do stat calls but does not support a custom cwd - if (shouldSwitchCwd) { - try { - process.chdir(parsed.options.cwd); - } catch (err) { - /* Empty */ - } - } +{ +name:"SIGILL", +number:4, +action:"core", +description:"Invalid machine instruction", +standard:"ansi"}, - let resolved; +{ +name:"SIGTRAP", +number:5, +action:"core", +description:"Debugger breakpoint", +standard:"posix"}, - try { - resolved = which.sync(parsed.command, { - path: (parsed.options.env || process.env)[pathKey], - pathExt: withoutPathExt ? path.delimiter : undefined, - }); - } catch (e) { - /* Empty */ - } finally { - if (shouldSwitchCwd) { - process.chdir(cwd); - } - } +{ +name:"SIGABRT", +number:6, +action:"core", +description:"Aborted", +standard:"ansi"}, - // If we successfully resolved, ensure that an absolute path is returned - // Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it - if (resolved) { - resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved); - } +{ +name:"SIGIOT", +number:6, +action:"core", +description:"Aborted", +standard:"bsd"}, - return resolved; -} +{ +name:"SIGBUS", +number:7, +action:"core", +description: +"Bus error due to misaligned, non-existing address or paging error", +standard:"bsd"}, -function resolveCommand(parsed) { - return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); -} +{ +name:"SIGEMT", +number:7, +action:"terminate", +description:"Command should be emulated but is not implemented", +standard:"other"}, -module.exports = resolveCommand; +{ +name:"SIGFPE", +number:8, +action:"core", +description:"Floating point arithmetic error", +standard:"ansi"}, +{ +name:"SIGKILL", +number:9, +action:"terminate", +description:"Forced termination", +standard:"posix", +forced:true}, -/***/ }), -/* 241 */ -/***/ (function(module, exports, __webpack_require__) { +{ +name:"SIGUSR1", +number:10, +action:"terminate", +description:"Application-specific signal", +standard:"posix"}, -const isWindows = process.platform === 'win32' || - process.env.OSTYPE === 'cygwin' || - process.env.OSTYPE === 'msys' +{ +name:"SIGSEGV", +number:11, +action:"core", +description:"Segmentation fault", +standard:"ansi"}, -const path = __webpack_require__(4) -const COLON = isWindows ? ';' : ':' -const isexe = __webpack_require__(242) +{ +name:"SIGUSR2", +number:12, +action:"terminate", +description:"Application-specific signal", +standard:"posix"}, -const getNotFoundError = (cmd) => - Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) +{ +name:"SIGPIPE", +number:13, +action:"terminate", +description:"Broken pipe or socket", +standard:"posix"}, -const getPathInfo = (cmd, opt) => { - const colon = opt.colon || COLON +{ +name:"SIGALRM", +number:14, +action:"terminate", +description:"Timeout or timer", +standard:"posix"}, - // If it has a slash, then we don't bother searching the pathenv. - // just check the file itself, and that's it. - const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [''] - : ( - [ - // windows always checks the cwd first - ...(isWindows ? [process.cwd()] : []), - ...(opt.path || process.env.PATH || - /* istanbul ignore next: very unusual */ '').split(colon), - ] - ) - const pathExtExe = isWindows - ? opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM' - : '' - const pathExt = isWindows ? pathExtExe.split(colon) : [''] +{ +name:"SIGTERM", +number:15, +action:"terminate", +description:"Termination", +standard:"ansi"}, - if (isWindows) { - if (cmd.indexOf('.') !== -1 && pathExt[0] !== '') - pathExt.unshift('') - } +{ +name:"SIGSTKFLT", +number:16, +action:"terminate", +description:"Stack is empty or overflowed", +standard:"other"}, - return { - pathEnv, - pathExt, - pathExtExe, - } -} +{ +name:"SIGCHLD", +number:17, +action:"ignore", +description:"Child process terminated, paused or unpaused", +standard:"posix"}, -const which = (cmd, opt, cb) => { - if (typeof opt === 'function') { - cb = opt - opt = {} - } - if (!opt) - opt = {} +{ +name:"SIGCLD", +number:17, +action:"ignore", +description:"Child process terminated, paused or unpaused", +standard:"other"}, - const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) - const found = [] +{ +name:"SIGCONT", +number:18, +action:"unpause", +description:"Unpaused", +standard:"posix", +forced:true}, - const step = i => new Promise((resolve, reject) => { - if (i === pathEnv.length) - return opt.all && found.length ? resolve(found) - : reject(getNotFoundError(cmd)) +{ +name:"SIGSTOP", +number:19, +action:"pause", +description:"Paused", +standard:"posix", +forced:true}, - const ppRaw = pathEnv[i] - const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw +{ +name:"SIGTSTP", +number:20, +action:"pause", +description:"Paused using CTRL-Z or \"suspend\"", +standard:"posix"}, - const pCmd = path.join(pathPart, cmd) - const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd - : pCmd +{ +name:"SIGTTIN", +number:21, +action:"pause", +description:"Background process cannot read terminal input", +standard:"posix"}, - resolve(subStep(p, i, 0)) - }) +{ +name:"SIGBREAK", +number:21, +action:"terminate", +description:"User interruption with CTRL-BREAK", +standard:"other"}, - const subStep = (p, i, ii) => new Promise((resolve, reject) => { - if (ii === pathExt.length) - return resolve(step(i + 1)) - const ext = pathExt[ii] - isexe(p + ext, { pathExt: pathExtExe }, (er, is) => { - if (!er && is) { - if (opt.all) - found.push(p + ext) - else - return resolve(p + ext) - } - return resolve(subStep(p, i, ii + 1)) - }) - }) +{ +name:"SIGTTOU", +number:22, +action:"pause", +description:"Background process cannot write to terminal output", +standard:"posix"}, - return cb ? step(0).then(res => cb(null, res), cb) : step(0) -} +{ +name:"SIGURG", +number:23, +action:"ignore", +description:"Socket received out-of-band data", +standard:"bsd"}, -const whichSync = (cmd, opt) => { - opt = opt || {} +{ +name:"SIGXCPU", +number:24, +action:"core", +description:"Process timed out", +standard:"bsd"}, - const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) - const found = [] +{ +name:"SIGXFSZ", +number:25, +action:"core", +description:"File too big", +standard:"bsd"}, - for (let i = 0; i < pathEnv.length; i ++) { - const ppRaw = pathEnv[i] - const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw +{ +name:"SIGVTALRM", +number:26, +action:"terminate", +description:"Timeout or timer", +standard:"bsd"}, - const pCmd = path.join(pathPart, cmd) - const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd - : pCmd +{ +name:"SIGPROF", +number:27, +action:"terminate", +description:"Timeout or timer", +standard:"bsd"}, - for (let j = 0; j < pathExt.length; j ++) { - const cur = p + pathExt[j] - try { - const is = isexe.sync(cur, { pathExt: pathExtExe }) - if (is) { - if (opt.all) - found.push(cur) - else - return cur - } - } catch (ex) {} - } - } +{ +name:"SIGWINCH", +number:28, +action:"ignore", +description:"Terminal window size changed", +standard:"bsd"}, - if (opt.all && found.length) - return found +{ +name:"SIGIO", +number:29, +action:"terminate", +description:"I/O is available", +standard:"other"}, - if (opt.nothrow) - return null +{ +name:"SIGPOLL", +number:29, +action:"terminate", +description:"Watched event", +standard:"other"}, - throw getNotFoundError(cmd) -} +{ +name:"SIGINFO", +number:29, +action:"ignore", +description:"Request for process information", +standard:"other"}, -module.exports = which -which.sync = whichSync +{ +name:"SIGPWR", +number:30, +action:"terminate", +description:"Device running out of power", +standard:"systemv"}, + +{ +name:"SIGSYS", +number:31, +action:"core", +description:"Invalid system call", +standard:"other"}, +{ +name:"SIGUNUSED", +number:31, +action:"terminate", +description:"Invalid system call", +standard:"other"}];exports.SIGNALS=SIGNALS; +//# sourceMappingURL=core.js.map /***/ }), -/* 242 */ +/* 255 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(133) -var core -if (process.platform === 'win32' || global.TESTING_WINDOWS) { - core = __webpack_require__(243) -} else { - core = __webpack_require__(244) -} - -module.exports = isexe -isexe.sync = sync - -function isexe (path, options, cb) { - if (typeof options === 'function') { - cb = options - options = {} - } - - if (!cb) { - if (typeof Promise !== 'function') { - throw new TypeError('callback not provided') - } - - return new Promise(function (resolve, reject) { - isexe(path, options || {}, function (er, is) { - if (er) { - reject(er) - } else { - resolve(is) - } - }) - }) - } +"use strict"; +Object.defineProperty(exports,"__esModule",{value:true});exports.SIGRTMAX=exports.getRealtimeSignals=void 0; +const getRealtimeSignals=function(){ +const length=SIGRTMAX-SIGRTMIN+1; +return Array.from({length},getRealtimeSignal); +};exports.getRealtimeSignals=getRealtimeSignals; - core(path, options || {}, function (er, is) { - // ignore EACCES because that just means we aren't allowed to run it - if (er) { - if (er.code === 'EACCES' || options && options.ignoreErrors) { - er = null - is = false - } - } - cb(er, is) - }) -} +const getRealtimeSignal=function(value,index){ +return{ +name:`SIGRT${index+1}`, +number:SIGRTMIN+index, +action:"terminate", +description:"Application-specific signal (realtime)", +standard:"posix"}; -function sync (path, options) { - // my kingdom for a filtered catch - try { - return core.sync(path, options || {}) - } catch (er) { - if (options && options.ignoreErrors || er.code === 'EACCES') { - return false - } else { - throw er - } - } -} +}; +const SIGRTMIN=34; +const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; +//# sourceMappingURL=realtime.js.map /***/ }), -/* 243 */ +/* 256 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = isexe -isexe.sync = sync - -var fs = __webpack_require__(133) - -function checkPathExt (path, options) { - var pathext = options.pathExt !== undefined ? - options.pathExt : process.env.PATHEXT - - if (!pathext) { - return true - } - - pathext = pathext.split(';') - if (pathext.indexOf('') !== -1) { - return true - } - for (var i = 0; i < pathext.length; i++) { - var p = pathext[i].toLowerCase() - if (p && path.substr(-p.length).toLowerCase() === p) { - return true - } - } - return false -} +"use strict"; -function checkStat (stat, path, options) { - if (!stat.isSymbolicLink() && !stat.isFile()) { - return false - } - return checkPathExt(path, options) -} +const aliases = ['stdin', 'stdout', 'stderr']; -function isexe (path, options, cb) { - fs.stat(path, function (er, stat) { - cb(er, er ? false : checkStat(stat, path, options)) - }) -} +const hasAlias = opts => aliases.some(alias => opts[alias] !== undefined); -function sync (path, options) { - return checkStat(fs.statSync(path), path, options) -} +const normalizeStdio = opts => { + if (!opts) { + return; + } + const {stdio} = opts; -/***/ }), -/* 244 */ -/***/ (function(module, exports, __webpack_require__) { + if (stdio === undefined) { + return aliases.map(alias => opts[alias]); + } -module.exports = isexe -isexe.sync = sync + if (hasAlias(opts)) { + throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`); + } -var fs = __webpack_require__(133) + if (typeof stdio === 'string') { + return stdio; + } -function isexe (path, options, cb) { - fs.stat(path, function (er, stat) { - cb(er, er ? false : checkStat(stat, options)) - }) -} + if (!Array.isArray(stdio)) { + throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); + } -function sync (path, options) { - return checkStat(fs.statSync(path), options) -} + const length = Math.max(stdio.length, aliases.length); + return Array.from({length}, (value, index) => stdio[index]); +}; -function checkStat (stat, options) { - return stat.isFile() && checkMode(stat, options) -} +module.exports = normalizeStdio; -function checkMode (stat, options) { - var mod = stat.mode - var uid = stat.uid - var gid = stat.gid +// `ipc` is pushed unless it is already present +module.exports.node = opts => { + const stdio = normalizeStdio(opts); - var myUid = options.uid !== undefined ? - options.uid : process.getuid && process.getuid() - var myGid = options.gid !== undefined ? - options.gid : process.getgid && process.getgid() + if (stdio === 'ipc') { + return 'ipc'; + } - var u = parseInt('100', 8) - var g = parseInt('010', 8) - var o = parseInt('001', 8) - var ug = u | g + if (stdio === undefined || typeof stdio === 'string') { + return [stdio, stdio, stdio, 'ipc']; + } - var ret = (mod & o) || - (mod & g) && gid === myGid || - (mod & u) && uid === myUid || - (mod & ug) && myUid === 0 + if (stdio.includes('ipc')) { + return stdio; + } - return ret -} + return [...stdio, 'ipc']; +}; /***/ }), -/* 245 */ +/* 257 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const os = __webpack_require__(120); +const onExit = __webpack_require__(217); -const pathKey = (options = {}) => { - const environment = options.env || process.env; - const platform = options.platform || process.platform; +const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; - if (platform !== 'win32') { - return 'PATH'; +// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior +const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => { + const killResult = kill(signal); + setKillTimeout(kill, signal, options, killResult); + return killResult; +}; + +const setKillTimeout = (kill, signal, options, killResult) => { + if (!shouldForceKill(signal, options, killResult)) { + return; } - return Object.keys(environment).find(key => key.toUpperCase() === 'PATH') || 'Path'; -}; + const timeout = getForceKillAfterTimeout(options); + const t = setTimeout(() => { + kill('SIGKILL'); + }, timeout); -module.exports = pathKey; -// TODO: Remove this for the next major release -module.exports.default = pathKey; + // Guarded because there's no `.unref()` when `execa` is used in the renderer + // process in Electron. This cannot be tested since we don't run tests in + // Electron. + // istanbul ignore else + if (t.unref) { + t.unref(); + } +}; +const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => { + return isSigterm(signal) && forceKillAfterTimeout !== false && killResult; +}; -/***/ }), -/* 246 */ -/***/ (function(module, exports, __webpack_require__) { +const isSigterm = signal => { + return signal === os.constants.signals.SIGTERM || + (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM'); +}; -"use strict"; +const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => { + if (forceKillAfterTimeout === true) { + return DEFAULT_FORCE_KILL_TIMEOUT; + } + if (!Number.isInteger(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { + throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); + } -// See http://www.robvanderwoude.com/escapechars.php -const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; + return forceKillAfterTimeout; +}; -function escapeCommand(arg) { - // Escape meta chars - arg = arg.replace(metaCharsRegExp, '^$1'); +// `childProcess.cancel()` +const spawnedCancel = (spawned, context) => { + const killResult = spawned.kill(); - return arg; -} + if (killResult) { + context.isCanceled = true; + } +}; -function escapeArgument(arg, doubleEscapeMetaChars) { - // Convert to string - arg = `${arg}`; +const timeoutKill = (spawned, signal, reject) => { + spawned.kill(signal); + reject(Object.assign(new Error('Timed out'), {timedOut: true, signal})); +}; - // Algorithm below is based on https://qntm.org/cmd +// `timeout` option handling +const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => { + if (timeout === 0 || timeout === undefined) { + return spawnedPromise; + } - // Sequence of backslashes followed by a double quote: - // double up all the backslashes and escape the double quote - arg = arg.replace(/(\\*)"/g, '$1$1\\"'); + if (!Number.isInteger(timeout) || timeout < 0) { + throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); + } - // Sequence of backslashes followed by the end of the string - // (which will become a double quote later): - // double up all the backslashes - arg = arg.replace(/(\\*)$/, '$1$1'); + let timeoutId; + const timeoutPromise = new Promise((resolve, reject) => { + timeoutId = setTimeout(() => { + timeoutKill(spawned, killSignal, reject); + }, timeout); + }); - // All other backslashes occur literally + const safeSpawnedPromise = spawnedPromise.finally(() => { + clearTimeout(timeoutId); + }); - // Quote the whole thing: - arg = `"${arg}"`; + return Promise.race([timeoutPromise, safeSpawnedPromise]); +}; - // Escape meta chars - arg = arg.replace(metaCharsRegExp, '^$1'); +// `cleanup` option handling +const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => { + if (!cleanup || detached) { + return timedPromise; + } - // Double escape meta chars if necessary - if (doubleEscapeMetaChars) { - arg = arg.replace(metaCharsRegExp, '^$1'); - } + const removeExitHandler = onExit(() => { + spawned.kill(); + }); - return arg; -} + return timedPromise.finally(() => { + removeExitHandler(); + }); +}; -module.exports.command = escapeCommand; -module.exports.argument = escapeArgument; +module.exports = { + spawnedKill, + spawnedCancel, + setupTimeout, + setExitHandler +}; /***/ }), -/* 247 */ +/* 258 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const isStream = __webpack_require__(259); +const getStream = __webpack_require__(260); +const mergeStream = __webpack_require__(264); -const fs = __webpack_require__(133); -const shebangCommand = __webpack_require__(248); - -function readShebang(command) { - // Read the first 150 bytes from the file - const size = 150; - const buffer = Buffer.alloc(size); - - let fd; +// `input` option +const handleInput = (spawned, input) => { + // Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 + // TODO: Remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 + if (input === undefined || spawned.stdin === undefined) { + return; + } - try { - fd = fs.openSync(command, 'r'); - fs.readSync(fd, buffer, 0, size, 0); - fs.closeSync(fd); - } catch (e) { /* Empty */ } + if (isStream(input)) { + input.pipe(spawned.stdin); + } else { + spawned.stdin.end(input); + } +}; - // Attempt to extract shebang (null is returned if not a shebang) - return shebangCommand(buffer.toString()); -} +// `all` interleaves `stdout` and `stderr` +const makeAllStream = (spawned, {all}) => { + if (!all || (!spawned.stdout && !spawned.stderr)) { + return; + } -module.exports = readShebang; + const mixed = mergeStream(); + if (spawned.stdout) { + mixed.add(spawned.stdout); + } -/***/ }), -/* 248 */ -/***/ (function(module, exports, __webpack_require__) { + if (spawned.stderr) { + mixed.add(spawned.stderr); + } -"use strict"; + return mixed; +}; -const shebangRegex = __webpack_require__(249); +// On failure, `result.stdout|stderr|all` should contain the currently buffered stream +const getBufferedData = async (stream, streamPromise) => { + if (!stream) { + return; + } -module.exports = (string = '') => { - const match = string.match(shebangRegex); + stream.destroy(); - if (!match) { - return null; + try { + return await streamPromise; + } catch (error) { + return error.bufferedData; } +}; - const [path, argument] = match[0].replace(/#! ?/, '').split(' '); - const binary = path.split('/').pop(); +const getStreamPromise = (stream, {encoding, buffer, maxBuffer}) => { + if (!stream || !buffer) { + return; + } - if (binary === 'env') { - return argument; + if (encoding) { + return getStream(stream, {encoding, maxBuffer}); } - return argument ? `${binary} ${argument}` : binary; + return getStream.buffer(stream, {maxBuffer}); }; +// Retrieve result of child process: exit code, signal, error, streams (stdout/stderr/all) +const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => { + const stdoutPromise = getStreamPromise(stdout, {encoding, buffer, maxBuffer}); + const stderrPromise = getStreamPromise(stderr, {encoding, buffer, maxBuffer}); + const allPromise = getStreamPromise(all, {encoding, buffer, maxBuffer: maxBuffer * 2}); -/***/ }), -/* 249 */ -/***/ (function(module, exports, __webpack_require__) { + try { + return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]); + } catch (error) { + return Promise.all([ + {error, signal: error.signal, timedOut: error.timedOut}, + getBufferedData(stdout, stdoutPromise), + getBufferedData(stderr, stderrPromise), + getBufferedData(all, allPromise) + ]); + } +}; -"use strict"; +const validateInputSync = ({input}) => { + if (isStream(input)) { + throw new TypeError('The `input` option cannot be a stream in sync mode'); + } +}; + +module.exports = { + handleInput, + makeAllStream, + getSpawnedResult, + validateInputSync +}; -module.exports = /^#!(.*)/; /***/ }), -/* 250 */ +/* 259 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isWin = process.platform === 'win32'; - -function notFoundError(original, syscall) { - return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { - code: 'ENOENT', - errno: 'ENOENT', - syscall: `${syscall} ${original.command}`, - path: original.command, - spawnargs: original.args, - }); -} - -function hookChildProcess(cp, parsed) { - if (!isWin) { - return; - } - - const originalEmit = cp.emit; - - cp.emit = function (name, arg1) { - // If emitting "exit" event and exit code is 1, we need to check if - // the command exists and emit an "error" instead - // See https://github.com/IndigoUnited/node-cross-spawn/issues/16 - if (name === 'exit') { - const err = verifyENOENT(arg1, parsed, 'spawn'); - - if (err) { - return originalEmit.call(cp, 'error', err); - } - } - - return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params - }; -} +const isStream = stream => + stream !== null && + typeof stream === 'object' && + typeof stream.pipe === 'function'; -function verifyENOENT(status, parsed) { - if (isWin && status === 1 && !parsed.file) { - return notFoundError(parsed.original, 'spawn'); - } +isStream.writable = stream => + isStream(stream) && + stream.writable !== false && + typeof stream._write === 'function' && + typeof stream._writableState === 'object'; - return null; -} +isStream.readable = stream => + isStream(stream) && + stream.readable !== false && + typeof stream._read === 'function' && + typeof stream._readableState === 'object'; -function verifyENOENTSync(status, parsed) { - if (isWin && status === 1 && !parsed.file) { - return notFoundError(parsed.original, 'spawnSync'); - } +isStream.duplex = stream => + isStream.writable(stream) && + isStream.readable(stream); - return null; -} +isStream.transform = stream => + isStream.duplex(stream) && + typeof stream._transform === 'function' && + typeof stream._transformState === 'object'; -module.exports = { - hookChildProcess, - verifyENOENT, - verifyENOENTSync, - notFoundError, -}; +module.exports = isStream; /***/ }), -/* 251 */ +/* 260 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const pump = __webpack_require__(261); +const bufferStream = __webpack_require__(263); -module.exports = input => { - const LF = typeof input === 'string' ? '\n' : '\n'.charCodeAt(); - const CR = typeof input === 'string' ? '\r' : '\r'.charCodeAt(); - - if (input[input.length - 1] === LF) { - input = input.slice(0, input.length - 1); +class MaxBufferError extends Error { + constructor() { + super('maxBuffer exceeded'); + this.name = 'MaxBufferError'; } +} - if (input[input.length - 1] === CR) { - input = input.slice(0, input.length - 1); +async function getStream(inputStream, options) { + if (!inputStream) { + return Promise.reject(new Error('Expected a stream')); } - return input; -}; - - -/***/ }), -/* 252 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const path = __webpack_require__(4); -const pathKey = __webpack_require__(245); - -const npmRunPath = options => { options = { - cwd: process.cwd(), - path: process.env[pathKey()], - execPath: process.execPath, + maxBuffer: Infinity, ...options }; - let previous; - let cwdPath = path.resolve(options.cwd); - const result = []; - - while (previous !== cwdPath) { - result.push(path.join(cwdPath, 'node_modules/.bin')); - previous = cwdPath; - cwdPath = path.resolve(cwdPath, '..'); - } + const {maxBuffer} = options; - // Ensure the running `node` binary is used - const execPathDir = path.resolve(options.cwd, options.execPath, '..'); - result.push(execPathDir); + let stream; + await new Promise((resolve, reject) => { + const rejectPromise = error => { + if (error) { // A null check + error.bufferedData = stream.getBufferedValue(); + } - return result.concat(options.path).join(path.delimiter); -}; + reject(error); + }; -module.exports = npmRunPath; -// TODO: Remove this for the next major release -module.exports.default = npmRunPath; + stream = pump(inputStream, bufferStream(options), error => { + if (error) { + rejectPromise(error); + return; + } -module.exports.env = options => { - options = { - env: process.env, - ...options - }; + resolve(); + }); - const env = {...options.env}; - const path = pathKey({env}); + stream.on('data', () => { + if (stream.getBufferedLength() > maxBuffer) { + rejectPromise(new MaxBufferError()); + } + }); + }); - options.path = env[path]; - env[path] = module.exports(options); + return stream.getBufferedValue(); +} - return env; -}; +module.exports = getStream; +// TODO: Remove this for the next major release +module.exports.default = getStream; +module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'}); +module.exports.array = (stream, options) => getStream(stream, {...options, array: true}); +module.exports.MaxBufferError = MaxBufferError; /***/ }), -/* 253 */ +/* 261 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -const mimicFn = __webpack_require__(254); - -const calledFunctions = new WeakMap(); - -const oneTime = (fn, options = {}) => { - if (typeof fn !== 'function') { - throw new TypeError('Expected a function'); - } +var once = __webpack_require__(161) +var eos = __webpack_require__(262) +var fs = __webpack_require__(133) // we only need fs to get the ReadStream and WriteStream prototypes - let ret; - let isCalled = false; - let callCount = 0; - const functionName = fn.displayName || fn.name || ''; +var noop = function () {} +var ancient = /^v?\.0/.test(process.version) - const onetime = function (...args) { - calledFunctions.set(onetime, ++callCount); +var isFn = function (fn) { + return typeof fn === 'function' +} - if (isCalled) { - if (options.throw === true) { - throw new Error(`Function \`${functionName}\` can only be called once`); - } +var isFS = function (stream) { + if (!ancient) return false // newer node version do not need to care about fs is a special way + if (!fs) return false // browser + return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close) +} - return ret; - } +var isRequest = function (stream) { + return stream.setHeader && isFn(stream.abort) +} - isCalled = true; - ret = fn.apply(this, args); - fn = null; +var destroyer = function (stream, reading, writing, callback) { + callback = once(callback) - return ret; - }; + var closed = false + stream.on('close', function () { + closed = true + }) - mimicFn(onetime, fn); - calledFunctions.set(onetime, callCount); + eos(stream, {readable: reading, writable: writing}, function (err) { + if (err) return callback(err) + closed = true + callback() + }) - return onetime; -}; + var destroyed = false + return function (err) { + if (closed) return + if (destroyed) return + destroyed = true -module.exports = oneTime; -// TODO: Remove this for the next major release -module.exports.default = oneTime; + if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks + if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want -module.exports.callCount = fn => { - if (!calledFunctions.has(fn)) { - throw new Error(`The given function \`${fn.name}\` is not wrapped by the \`onetime\` package`); - } + if (isFn(stream.destroy)) return stream.destroy() - return calledFunctions.get(fn); -}; + callback(err || new Error('stream was destroyed')) + } +} +var call = function (fn) { + fn() +} -/***/ }), -/* 254 */ -/***/ (function(module, exports, __webpack_require__) { +var pipe = function (from, to) { + return from.pipe(to) +} -"use strict"; +var pump = function () { + var streams = Array.prototype.slice.call(arguments) + var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop + if (Array.isArray(streams[0])) streams = streams[0] + if (streams.length < 2) throw new Error('pump requires two streams per minimum') -const mimicFn = (to, from) => { - for (const prop of Reflect.ownKeys(from)) { - Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop)); - } + var error + var destroys = streams.map(function (stream, i) { + var reading = i < streams.length - 1 + var writing = i > 0 + return destroyer(stream, reading, writing, function (err) { + if (!error) error = err + if (err) destroys.forEach(call) + if (reading) return + destroys.forEach(call) + callback(error) + }) + }) - return to; -}; + return streams.reduce(pipe) +} -module.exports = mimicFn; -// TODO: Remove this for the next major release -module.exports.default = mimicFn; +module.exports = pump /***/ }), -/* 255 */ +/* 262 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var once = __webpack_require__(161); -const {signalsByName} = __webpack_require__(256); +var noop = function() {}; -const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { - if (timedOut) { - return `timed out after ${timeout} milliseconds`; - } +var isRequest = function(stream) { + return stream.setHeader && typeof stream.abort === 'function'; +}; - if (isCanceled) { - return 'was canceled'; - } +var isChildProcess = function(stream) { + return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +}; - if (errorCode !== undefined) { - return `failed with ${errorCode}`; - } +var eos = function(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; - if (signal !== undefined) { - return `was killed with ${signal} (${signalDescription})`; - } + callback = once(callback || noop); - if (exitCode !== undefined) { - return `failed with exit code ${exitCode}`; - } + var ws = stream._writableState; + var rs = stream._readableState; + var readable = opts.readable || (opts.readable !== false && stream.readable); + var writable = opts.writable || (opts.writable !== false && stream.writable); + var cancelled = false; - return 'failed'; -}; + var onlegacyfinish = function() { + if (!stream.writable) onfinish(); + }; -const makeError = ({ - stdout, - stderr, - all, - error, - signal, - exitCode, - command, - timedOut, - isCanceled, - killed, - parsed: {options: {timeout}} -}) => { - // `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`. - // We normalize them to `undefined` - exitCode = exitCode === null ? undefined : exitCode; - signal = signal === null ? undefined : signal; - const signalDescription = signal === undefined ? undefined : signalsByName[signal].description; + var onfinish = function() { + writable = false; + if (!readable) callback.call(stream); + }; - const errorCode = error && error.code; + var onend = function() { + readable = false; + if (!writable) callback.call(stream); + }; - const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}); - const execaMessage = `Command ${prefix}: ${command}`; - const isError = Object.prototype.toString.call(error) === '[object Error]'; - const shortMessage = isError ? `${execaMessage}\n${error.message}` : execaMessage; - const message = [shortMessage, stderr, stdout].filter(Boolean).join('\n'); + var onexit = function(exitCode) { + callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); + }; - if (isError) { - error.originalMessage = error.message; - error.message = message; - } else { - error = new Error(message); - } + var onerror = function(err) { + callback.call(stream, err); + }; - error.shortMessage = shortMessage; - error.command = command; - error.exitCode = exitCode; - error.signal = signal; - error.signalDescription = signalDescription; - error.stdout = stdout; - error.stderr = stderr; + var onclose = function() { + process.nextTick(onclosenexttick); + }; - if (all !== undefined) { - error.all = all; - } + var onclosenexttick = function() { + if (cancelled) return; + if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close')); + if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close')); + }; - if ('bufferedData' in error) { - delete error.bufferedData; + var onrequest = function() { + stream.req.on('finish', onfinish); + }; + + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); } - error.failed = true; - error.timedOut = Boolean(timedOut); - error.isCanceled = isCanceled; - error.killed = killed && !timedOut; + if (isChildProcess(stream)) stream.on('exit', onexit); - return error; + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); + + return function() { + cancelled = true; + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('exit', onexit); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; }; -module.exports = makeError; +module.exports = eos; /***/ }), -/* 256 */ +/* 263 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(120); - -var _signals=__webpack_require__(257); -var _realtime=__webpack_require__(259); - - - -const getSignalsByName=function(){ -const signals=(0,_signals.getSignals)(); -return signals.reduce(getSignalByName,{}); -}; - -const getSignalByName=function( -signalByNameMemo, -{name,number,description,supported,action,forced,standard}) -{ -return{ -...signalByNameMemo, -[name]:{name,number,description,supported,action,forced,standard}}; - -}; - -const signalsByName=getSignalsByName();exports.signalsByName=signalsByName; - +const {PassThrough: PassThroughStream} = __webpack_require__(137); +module.exports = options => { + options = {...options}; -const getSignalsByNumber=function(){ -const signals=(0,_signals.getSignals)(); -const length=_realtime.SIGRTMAX+1; -const signalsA=Array.from({length},(value,number)=> -getSignalByNumber(number,signals)); + const {array} = options; + let {encoding} = options; + const isBuffer = encoding === 'buffer'; + let objectMode = false; -return Object.assign({},...signalsA); -}; + if (array) { + objectMode = !(encoding || isBuffer); + } else { + encoding = encoding || 'utf8'; + } -const getSignalByNumber=function(number,signals){ -const signal=findSignalByNumber(number,signals); + if (isBuffer) { + encoding = null; + } -if(signal===undefined){ -return{}; -} + const stream = new PassThroughStream({objectMode}); -const{name,description,supported,action,forced,standard}=signal; -return{ -[number]:{ -name, -number, -description, -supported, -action, -forced, -standard}}; + if (encoding) { + stream.setEncoding(encoding); + } + let length = 0; + const chunks = []; -}; + stream.on('data', chunk => { + chunks.push(chunk); + if (objectMode) { + length = chunks.length; + } else { + length += chunk.length; + } + }); + stream.getBufferedValue = () => { + if (array) { + return chunks; + } -const findSignalByNumber=function(number,signals){ -const signal=signals.find(({name})=>_os.constants.signals[name]===number); + return isBuffer ? Buffer.concat(chunks, length) : chunks.join(''); + }; -if(signal!==undefined){ -return signal; -} + stream.getBufferedLength = () => length; -return signals.find(signalA=>signalA.number===number); + return stream; }; -const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumber; -//# sourceMappingURL=main.js.map /***/ }), -/* 257 */ +/* 264 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(120); -var _core=__webpack_require__(258); -var _realtime=__webpack_require__(259); +const { PassThrough } = __webpack_require__(137); + +module.exports = function (/*streams...*/) { + var sources = [] + var output = new PassThrough({objectMode: true}) + + output.setMaxListeners(0) + output.add = add + output.isEmpty = isEmpty -const getSignals=function(){ -const realtimeSignals=(0,_realtime.getRealtimeSignals)(); -const signals=[..._core.SIGNALS,...realtimeSignals].map(normalizeSignal); -return signals; -};exports.getSignals=getSignals; + output.on('unpipe', remove) + Array.prototype.slice.call(arguments).forEach(add) + return output + function add (source) { + if (Array.isArray(source)) { + source.forEach(add) + return this + } + sources.push(source); + source.once('end', remove.bind(null, source)) + source.once('error', output.emit.bind(output, 'error')) + source.pipe(output, {end: false}) + return this + } + function isEmpty () { + return sources.length == 0; + } + function remove (source) { + sources = sources.filter(function (it) { return it !== source }) + if (!sources.length && output.readable) { output.end() } + } +} -const normalizeSignal=function({ -name, -number:defaultNumber, -description, -action, -forced=false, -standard}) -{ -const{ -signals:{[name]:constantSignal}}= -_os.constants; -const supported=constantSignal!==undefined; -const number=supported?constantSignal:defaultNumber; -return{name,number,description,supported,action,forced,standard}; -}; -//# sourceMappingURL=signals.js.map /***/ }), -/* 258 */ +/* 265 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.SIGNALS=void 0; -const SIGNALS=[ -{ -name:"SIGHUP", -number:1, -action:"terminate", -description:"Terminal closed", -standard:"posix"}, -{ -name:"SIGINT", -number:2, -action:"terminate", -description:"User interruption with CTRL-C", -standard:"ansi"}, - -{ -name:"SIGQUIT", -number:3, -action:"core", -description:"User interruption with CTRL-\\", -standard:"posix"}, - -{ -name:"SIGILL", -number:4, -action:"core", -description:"Invalid machine instruction", -standard:"ansi"}, - -{ -name:"SIGTRAP", -number:5, -action:"core", -description:"Debugger breakpoint", -standard:"posix"}, - -{ -name:"SIGABRT", -number:6, -action:"core", -description:"Aborted", -standard:"ansi"}, - -{ -name:"SIGIOT", -number:6, -action:"core", -description:"Aborted", -standard:"bsd"}, - -{ -name:"SIGBUS", -number:7, -action:"core", -description: -"Bus error due to misaligned, non-existing address or paging error", -standard:"bsd"}, - -{ -name:"SIGEMT", -number:7, -action:"terminate", -description:"Command should be emulated but is not implemented", -standard:"other"}, - -{ -name:"SIGFPE", -number:8, -action:"core", -description:"Floating point arithmetic error", -standard:"ansi"}, - -{ -name:"SIGKILL", -number:9, -action:"terminate", -description:"Forced termination", -standard:"posix", -forced:true}, - -{ -name:"SIGUSR1", -number:10, -action:"terminate", -description:"Application-specific signal", -standard:"posix"}, - -{ -name:"SIGSEGV", -number:11, -action:"core", -description:"Segmentation fault", -standard:"ansi"}, - -{ -name:"SIGUSR2", -number:12, -action:"terminate", -description:"Application-specific signal", -standard:"posix"}, - -{ -name:"SIGPIPE", -number:13, -action:"terminate", -description:"Broken pipe or socket", -standard:"posix"}, - -{ -name:"SIGALRM", -number:14, -action:"terminate", -description:"Timeout or timer", -standard:"posix"}, - -{ -name:"SIGTERM", -number:15, -action:"terminate", -description:"Termination", -standard:"ansi"}, +const nativePromisePrototype = (async () => {})().constructor.prototype; +const descriptors = ['then', 'catch', 'finally'].map(property => [ + property, + Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property) +]); -{ -name:"SIGSTKFLT", -number:16, -action:"terminate", -description:"Stack is empty or overflowed", -standard:"other"}, +// The return value is a mixin of `childProcess` and `Promise` +const mergePromise = (spawned, promise) => { + for (const [property, descriptor] of descriptors) { + // Starting the main `promise` is deferred to avoid consuming streams + const value = typeof promise === 'function' ? + (...args) => Reflect.apply(descriptor.value, promise(), args) : + descriptor.value.bind(promise); -{ -name:"SIGCHLD", -number:17, -action:"ignore", -description:"Child process terminated, paused or unpaused", -standard:"posix"}, + Reflect.defineProperty(spawned, property, {...descriptor, value}); + } -{ -name:"SIGCLD", -number:17, -action:"ignore", -description:"Child process terminated, paused or unpaused", -standard:"other"}, + return spawned; +}; -{ -name:"SIGCONT", -number:18, -action:"unpause", -description:"Unpaused", -standard:"posix", -forced:true}, +// Use promises instead of `child_process` events +const getSpawnedPromise = spawned => { + return new Promise((resolve, reject) => { + spawned.on('exit', (exitCode, signal) => { + resolve({exitCode, signal}); + }); -{ -name:"SIGSTOP", -number:19, -action:"pause", -description:"Paused", -standard:"posix", -forced:true}, + spawned.on('error', error => { + reject(error); + }); -{ -name:"SIGTSTP", -number:20, -action:"pause", -description:"Paused using CTRL-Z or \"suspend\"", -standard:"posix"}, + if (spawned.stdin) { + spawned.stdin.on('error', error => { + reject(error); + }); + } + }); +}; -{ -name:"SIGTTIN", -number:21, -action:"pause", -description:"Background process cannot read terminal input", -standard:"posix"}, +module.exports = { + mergePromise, + getSpawnedPromise +}; -{ -name:"SIGBREAK", -number:21, -action:"terminate", -description:"User interruption with CTRL-BREAK", -standard:"other"}, -{ -name:"SIGTTOU", -number:22, -action:"pause", -description:"Background process cannot write to terminal output", -standard:"posix"}, -{ -name:"SIGURG", -number:23, -action:"ignore", -description:"Socket received out-of-band data", -standard:"bsd"}, +/***/ }), +/* 266 */ +/***/ (function(module, exports, __webpack_require__) { -{ -name:"SIGXCPU", -number:24, -action:"core", -description:"Process timed out", -standard:"bsd"}, +"use strict"; -{ -name:"SIGXFSZ", -number:25, -action:"core", -description:"File too big", -standard:"bsd"}, +const SPACES_REGEXP = / +/g; -{ -name:"SIGVTALRM", -number:26, -action:"terminate", -description:"Timeout or timer", -standard:"bsd"}, +const joinCommand = (file, args = []) => { + if (!Array.isArray(args)) { + return file; + } -{ -name:"SIGPROF", -number:27, -action:"terminate", -description:"Timeout or timer", -standard:"bsd"}, + return [file, ...args].join(' '); +}; -{ -name:"SIGWINCH", -number:28, -action:"ignore", -description:"Terminal window size changed", -standard:"bsd"}, +// Allow spaces to be escaped by a backslash if not meant as a delimiter +const handleEscaping = (tokens, token, index) => { + if (index === 0) { + return [token]; + } -{ -name:"SIGIO", -number:29, -action:"terminate", -description:"I/O is available", -standard:"other"}, + const previousToken = tokens[tokens.length - 1]; -{ -name:"SIGPOLL", -number:29, -action:"terminate", -description:"Watched event", -standard:"other"}, + if (previousToken.endsWith('\\')) { + return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`]; + } -{ -name:"SIGINFO", -number:29, -action:"ignore", -description:"Request for process information", -standard:"other"}, + return [...tokens, token]; +}; -{ -name:"SIGPWR", -number:30, -action:"terminate", -description:"Device running out of power", -standard:"systemv"}, +// Handle `execa.command()` +const parseCommand = command => { + return command + .trim() + .split(SPACES_REGEXP) + .reduce(handleEscaping, []); +}; -{ -name:"SIGSYS", -number:31, -action:"core", -description:"Invalid system call", -standard:"other"}, +module.exports = { + joinCommand, + parseCommand +}; -{ -name:"SIGUNUSED", -number:31, -action:"terminate", -description:"Invalid system call", -standard:"other"}];exports.SIGNALS=SIGNALS; -//# sourceMappingURL=core.js.map /***/ }), -/* 259 */ +/* 267 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.SIGRTMAX=exports.getRealtimeSignals=void 0; -const getRealtimeSignals=function(){ -const length=SIGRTMAX-SIGRTMIN+1; -return Array.from({length},getRealtimeSignal); -};exports.getRealtimeSignals=getRealtimeSignals; - -const getRealtimeSignal=function(value,index){ -return{ -name:`SIGRT${index+1}`, -number:SIGRTMIN+index, -action:"terminate", -description:"Application-specific signal (realtime)", -standard:"posix"}; +// Copyright IBM Corp. 2014,2018. All Rights Reserved. +// Node module: strong-log-transformer +// This file is licensed under the Apache License 2.0. +// License text available at https://opensource.org/licenses/Apache-2.0 -}; +module.exports = __webpack_require__(268); +module.exports.cli = __webpack_require__(272); -const SIGRTMIN=34; -const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; -//# sourceMappingURL=realtime.js.map /***/ }), -/* 260 */ +/* 268 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +// Copyright IBM Corp. 2014,2018. All Rights Reserved. +// Node module: strong-log-transformer +// This file is licensed under the Apache License 2.0. +// License text available at https://opensource.org/licenses/Apache-2.0 -const aliases = ['stdin', 'stdout', 'stderr']; - -const hasAlias = opts => aliases.some(alias => opts[alias] !== undefined); - -const normalizeStdio = opts => { - if (!opts) { - return; - } - - const {stdio} = opts; - if (stdio === undefined) { - return aliases.map(alias => opts[alias]); - } - if (hasAlias(opts)) { - throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`); - } +var stream = __webpack_require__(137); +var util = __webpack_require__(111); +var fs = __webpack_require__(133); - if (typeof stdio === 'string') { - return stdio; - } +var through = __webpack_require__(269); +var duplexer = __webpack_require__(270); +var StringDecoder = __webpack_require__(271).StringDecoder; - if (!Array.isArray(stdio)) { - throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); - } +module.exports = Logger; - const length = Math.max(stdio.length, aliases.length); - return Array.from({length}, (value, index) => stdio[index]); +Logger.DEFAULTS = { + format: 'text', + tag: '', + mergeMultiline: false, + timeStamp: false, }; -module.exports = normalizeStdio; +var formatters = { + text: textFormatter, + json: jsonFormatter, +} -// `ipc` is pushed unless it is already present -module.exports.node = opts => { - const stdio = normalizeStdio(opts); +function Logger(options) { + var defaults = JSON.parse(JSON.stringify(Logger.DEFAULTS)); + options = util._extend(defaults, options || {}); + var catcher = deLiner(); + var emitter = catcher; + var transforms = [ + objectifier(), + ]; - if (stdio === 'ipc') { - return 'ipc'; - } + if (options.tag) { + transforms.push(staticTagger(options.tag)); + } - if (stdio === undefined || typeof stdio === 'string') { - return [stdio, stdio, stdio, 'ipc']; - } + if (options.mergeMultiline) { + transforms.push(lineMerger()); + } - if (stdio.includes('ipc')) { - return stdio; - } + // TODO + // if (options.pidStamp) { + // transforms.push(pidStamper(options.pid)); + // } - return [...stdio, 'ipc']; -}; + // TODO + // if (options.workerStamp) { + // transforms.push(workerStamper(options.worker)); + // } + transforms.push(formatters[options.format](options)); -/***/ }), -/* 261 */ -/***/ (function(module, exports, __webpack_require__) { + // restore line endings that were removed by line splitting + transforms.push(reLiner()); -"use strict"; + for (var t in transforms) { + emitter = emitter.pipe(transforms[t]); + } -const os = __webpack_require__(120); -const onExit = __webpack_require__(217); + return duplexer(catcher, emitter); +} -const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; +function deLiner() { + var decoder = new StringDecoder('utf8'); + var last = ''; -// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior -const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => { - const killResult = kill(signal); - setKillTimeout(kill, signal, options, killResult); - return killResult; -}; + return new stream.Transform({ + transform(chunk, _enc, callback) { + last += decoder.write(chunk); + var list = last.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g); + last = list.pop(); + for (var i = 0; i < list.length; i++) { + // swallow empty lines + if (list[i]) { + this.push(list[i]); + } + } + callback(); + }, + flush(callback) { + // incomplete UTF8 sequences become UTF8 replacement characters + last += decoder.end(); + if (last) { + this.push(last); + } + callback(); + }, + }); +} -const setKillTimeout = (kill, signal, options, killResult) => { - if (!shouldForceKill(signal, options, killResult)) { - return; - } +function reLiner() { + return through(appendNewline); - const timeout = getForceKillAfterTimeout(options); - const t = setTimeout(() => { - kill('SIGKILL'); - }, timeout); + function appendNewline(line) { + this.emit('data', line + '\n'); + } +} - // Guarded because there's no `.unref()` when `execa` is used in the renderer - // process in Electron. This cannot be tested since we don't run tests in - // Electron. - // istanbul ignore else - if (t.unref) { - t.unref(); - } -}; +function objectifier() { + return through(objectify, null, {autoDestroy: false}); -const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => { - return isSigterm(signal) && forceKillAfterTimeout !== false && killResult; -}; + function objectify(line) { + this.emit('data', { + msg: line, + time: Date.now(), + }); + } +} -const isSigterm = signal => { - return signal === os.constants.signals.SIGTERM || - (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM'); -}; +function staticTagger(tag) { + return through(tagger); -const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => { - if (forceKillAfterTimeout === true) { - return DEFAULT_FORCE_KILL_TIMEOUT; - } + function tagger(logEvent) { + logEvent.tag = tag; + this.emit('data', logEvent); + } +} - if (!Number.isInteger(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { - throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); - } +function textFormatter(options) { + return through(textify); - return forceKillAfterTimeout; -}; - -// `childProcess.cancel()` -const spawnedCancel = (spawned, context) => { - const killResult = spawned.kill(); - - if (killResult) { - context.isCanceled = true; - } -}; - -const timeoutKill = (spawned, signal, reject) => { - spawned.kill(signal); - reject(Object.assign(new Error('Timed out'), {timedOut: true, signal})); -}; - -// `timeout` option handling -const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => { - if (timeout === 0 || timeout === undefined) { - return spawnedPromise; - } + function textify(logEvent) { + var line = util.format('%s%s', textifyTags(logEvent.tag), + logEvent.msg.toString()); + if (options.timeStamp) { + line = util.format('%s %s', new Date(logEvent.time).toISOString(), line); + } + this.emit('data', line.replace(/\n/g, '\\n')); + } - if (!Number.isInteger(timeout) || timeout < 0) { - throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); - } + function textifyTags(tags) { + var str = ''; + if (typeof tags === 'string') { + str = tags + ' '; + } else if (typeof tags === 'object') { + for (var t in tags) { + str += t + ':' + tags[t] + ' '; + } + } + return str; + } +} - let timeoutId; - const timeoutPromise = new Promise((resolve, reject) => { - timeoutId = setTimeout(() => { - timeoutKill(spawned, killSignal, reject); - }, timeout); - }); +function jsonFormatter(options) { + return through(jsonify); - const safeSpawnedPromise = spawnedPromise.finally(() => { - clearTimeout(timeoutId); - }); + function jsonify(logEvent) { + if (options.timeStamp) { + logEvent.time = new Date(logEvent.time).toISOString(); + } else { + delete logEvent.time; + } + logEvent.msg = logEvent.msg.toString(); + this.emit('data', JSON.stringify(logEvent)); + } +} - return Promise.race([timeoutPromise, safeSpawnedPromise]); -}; +function lineMerger(host) { + var previousLine = null; + var flushTimer = null; + var stream = through(lineMergerWrite, lineMergerEnd); + var flush = _flush.bind(stream); -// `cleanup` option handling -const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => { - if (!cleanup || detached) { - return timedPromise; - } + return stream; - const removeExitHandler = onExit(() => { - spawned.kill(); - }); + function lineMergerWrite(line) { + if (/^\s+/.test(line.msg)) { + if (previousLine) { + previousLine.msg += '\n' + line.msg; + } else { + previousLine = line; + } + } else { + flush(); + previousLine = line; + } + // rolling timeout + clearTimeout(flushTimer); + flushTimer = setTimeout(flush.bind(this), 10); + } - return timedPromise.finally(() => { - removeExitHandler(); - }); -}; + function _flush() { + if (previousLine) { + this.emit('data', previousLine); + previousLine = null; + } + } -module.exports = { - spawnedKill, - spawnedCancel, - setupTimeout, - setExitHandler -}; + function lineMergerEnd() { + flush.call(this); + this.emit('end'); + } +} /***/ }), -/* 262 */ +/* 269 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -const isStream = __webpack_require__(263); -const getStream = __webpack_require__(264); -const mergeStream = __webpack_require__(268); - -// `input` option -const handleInput = (spawned, input) => { - // Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 - // TODO: Remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 - if (input === undefined || spawned.stdin === undefined) { - return; - } +var Stream = __webpack_require__(137) - if (isStream(input)) { - input.pipe(spawned.stdin); - } else { - spawned.stdin.end(input); - } -}; +// through +// +// a stream that does nothing but re-emit the input. +// useful for aggregating a series of changing but not ending streams into one stream) -// `all` interleaves `stdout` and `stderr` -const makeAllStream = (spawned, {all}) => { - if (!all || (!spawned.stdout && !spawned.stderr)) { - return; - } +exports = module.exports = through +through.through = through - const mixed = mergeStream(); +//create a readable writable stream. - if (spawned.stdout) { - mixed.add(spawned.stdout); - } +function through (write, end, opts) { + write = write || function (data) { this.queue(data) } + end = end || function () { this.queue(null) } - if (spawned.stderr) { - mixed.add(spawned.stderr); - } + var ended = false, destroyed = false, buffer = [], _ended = false + var stream = new Stream() + stream.readable = stream.writable = true + stream.paused = false - return mixed; -}; +// stream.autoPause = !(opts && opts.autoPause === false) + stream.autoDestroy = !(opts && opts.autoDestroy === false) -// On failure, `result.stdout|stderr|all` should contain the currently buffered stream -const getBufferedData = async (stream, streamPromise) => { - if (!stream) { - return; - } + stream.write = function (data) { + write.call(this, data) + return !stream.paused + } - stream.destroy(); + function drain() { + while(buffer.length && !stream.paused) { + var data = buffer.shift() + if(null === data) + return stream.emit('end') + else + stream.emit('data', data) + } + } - try { - return await streamPromise; - } catch (error) { - return error.bufferedData; - } -}; + stream.queue = stream.push = function (data) { +// console.error(ended) + if(_ended) return stream + if(data === null) _ended = true + buffer.push(data) + drain() + return stream + } -const getStreamPromise = (stream, {encoding, buffer, maxBuffer}) => { - if (!stream || !buffer) { - return; - } + //this will be registered as the first 'end' listener + //must call destroy next tick, to make sure we're after any + //stream piped from here. + //this is only a problem if end is not emitted synchronously. + //a nicer way to do this is to make sure this is the last listener for 'end' - if (encoding) { - return getStream(stream, {encoding, maxBuffer}); - } + stream.on('end', function () { + stream.readable = false + if(!stream.writable && stream.autoDestroy) + process.nextTick(function () { + stream.destroy() + }) + }) - return getStream.buffer(stream, {maxBuffer}); -}; + function _end () { + stream.writable = false + end.call(stream) + if(!stream.readable && stream.autoDestroy) + stream.destroy() + } -// Retrieve result of child process: exit code, signal, error, streams (stdout/stderr/all) -const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => { - const stdoutPromise = getStreamPromise(stdout, {encoding, buffer, maxBuffer}); - const stderrPromise = getStreamPromise(stderr, {encoding, buffer, maxBuffer}); - const allPromise = getStreamPromise(all, {encoding, buffer, maxBuffer: maxBuffer * 2}); + stream.end = function (data) { + if(ended) return + ended = true + if(arguments.length) stream.write(data) + _end() // will emit or queue + return stream + } - try { - return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]); - } catch (error) { - return Promise.all([ - {error, signal: error.signal, timedOut: error.timedOut}, - getBufferedData(stdout, stdoutPromise), - getBufferedData(stderr, stderrPromise), - getBufferedData(all, allPromise) - ]); - } -}; + stream.destroy = function () { + if(destroyed) return + destroyed = true + ended = true + buffer.length = 0 + stream.writable = stream.readable = false + stream.emit('close') + return stream + } -const validateInputSync = ({input}) => { - if (isStream(input)) { - throw new TypeError('The `input` option cannot be a stream in sync mode'); - } -}; + stream.pause = function () { + if(stream.paused) return + stream.paused = true + return stream + } -module.exports = { - handleInput, - makeAllStream, - getSpawnedResult, - validateInputSync -}; + stream.resume = function () { + if(stream.paused) { + stream.paused = false + stream.emit('resume') + } + drain() + //may have become paused again, + //as drain emits 'data'. + if(!stream.paused) + stream.emit('drain') + return stream + } + return stream +} /***/ }), -/* 263 */ +/* 270 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +var Stream = __webpack_require__(137) +var writeMethods = ["write", "end", "destroy"] +var readMethods = ["resume", "pause"] +var readEvents = ["data", "close"] +var slice = Array.prototype.slice -const isStream = stream => - stream !== null && - typeof stream === 'object' && - typeof stream.pipe === 'function'; +module.exports = duplex -isStream.writable = stream => - isStream(stream) && - stream.writable !== false && - typeof stream._write === 'function' && - typeof stream._writableState === 'object'; +function forEach (arr, fn) { + if (arr.forEach) { + return arr.forEach(fn) + } -isStream.readable = stream => - isStream(stream) && - stream.readable !== false && - typeof stream._read === 'function' && - typeof stream._readableState === 'object'; + for (var i = 0; i < arr.length; i++) { + fn(arr[i], i) + } +} -isStream.duplex = stream => - isStream.writable(stream) && - isStream.readable(stream); +function duplex(writer, reader) { + var stream = new Stream() + var ended = false -isStream.transform = stream => - isStream.duplex(stream) && - typeof stream._transform === 'function' && - typeof stream._transformState === 'object'; + forEach(writeMethods, proxyWriter) -module.exports = isStream; + forEach(readMethods, proxyReader) + forEach(readEvents, proxyStream) -/***/ }), -/* 264 */ -/***/ (function(module, exports, __webpack_require__) { + reader.on("end", handleEnd) -"use strict"; + writer.on("drain", function() { + stream.emit("drain") + }) -const pump = __webpack_require__(265); -const bufferStream = __webpack_require__(267); + writer.on("error", reemit) + reader.on("error", reemit) -class MaxBufferError extends Error { - constructor() { - super('maxBuffer exceeded'); - this.name = 'MaxBufferError'; - } -} + stream.writable = writer.writable + stream.readable = reader.readable -async function getStream(inputStream, options) { - if (!inputStream) { - return Promise.reject(new Error('Expected a stream')); - } + return stream - options = { - maxBuffer: Infinity, - ...options - }; + function proxyWriter(methodName) { + stream[methodName] = method - const {maxBuffer} = options; + function method() { + return writer[methodName].apply(writer, arguments) + } + } - let stream; - await new Promise((resolve, reject) => { - const rejectPromise = error => { - if (error) { // A null check - error.bufferedData = stream.getBufferedValue(); - } + function proxyReader(methodName) { + stream[methodName] = method - reject(error); - }; + function method() { + stream.emit(methodName) + var func = reader[methodName] + if (func) { + return func.apply(reader, arguments) + } + reader.emit(methodName) + } + } - stream = pump(inputStream, bufferStream(options), error => { - if (error) { - rejectPromise(error); - return; - } + function proxyStream(methodName) { + reader.on(methodName, reemit) - resolve(); - }); + function reemit() { + var args = slice.call(arguments) + args.unshift(methodName) + stream.emit.apply(stream, args) + } + } - stream.on('data', () => { - if (stream.getBufferedLength() > maxBuffer) { - rejectPromise(new MaxBufferError()); - } - }); - }); + function handleEnd() { + if (ended) { + return + } + ended = true + var args = slice.call(arguments) + args.unshift("end") + stream.emit.apply(stream, args) + } - return stream.getBufferedValue(); + function reemit(err) { + stream.emit("error", err) + } } -module.exports = getStream; -// TODO: Remove this for the next major release -module.exports.default = getStream; -module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'}); -module.exports.array = (stream, options) => getStream(stream, {...options, array: true}); -module.exports.MaxBufferError = MaxBufferError; - /***/ }), -/* 265 */ -/***/ (function(module, exports, __webpack_require__) { - -var once = __webpack_require__(161) -var eos = __webpack_require__(266) -var fs = __webpack_require__(133) // we only need fs to get the ReadStream and WriteStream prototypes - -var noop = function () {} -var ancient = /^v?\.0/.test(process.version) - -var isFn = function (fn) { - return typeof fn === 'function' -} +/* 271 */ +/***/ (function(module, exports) { -var isFS = function (stream) { - if (!ancient) return false // newer node version do not need to care about fs is a special way - if (!fs) return false // browser - return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close) -} +module.exports = require("string_decoder"); -var isRequest = function (stream) { - return stream.setHeader && isFn(stream.abort) -} +/***/ }), +/* 272 */ +/***/ (function(module, exports, __webpack_require__) { -var destroyer = function (stream, reading, writing, callback) { - callback = once(callback) +"use strict"; +// Copyright IBM Corp. 2014,2018. All Rights Reserved. +// Node module: strong-log-transformer +// This file is licensed under the Apache License 2.0. +// License text available at https://opensource.org/licenses/Apache-2.0 - var closed = false - stream.on('close', function () { - closed = true - }) - eos(stream, {readable: reading, writable: writing}, function (err) { - if (err) return callback(err) - closed = true - callback() - }) - var destroyed = false - return function (err) { - if (closed) return - if (destroyed) return - destroyed = true +var minimist = __webpack_require__(273); +var path = __webpack_require__(4); - if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks - if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want +var Logger = __webpack_require__(268); +var pkg = __webpack_require__(274); - if (isFn(stream.destroy)) return stream.destroy() +module.exports = cli; - callback(err || new Error('stream was destroyed')) +function cli(args) { + var opts = minimist(args.slice(2)); + var $0 = path.basename(args[1]); + var p = console.log.bind(console); + if (opts.v || opts.version) { + version($0, p); + } else if (opts.h || opts.help) { + usage($0, p); + } else if (args.length < 3) { + process.stdin.pipe(Logger()).pipe(process.stdout); + } else { + process.stdin.pipe(Logger(opts)).pipe(process.stdout); } } -var call = function (fn) { - fn() -} - -var pipe = function (from, to) { - return from.pipe(to) +function version($0, p) { + p('%s v%s', pkg.name, pkg.version); } -var pump = function () { - var streams = Array.prototype.slice.call(arguments) - var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop - - if (Array.isArray(streams[0])) streams = streams[0] - if (streams.length < 2) throw new Error('pump requires two streams per minimum') - - var error - var destroys = streams.map(function (stream, i) { - var reading = i < streams.length - 1 - var writing = i > 0 - return destroyer(stream, reading, writing, function (err) { - if (!error) error = err - if (err) destroys.forEach(call) - if (reading) return - destroys.forEach(call) - callback(error) - }) - }) +function usage($0, p) { + var PADDING = ' '; + var opt, def; + p('Usage: %s [options]', $0); + p(''); + p('%s', pkg.description); + p(''); + p('OPTIONS:'); + for (opt in Logger.DEFAULTS) { + def = Logger.DEFAULTS[opt]; + if (typeof def === 'boolean') + boolOpt(opt, Logger.DEFAULTS[opt]); + else + stdOpt(opt, Logger.DEFAULTS[opt]); + } + p(''); - return streams.reduce(pipe) -} + function boolOpt(name, def) { + name = name + PADDING.slice(0, 20-name.length); + p(' --%s default: %s', name, def); + } -module.exports = pump + function stdOpt(name, def) { + var value = name.toUpperCase() + + PADDING.slice(0, 19 - name.length*2); + p(' --%s %s default: %j', name, value, def); + } +} /***/ }), -/* 266 */ -/***/ (function(module, exports, __webpack_require__) { - -var once = __webpack_require__(161); +/* 273 */ +/***/ (function(module, exports) { -var noop = function() {}; +module.exports = function (args, opts) { + if (!opts) opts = {}; + + var flags = { bools : {}, strings : {}, unknownFn: null }; -var isRequest = function(stream) { - return stream.setHeader && typeof stream.abort === 'function'; -}; + if (typeof opts['unknown'] === 'function') { + flags.unknownFn = opts['unknown']; + } -var isChildProcess = function(stream) { - return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 -}; + if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { + flags.allBools = true; + } else { + [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { + flags.bools[key] = true; + }); + } + + var aliases = {}; + Object.keys(opts.alias || {}).forEach(function (key) { + aliases[key] = [].concat(opts.alias[key]); + aliases[key].forEach(function (x) { + aliases[x] = [key].concat(aliases[key].filter(function (y) { + return x !== y; + })); + }); + }); -var eos = function(stream, opts, callback) { - if (typeof opts === 'function') return eos(stream, null, opts); - if (!opts) opts = {}; + [].concat(opts.string).filter(Boolean).forEach(function (key) { + flags.strings[key] = true; + if (aliases[key]) { + flags.strings[aliases[key]] = true; + } + }); - callback = once(callback || noop); + var defaults = opts['default'] || {}; + + var argv = { _ : [] }; + Object.keys(flags.bools).forEach(function (key) { + setArg(key, defaults[key] === undefined ? false : defaults[key]); + }); + + var notFlags = []; - var ws = stream._writableState; - var rs = stream._readableState; - var readable = opts.readable || (opts.readable !== false && stream.readable); - var writable = opts.writable || (opts.writable !== false && stream.writable); - var cancelled = false; + if (args.indexOf('--') !== -1) { + notFlags = args.slice(args.indexOf('--')+1); + args = args.slice(0, args.indexOf('--')); + } - var onlegacyfinish = function() { - if (!stream.writable) onfinish(); - }; + function argDefined(key, arg) { + return (flags.allBools && /^--[^=]+$/.test(arg)) || + flags.strings[key] || flags.bools[key] || aliases[key]; + } - var onfinish = function() { - writable = false; - if (!readable) callback.call(stream); - }; + function setArg (key, val, arg) { + if (arg && flags.unknownFn && !argDefined(key, arg)) { + if (flags.unknownFn(arg) === false) return; + } - var onend = function() { - readable = false; - if (!writable) callback.call(stream); - }; + var value = !flags.strings[key] && isNumber(val) + ? Number(val) : val + ; + setKey(argv, key.split('.'), value); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), value); + }); + } - var onexit = function(exitCode) { - callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); - }; + function setKey (obj, keys, value) { + var o = obj; + for (var i = 0; i < keys.length-1; i++) { + var key = keys[i]; + if (key === '__proto__') return; + if (o[key] === undefined) o[key] = {}; + if (o[key] === Object.prototype || o[key] === Number.prototype + || o[key] === String.prototype) o[key] = {}; + if (o[key] === Array.prototype) o[key] = []; + o = o[key]; + } - var onerror = function(err) { - callback.call(stream, err); - }; + var key = keys[keys.length - 1]; + if (key === '__proto__') return; + if (o === Object.prototype || o === Number.prototype + || o === String.prototype) o = {}; + if (o === Array.prototype) o = []; + if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') { + o[key] = value; + } + else if (Array.isArray(o[key])) { + o[key].push(value); + } + else { + o[key] = [ o[key], value ]; + } + } + + function aliasIsBoolean(key) { + return aliases[key].some(function (x) { + return flags.bools[x]; + }); + } - var onclose = function() { - process.nextTick(onclosenexttick); - }; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + + if (/^--.+=/.test(arg)) { + // Using [\s\S] instead of . because js doesn't support the + // 'dotall' regex modifier. See: + // http://stackoverflow.com/a/1068308/13216 + var m = arg.match(/^--([^=]+)=([\s\S]*)$/); + var key = m[1]; + var value = m[2]; + if (flags.bools[key]) { + value = value !== 'false'; + } + setArg(key, value, arg); + } + else if (/^--no-.+/.test(arg)) { + var key = arg.match(/^--no-(.+)/)[1]; + setArg(key, false, arg); + } + else if (/^--.+/.test(arg)) { + var key = arg.match(/^--(.+)/)[1]; + var next = args[i + 1]; + if (next !== undefined && !/^-/.test(next) + && !flags.bools[key] + && !flags.allBools + && (aliases[key] ? !aliasIsBoolean(key) : true)) { + setArg(key, next, arg); + i++; + } + else if (/^(true|false)$/.test(next)) { + setArg(key, next === 'true', arg); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } + else if (/^-[^-]+/.test(arg)) { + var letters = arg.slice(1,-1).split(''); + + var broken = false; + for (var j = 0; j < letters.length; j++) { + var next = arg.slice(j+2); + + if (next === '-') { + setArg(letters[j], next, arg) + continue; + } + + if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { + setArg(letters[j], next.split('=')[1], arg); + broken = true; + break; + } + + if (/[A-Za-z]/.test(letters[j]) + && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { + setArg(letters[j], next, arg); + broken = true; + break; + } + + if (letters[j+1] && letters[j+1].match(/\W/)) { + setArg(letters[j], arg.slice(j+2), arg); + broken = true; + break; + } + else { + setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); + } + } + + var key = arg.slice(-1)[0]; + if (!broken && key !== '-') { + if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) + && !flags.bools[key] + && (aliases[key] ? !aliasIsBoolean(key) : true)) { + setArg(key, args[i+1], arg); + i++; + } + else if (args[i+1] && /^(true|false)$/.test(args[i+1])) { + setArg(key, args[i+1] === 'true', arg); + i++; + } + else { + setArg(key, flags.strings[key] ? '' : true, arg); + } + } + } + else { + if (!flags.unknownFn || flags.unknownFn(arg) !== false) { + argv._.push( + flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) + ); + } + if (opts.stopEarly) { + argv._.push.apply(argv._, args.slice(i + 1)); + break; + } + } + } + + Object.keys(defaults).forEach(function (key) { + if (!hasKey(argv, key.split('.'))) { + setKey(argv, key.split('.'), defaults[key]); + + (aliases[key] || []).forEach(function (x) { + setKey(argv, x.split('.'), defaults[key]); + }); + } + }); + + if (opts['--']) { + argv['--'] = new Array(); + notFlags.forEach(function(key) { + argv['--'].push(key); + }); + } + else { + notFlags.forEach(function(key) { + argv._.push(key); + }); + } - var onclosenexttick = function() { - if (cancelled) return; - if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close')); - if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close')); - }; + return argv; +}; - var onrequest = function() { - stream.req.on('finish', onfinish); - }; +function hasKey (obj, keys) { + var o = obj; + keys.slice(0,-1).forEach(function (key) { + o = (o[key] || {}); + }); - if (isRequest(stream)) { - stream.on('complete', onfinish); - stream.on('abort', onclose); - if (stream.req) onrequest(); - else stream.on('request', onrequest); - } else if (writable && !ws) { // legacy streams - stream.on('end', onlegacyfinish); - stream.on('close', onlegacyfinish); - } + var key = keys[keys.length - 1]; + return key in o; +} - if (isChildProcess(stream)) stream.on('exit', onexit); +function isNumber (x) { + if (typeof x === 'number') return true; + if (/^0x[0-9a-f]+$/i.test(x)) return true; + return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); +} - stream.on('end', onend); - stream.on('finish', onfinish); - if (opts.error !== false) stream.on('error', onerror); - stream.on('close', onclose); - return function() { - cancelled = true; - stream.removeListener('complete', onfinish); - stream.removeListener('abort', onclose); - stream.removeListener('request', onrequest); - if (stream.req) stream.req.removeListener('finish', onfinish); - stream.removeListener('end', onlegacyfinish); - stream.removeListener('close', onlegacyfinish); - stream.removeListener('finish', onfinish); - stream.removeListener('exit', onexit); - stream.removeListener('end', onend); - stream.removeListener('error', onerror); - stream.removeListener('close', onclose); - }; -}; -module.exports = eos; +/***/ }), +/* 274 */ +/***/ (function(module) { +module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham \",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 267 */ -/***/ (function(module, exports, __webpack_require__) { +/* 275 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "workspacePackagePaths", function() { return workspacePackagePaths; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return copyWorkspacePackages; }); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(146); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(glob__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(276); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(164); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(145); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -const {PassThrough: PassThroughStream} = __webpack_require__(137); -module.exports = options => { - options = {...options}; - const {array} = options; - let {encoding} = options; - const isBuffer = encoding === 'buffer'; - let objectMode = false; - if (array) { - objectMode = !(encoding || isBuffer); - } else { - encoding = encoding || 'utf8'; - } - if (isBuffer) { - encoding = null; - } - const stream = new PassThroughStream({objectMode}); - if (encoding) { - stream.setEncoding(encoding); - } +const glob = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(glob__WEBPACK_IMPORTED_MODULE_0___default.a); +async function workspacePackagePaths(rootPath) { + const rootPkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["readPackageJson"])(rootPath); - let length = 0; - const chunks = []; + if (!rootPkgJson.workspaces) { + return []; + } - stream.on('data', chunk => { - chunks.push(chunk); + const workspacesPathsPatterns = rootPkgJson.workspaces.packages; + let workspaceProjectsPaths = []; - if (objectMode) { - length = chunks.length; - } else { - length += chunk.length; - } - }); + for (const pattern of workspacesPathsPatterns) { + workspaceProjectsPaths = workspaceProjectsPaths.concat(await packagesFromGlobPattern({ + pattern, + rootPath + })); + } // Filter out exclude glob patterns - stream.getBufferedValue = () => { - if (array) { - return chunks; - } - return isBuffer ? Buffer.concat(chunks, length) : chunks.join(''); - }; + for (const pattern of workspacesPathsPatterns) { + if (pattern.startsWith('!')) { + const pathToRemove = path__WEBPACK_IMPORTED_MODULE_1___default.a.join(rootPath, pattern.slice(1), 'package.json'); + workspaceProjectsPaths = workspaceProjectsPaths.filter(p => p !== pathToRemove); + } + } - stream.getBufferedLength = () => length; + return workspaceProjectsPaths; +} +async function copyWorkspacePackages(rootPath) { + const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ + rootPath + }); + const projects = await Object(_projects__WEBPACK_IMPORTED_MODULE_6__["getProjects"])(rootPath, projectPaths); - return stream; -}; + for (const project of projects.values()) { + const dest = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(rootPath, 'node_modules', project.name); + if ((await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["isSymlink"])(dest)) === false) { + continue; + } // Remove the symlink -/***/ }), -/* 268 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; + await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["unlink"])(dest); // Copy in the package + await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["copyDirectory"])(project.path, dest); + } +} -const { PassThrough } = __webpack_require__(137); +function packagesFromGlobPattern({ + pattern, + rootPath +}) { + const globOptions = { + cwd: rootPath, + // Should throw in case of unusual errors when reading the file system + strict: true, + // Always returns absolute paths for matched files + absolute: true, + // Do not match ** against multiple filenames + // (This is only specified because we currently don't have a need for it.) + noglobstar: true + }; + return glob(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(pattern, 'package.json'), globOptions); +} -module.exports = function (/*streams...*/) { - var sources = [] - var output = new PassThrough({objectMode: true}) +/***/ }), +/* 276 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - output.setMaxListeners(0) +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_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. + */ - output.add = add - output.isEmpty = isEmpty - output.on('unpipe', remove) - - Array.prototype.slice.call(arguments).forEach(add) - - return output +/** + * Returns all the paths where plugins are located + */ +function getProjectPaths({ + rootPath, + ossOnly, + skipKibanaPlugins +}) { + const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared + // plugin functional used in the selenium functional tests. + // As we are now using the webpack dll for the client vendors dependencies + // when we run the plugin functional tests against the distributable + // dependencies used by such plugins like @eui, react and react-dom can't + // be loaded from the dll as the context is different from the one declared + // into the webpack dll reference plugin. + // In anyway, have a plugin declaring their own dependencies is the + // correct and the expect behavior. - function add (source) { - if (Array.isArray(source)) { - source.forEach(add) - return this - } + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); - sources.push(source); - source.once('end', remove.bind(null, source)) - source.once('error', output.emit.bind(output, 'error')) - source.pipe(output, {end: false}) - return this + if (!ossOnly) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); } - function isEmpty () { - return sources.length == 0; + if (!skipKibanaPlugins) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); } - function remove (source) { - sources = sources.filter(function (it) { return it !== source }) - if (!sources.length && output.readable) { output.end() } - } + return projectPaths; } - /***/ }), -/* 269 */ -/***/ (function(module, exports, __webpack_require__) { +/* 277 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(232); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(279); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -const nativePromisePrototype = (async () => {})().constructor.prototype; -const descriptors = ['then', 'catch', 'finally'].map(property => [ - property, - Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property) -]); - -// The return value is a mixin of `childProcess` and `Promise` -const mergePromise = (spawned, promise) => { - for (const [property, descriptor] of descriptors) { - // Starting the main `promise` is deferred to avoid consuming streams - const value = typeof promise === 'function' ? - (...args) => Reflect.apply(descriptor.value, promise(), args) : - descriptor.value.bind(promise); - - Reflect.defineProperty(spawned, property, {...descriptor, value}); - } - return spawned; -}; -// Use promises instead of `child_process` events -const getSpawnedPromise = spawned => { - return new Promise((resolve, reject) => { - spawned.on('exit', (exitCode, signal) => { - resolve({exitCode, signal}); - }); - spawned.on('error', error => { - reject(error); - }); +const statAsync = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_0___default.a.stat); - if (spawned.stdin) { - spawned.stdin.on('error', error => { - reject(error); - }); - } - }); -}; +const projectBySpecificitySorter = (a, b) => b.path.length - a.path.length; +/** Get the changed files for a set of projects */ -module.exports = { - mergePromise, - getSpawnedPromise -}; +async function getChangesForProjects(projects, kbn, log) { + log.verbose('getting changed files'); + const { + stdout + } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmto', '--exclude-standard', '--', ...Array.from(projects.values()).filter(p => kbn.isPartOfRepo(p)).map(p => p.path)], { + cwd: kbn.getAbsolute() + }); + const output = stdout.trim(); + const unassignedChanges = new Map(); + if (output) { + for (const line of output.split('\n')) { + const [tag, ...pathParts] = line.trim().split(' '); + const path = pathParts.join(' '); -/***/ }), -/* 270 */ -/***/ (function(module, exports, __webpack_require__) { + switch (tag) { + case 'M': + case 'C': + // for some reason ls-files returns deleted files as both deleted + // and modified, so make sure not to overwrite changes already + // tracked as "deleted" + if (unassignedChanges.get(path) !== 'deleted') { + unassignedChanges.set(path, 'modified'); + } -"use strict"; + break; -const SPACES_REGEXP = / +/g; + case 'R': + unassignedChanges.set(path, 'deleted'); + break; -const joinCommand = (file, args = []) => { - if (!Array.isArray(args)) { - return file; - } + case '?': + unassignedChanges.set(path, 'untracked'); + break; - return [file, ...args].join(' '); -}; + case 'H': + case 'S': + case 'K': + default: + log.warning(`unexpected modification status "${tag}" for ${path}, please report this!`); + unassignedChanges.set(path, 'invalid'); + break; + } + } + } -// Allow spaces to be escaped by a backslash if not meant as a delimiter -const handleEscaping = (tokens, token, index) => { - if (index === 0) { - return [token]; - } + const sortedRelevantProjects = Array.from(projects.values()).sort(projectBySpecificitySorter); + const changesByProject = new Map(); - const previousToken = tokens[tokens.length - 1]; + for (const project of sortedRelevantProjects) { + if (kbn.isOutsideRepo(project)) { + changesByProject.set(project, undefined); + continue; + } - if (previousToken.endsWith('\\')) { - return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`]; - } + const ownChanges = new Map(); + const prefix = kbn.getRelative(project.path); - return [...tokens, token]; -}; + for (const [path, type] of unassignedChanges) { + if (path.startsWith(prefix)) { + ownChanges.set(path, type); + unassignedChanges.delete(path); + } + } -// Handle `execa.command()` -const parseCommand = command => { - return command - .trim() - .split(SPACES_REGEXP) - .reduce(handleEscaping, []); -}; + log.verbose(`[${project.name}] found ${ownChanges.size} changes`); + changesByProject.set(project, ownChanges); + } -module.exports = { - joinCommand, - parseCommand -}; + if (unassignedChanges.size) { + throw new Error(`unable to assign all change paths to a project: ${JSON.stringify(Array.from(unassignedChanges.entries()))}`); + } + return changesByProject; +} +/** Get the latest commit sha for a project */ -/***/ }), -/* 271 */ -/***/ (function(module, exports, __webpack_require__) { -// Copyright IBM Corp. 2014,2018. All Rights Reserved. -// Node module: strong-log-transformer -// This file is licensed under the Apache License 2.0. -// License text available at https://opensource.org/licenses/Apache-2.0 +async function getLatestSha(project, kbn) { + if (kbn.isOutsideRepo(project)) { + return; + } -module.exports = __webpack_require__(272); -module.exports.cli = __webpack_require__(276); + const { + stdout + } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], { + cwd: kbn.getAbsolute() + }); + return stdout.trim() || undefined; +} +/** + * Get the checksum for a specific project in the workspace + */ -/***/ }), -/* 272 */ -/***/ (function(module, exports, __webpack_require__) { +async function getChecksum(project, changes, yarnLock, kbn, log) { + const sha = await getLatestSha(project, kbn); -"use strict"; -// Copyright IBM Corp. 2014,2018. All Rights Reserved. -// Node module: strong-log-transformer -// This file is licensed under the Apache License 2.0. -// License text available at https://opensource.org/licenses/Apache-2.0 + if (sha) { + log.verbose(`[${project.name}] local sha:`, sha); + } + if (!changes || Array.from(changes.values()).includes('invalid')) { + log.warning(`[${project.name}] unable to determine local changes, caching disabled`); + return; + } + const changesSummary = await Promise.all(Array.from(changes).sort((a, b) => a[0].localeCompare(b[0])).map(async ([path, type]) => { + if (type === 'deleted') { + return `${path}:deleted`; + } -var stream = __webpack_require__(137); -var util = __webpack_require__(111); -var fs = __webpack_require__(133); + const stats = await statAsync(kbn.getAbsolute(path)); + log.verbose(`[${project.name}] modified time ${stats.mtimeMs} for ${path}`); + return `${path}:${stats.mtimeMs}`; + })); + const depMap = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ + project, + yarnLock, + kbn, + log, + includeDependentProject: false, + productionDepsOnly: false + }); -var through = __webpack_require__(273); -var duplexer = __webpack_require__(274); -var StringDecoder = __webpack_require__(275).StringDecoder; + if (!depMap) { + return; + } -module.exports = Logger; + const deps = Array.from(depMap.values()).map(({ + name, + version + }) => `${name}@${version}`).sort((a, b) => a.localeCompare(b)); + log.verbose(`[${project.name}] resolved %d deps`, deps.length); + const checksum = JSON.stringify({ + sha, + changes: changesSummary, + deps + }, null, 2); -Logger.DEFAULTS = { - format: 'text', - tag: '', - mergeMultiline: false, - timeStamp: false, -}; + if (process.env.BOOTSTRAP_CACHE_DEBUG_CHECKSUM) { + return checksum; + } -var formatters = { - text: textFormatter, - json: jsonFormatter, + const hash = crypto__WEBPACK_IMPORTED_MODULE_1___default.a.createHash('sha1'); + hash.update(checksum); + return hash.digest('hex'); } +/** + * Calculate checksums for all projects in the workspace based on + * - last git commit to project directory + * - un-committed changes + * - resolved dependencies from yarn.lock referenced by project package.json + */ -function Logger(options) { - var defaults = JSON.parse(JSON.stringify(Logger.DEFAULTS)); - options = util._extend(defaults, options || {}); - var catcher = deLiner(); - var emitter = catcher; - var transforms = [ - objectifier(), - ]; - - if (options.tag) { - transforms.push(staticTagger(options.tag)); - } - - if (options.mergeMultiline) { - transforms.push(lineMerger()); - } - // TODO - // if (options.pidStamp) { - // transforms.push(pidStamper(options.pid)); - // } +async function getAllChecksums(kbn, log, yarnLock) { + const projects = kbn.getAllProjects(); + const changesByProject = await getChangesForProjects(projects, kbn, log); + /** map of [project.name, cacheKey] */ - // TODO - // if (options.workerStamp) { - // transforms.push(workerStamper(options.worker)); - // } + const cacheKeys = new Map(); + await Promise.all(Array.from(projects.values()).map(async project => { + cacheKeys.set(project.name, await getChecksum(project, changesByProject.get(project), yarnLock, kbn, log)); + })); + return cacheKeys; +} - transforms.push(formatters[options.format](options)); +/***/ }), +/* 278 */ +/***/ (function(module, exports) { - // restore line endings that were removed by line splitting - transforms.push(reLiner()); +module.exports = require("crypto"); - for (var t in transforms) { - emitter = emitter.pipe(transforms[t]); - } +/***/ }), +/* 279 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return duplexer(catcher, emitter); -} +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resolveDepsForProject", function() { return resolveDepsForProject; }); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(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. + */ +// @ts-expect-error published types are worthless -function deLiner() { - var decoder = new StringDecoder('utf8'); - var last = ''; - return new stream.Transform({ - transform(chunk, _enc, callback) { - last += decoder.write(chunk); - var list = last.split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g); - last = list.pop(); - for (var i = 0; i < list.length; i++) { - // swallow empty lines - if (list[i]) { - this.push(list[i]); - } - } - callback(); - }, - flush(callback) { - // incomplete UTF8 sequences become UTF8 replacement characters - last += decoder.end(); - if (last) { - this.push(last); - } - callback(); - }, - }); -} +async function readYarnLock(kbn) { + try { + const contents = await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_1__["readFile"])(kbn.getAbsolute('yarn.lock'), 'utf8'); + const yarnLock = Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["parse"])(contents); -function reLiner() { - return through(appendNewline); + if (yarnLock.type === 'success') { + return yarnLock.object; + } - function appendNewline(line) { - this.emit('data', line + '\n'); + throw new Error('unable to read yarn.lock file, please run `yarn kbn bootstrap`'); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } } -} -function objectifier() { - return through(objectify, null, {autoDestroy: false}); - - function objectify(line) { - this.emit('data', { - msg: line, - time: Date.now(), - }); - } + return {}; } +/** + * Get a list of the absolute dependencies of this project, as resolved + * in the yarn.lock file, does not include other projects in the workspace + * or their dependencies + */ -function staticTagger(tag) { - return through(tagger); +function resolveDepsForProject({ + project: rootProject, + yarnLock, + kbn, + log, + productionDepsOnly, + includeDependentProject +}) { + /** map of [name@range, { name, version }] */ + const resolved = new Map(); + const seenProjects = new Set(); + const projectQueue = [rootProject]; + const depQueue = []; - function tagger(logEvent) { - logEvent.tag = tag; - this.emit('data', logEvent); - } -} + while (projectQueue.length) { + const project = projectQueue.shift(); -function textFormatter(options) { - return through(textify); + if (seenProjects.has(project)) { + continue; + } - function textify(logEvent) { - var line = util.format('%s%s', textifyTags(logEvent.tag), - logEvent.msg.toString()); - if (options.timeStamp) { - line = util.format('%s %s', new Date(logEvent.time).toISOString(), line); + seenProjects.add(project); + const projectDeps = Object.entries(productionDepsOnly ? project.productionDependencies : project.allDependencies); + + for (const [name, versionRange] of projectDeps) { + depQueue.push([name, versionRange]); } - this.emit('data', line.replace(/\n/g, '\\n')); - } - function textifyTags(tags) { - var str = ''; - if (typeof tags === 'string') { - str = tags + ' '; - } else if (typeof tags === 'object') { - for (var t in tags) { - str += t + ':' + tags[t] + ' '; + while (depQueue.length) { + const [name, versionRange] = depQueue.shift(); + const req = `${name}@${versionRange}`; + + if (resolved.has(req)) { + continue; } - } - return str; - } -} -function jsonFormatter(options) { - return through(jsonify); + if (includeDependentProject && kbn.hasProject(name)) { + projectQueue.push(kbn.getProject(name)); + } - function jsonify(logEvent) { - if (options.timeStamp) { - logEvent.time = new Date(logEvent.time).toISOString(); - } else { - delete logEvent.time; - } - logEvent.msg = logEvent.msg.toString(); - this.emit('data', JSON.stringify(logEvent)); - } -} + if (!kbn.hasProject(name)) { + const pkg = yarnLock[req]; -function lineMerger(host) { - var previousLine = null; - var flushTimer = null; - var stream = through(lineMergerWrite, lineMergerEnd); - var flush = _flush.bind(stream); + if (!pkg) { + log.warning('yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching'); + return; + } - return stream; + resolved.set(req, { + name, + version: pkg.version + }); + const allDepsEntries = [...Object.entries(pkg.dependencies || {}), ...Object.entries(pkg.optionalDependencies || {})]; - function lineMergerWrite(line) { - if (/^\s+/.test(line.msg)) { - if (previousLine) { - previousLine.msg += '\n' + line.msg; - } else { - previousLine = line; + for (const [childName, childVersionRange] of allDepsEntries) { + depQueue.push([childName, childVersionRange]); + } } - } else { - flush(); - previousLine = line; - } - // rolling timeout - clearTimeout(flushTimer); - flushTimer = setTimeout(flush.bind(this), 10); - } - - function _flush() { - if (previousLine) { - this.emit('data', previousLine); - previousLine = null; } } - function lineMergerEnd() { - flush.call(this); - this.emit('end'); - } + return resolved; } - /***/ }), -/* 273 */ +/* 280 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(137) +module.exports = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 14); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { -// through -// -// a stream that does nothing but re-emit the input. -// useful for aggregating a series of changing but not ending streams into one stream) +module.exports = __webpack_require__(4); -exports = module.exports = through -through.through = through +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { -//create a readable writable stream. +"use strict"; -function through (write, end, opts) { - write = write || function (data) { this.queue(data) } - end = end || function () { this.queue(null) } - var ended = false, destroyed = false, buffer = [], _ended = false - var stream = new Stream() - stream.readable = stream.writable = true - stream.paused = false +exports.__esModule = true; -// stream.autoPause = !(opts && opts.autoPause === false) - stream.autoDestroy = !(opts && opts.autoDestroy === false) +var _promise = __webpack_require__(173); - stream.write = function (data) { - write.call(this, data) - return !stream.paused - } +var _promise2 = _interopRequireDefault(_promise); - function drain() { - while(buffer.length && !stream.paused) { - var data = buffer.shift() - if(null === data) - return stream.emit('end') - else - stream.emit('data', data) - } - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - stream.queue = stream.push = function (data) { -// console.error(ended) - if(_ended) return stream - if(data === null) _ended = true - buffer.push(data) - drain() - return stream - } +exports.default = function (fn) { + return function () { + var gen = fn.apply(this, arguments); + return new _promise2.default(function (resolve, reject) { + function step(key, arg) { + try { + var info = gen[key](arg); + var value = info.value; + } catch (error) { + reject(error); + return; + } - //this will be registered as the first 'end' listener - //must call destroy next tick, to make sure we're after any - //stream piped from here. - //this is only a problem if end is not emitted synchronously. - //a nicer way to do this is to make sure this is the last listener for 'end' + if (info.done) { + resolve(value); + } else { + return _promise2.default.resolve(value).then(function (value) { + step("next", value); + }, function (err) { + step("throw", err); + }); + } + } - stream.on('end', function () { - stream.readable = false - if(!stream.writable && stream.autoDestroy) - process.nextTick(function () { - stream.destroy() - }) - }) + return step("next"); + }); + }; +}; - function _end () { - stream.writable = false - end.call(stream) - if(!stream.readable && stream.autoDestroy) - stream.destroy() - } +/***/ }), +/* 2 */ +/***/ (function(module, exports) { - stream.end = function (data) { - if(ended) return - ended = true - if(arguments.length) stream.write(data) - _end() // will emit or queue - return stream - } +module.exports = __webpack_require__(111); - stream.destroy = function () { - if(destroyed) return - destroyed = true - ended = true - buffer.length = 0 - stream.writable = stream.readable = false - stream.emit('close') - return stream - } +/***/ }), +/* 3 */ +/***/ (function(module, exports) { - stream.pause = function () { - if(stream.paused) return - stream.paused = true - return stream - } +module.exports = __webpack_require__(133); - stream.resume = function () { - if(stream.paused) { - stream.paused = false - stream.emit('resume') - } - drain() - //may have become paused again, - //as drain emits 'data'. - if(!stream.paused) - stream.emit('drain') - return stream +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +class MessageError extends Error { + constructor(msg, code) { + super(msg); + this.code = code; } - return stream -} +} +exports.MessageError = MessageError; +class ProcessSpawnError extends MessageError { + constructor(msg, code, process) { + super(msg, code); + this.process = process; + } -/***/ }), -/* 274 */ -/***/ (function(module, exports, __webpack_require__) { +} -var Stream = __webpack_require__(137) -var writeMethods = ["write", "end", "destroy"] -var readMethods = ["resume", "pause"] -var readEvents = ["data", "close"] -var slice = Array.prototype.slice +exports.ProcessSpawnError = ProcessSpawnError; +class SecurityError extends MessageError {} -module.exports = duplex +exports.SecurityError = SecurityError; +class ProcessTermError extends MessageError {} -function forEach (arr, fn) { - if (arr.forEach) { - return arr.forEach(fn) - } +exports.ProcessTermError = ProcessTermError; +class ResponseError extends Error { + constructor(msg, responseCode) { + super(msg); + this.responseCode = responseCode; + } - for (var i = 0; i < arr.length; i++) { - fn(arr[i], i) - } } +exports.ResponseError = ResponseError; -function duplex(writer, reader) { - var stream = new Stream() - var ended = false - - forEach(writeMethods, proxyWriter) +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { - forEach(readMethods, proxyReader) +"use strict"; - forEach(readEvents, proxyStream) - reader.on("end", handleEnd) +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; - writer.on("drain", function() { - stream.emit("drain") - }) +var _asyncToGenerator2; - writer.on("error", reemit) - reader.on("error", reemit) +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +} - stream.writable = writer.writable - stream.readable = reader.readable +let buildActionsForCopy = (() => { + var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { - return stream + // + let build = (() => { + var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest, + type = data.type; - function proxyWriter(methodName) { - stream[methodName] = method + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; - function method() { - return writer[methodName].apply(writer, arguments) + // TODO https://github.com/yarnpkg/yarn/issues/3751 + // related to bundled dependencies handling + if (files.has(dest.toLowerCase())) { + reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); + } else { + files.add(dest.toLowerCase()); } - } - function proxyReader(methodName) { - stream[methodName] = method + if (type === 'symlink') { + yield mkdirp((_path || _load_path()).default.dirname(dest)); + onFresh(); + actions.symlink.push({ + dest, + linkname: src + }); + onDone(); + return; + } - function method() { - stream.emit(methodName) - var func = reader[methodName] - if (func) { - return func.apply(reader, arguments) - } - reader.emit(methodName) + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; } - } - function proxyStream(methodName) { - reader.on(methodName, reemit) + const srcStat = yield lstat(src); + let srcFiles; - function reemit() { - var args = slice.call(arguments) - args.unshift(methodName) - stream.emit.apply(stream, args) + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); } - } - function handleEnd() { - if (ended) { - return + let destStat; + try { + // try accessing the destination + destStat = yield lstat(dest); + } catch (e) { + // proceed if destination doesn't exist, otherwise error + if (e.code !== 'ENOENT') { + throw e; + } } - ended = true - var args = slice.call(arguments) - args.unshift("end") - stream.emit.apply(stream, args) - } - function reemit(err) { - stream.emit("error", err) - } -} + // if destination exists + if (destStat) { + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. -/***/ }), -/* 275 */ -/***/ (function(module, exports) { + /* if (srcStat.mode !== destStat.mode) { + try { + await access(dest, srcStat.mode); + } catch (err) {} + } */ -module.exports = require("string_decoder"); + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } -/***/ }), -/* 276 */ -/***/ (function(module, exports, __webpack_require__) { + if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { + // we can safely assume this is the same file + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); + return; + } -"use strict"; -// Copyright IBM Corp. 2014,2018. All Rights Reserved. -// Node module: strong-log-transformer -// This file is licensed under the Apache License 2.0. -// License text available at https://opensource.org/licenses/Apache-2.0 + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); + for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref6; -var minimist = __webpack_require__(277); -var path = __webpack_require__(4); + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref6 = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref6 = _i4.value; + } -var Logger = __webpack_require__(272); -var pkg = __webpack_require__(278); + const file = _ref6; -module.exports = cli; + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); -function cli(args) { - var opts = minimist(args.slice(2)); - var $0 = path.basename(args[1]); - var p = console.log.bind(console); - if (opts.v || opts.version) { - version($0, p); - } else if (opts.h || opts.help) { - usage($0, p); - } else if (args.length < 3) { - process.stdin.pipe(Logger()).pipe(process.stdout); - } else { - process.stdin.pipe(Logger(opts)).pipe(process.stdout); - } -} + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref7; -function version($0, p) { - p('%s v%s', pkg.name, pkg.version); -} + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref7 = _iterator5[_i5++]; + } else { + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref7 = _i5.value; + } -function usage($0, p) { - var PADDING = ' '; - var opt, def; - p('Usage: %s [options]', $0); - p(''); - p('%s', pkg.description); - p(''); - p('OPTIONS:'); - for (opt in Logger.DEFAULTS) { - def = Logger.DEFAULTS[opt]; - if (typeof def === 'boolean') - boolOpt(opt, Logger.DEFAULTS[opt]); - else - stdOpt(opt, Logger.DEFAULTS[opt]); - } - p(''); + const file = _ref7; - function boolOpt(name, def) { - name = name + PADDING.slice(0, 20-name.length); - p(' --%s default: %s', name, def); - } + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } + } - function stdOpt(name, def) { - var value = name.toUpperCase() + - PADDING.slice(0, 19 - name.length*2); - p(' --%s %s default: %j', name, value, def); - } -} + if (destStat && destStat.isSymbolicLink()) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); + destStat = null; + } + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + if (!destStat) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); + } -/***/ }), -/* 277 */ -/***/ (function(module, exports) { + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } -module.exports = function (args, opts) { - if (!opts) opts = {}; - - var flags = { bools : {}, strings : {}, unknownFn: null }; + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref8; - if (typeof opts['unknown'] === 'function') { - flags.unknownFn = opts['unknown']; - } + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref8 = _iterator6[_i6++]; + } else { + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref8 = _i6.value; + } - if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { - flags.allBools = true; - } else { - [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { - flags.bools[key] = true; - }); - } - - var aliases = {}; - Object.keys(opts.alias || {}).forEach(function (key) { - aliases[key] = [].concat(opts.alias[key]); - aliases[key].forEach(function (x) { - aliases[x] = [key].concat(aliases[key].filter(function (y) { - return x !== y; - })); - }); - }); + const file = _ref8; - [].concat(opts.string).filter(Boolean).forEach(function (key) { - flags.strings[key] = true; - if (aliases[key]) { - flags.strings[aliases[key]] = true; - } - }); + queue.push({ + dest: (_path || _load_path()).default.join(dest, file), + onFresh, + onDone: function (_onDone) { + function onDone() { + return _onDone.apply(this, arguments); + } - var defaults = opts['default'] || {}; - - var argv = { _ : [] }; - Object.keys(flags.bools).forEach(function (key) { - setArg(key, defaults[key] === undefined ? false : defaults[key]); - }); - - var notFlags = []; + onDone.toString = function () { + return _onDone.toString(); + }; - if (args.indexOf('--') !== -1) { - notFlags = args.slice(args.indexOf('--')+1); - args = args.slice(0, args.indexOf('--')); - } + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }), + src: (_path || _load_path()).default.join(src, file) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.file.push({ + src, + dest, + atime: srcStat.atime, + mtime: srcStat.mtime, + mode: srcStat.mode + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); - function argDefined(key, arg) { - return (flags.allBools && /^--[^=]+$/.test(arg)) || - flags.strings[key] || flags.bools[key] || aliases[key]; - } + return function build(_x5) { + return _ref5.apply(this, arguments); + }; + })(); - function setArg (key, val, arg) { - if (arg && flags.unknownFn && !argDefined(key, arg)) { - if (flags.unknownFn(arg) === false) return; - } + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); - var value = !flags.strings[key] && isNumber(val) - ? Number(val) : val - ; - setKey(argv, key.split('.'), value); - - (aliases[key] || []).forEach(function (x) { - setKey(argv, x.split('.'), value); - }); - } + // initialise events + for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref2; - function setKey (obj, keys, value) { - var o = obj; - for (var i = 0; i < keys.length-1; i++) { - var key = keys[i]; - if (key === '__proto__') return; - if (o[key] === undefined) o[key] = {}; - if (o[key] === Object.prototype || o[key] === Number.prototype - || o[key] === String.prototype) o[key] = {}; - if (o[key] === Array.prototype) o[key] = []; - o = o[key]; - } + if (_isArray) { + if (_i >= _iterator.length) break; + _ref2 = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref2 = _i.value; + } - var key = keys[keys.length - 1]; - if (key === '__proto__') return; - if (o === Object.prototype || o === Number.prototype - || o === String.prototype) o = {}; - if (o === Array.prototype) o = []; - if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') { - o[key] = value; - } - else if (Array.isArray(o[key])) { - o[key].push(value); - } - else { - o[key] = [ o[key], value ]; - } - } - - function aliasIsBoolean(key) { - return aliases[key].some(function (x) { - return flags.bools[x]; - }); - } + const item = _ref2; - for (var i = 0; i < args.length; i++) { - var arg = args[i]; - - if (/^--.+=/.test(arg)) { - // Using [\s\S] instead of . because js doesn't support the - // 'dotall' regex modifier. See: - // http://stackoverflow.com/a/1068308/13216 - var m = arg.match(/^--([^=]+)=([\s\S]*)$/); - var key = m[1]; - var value = m[2]; - if (flags.bools[key]) { - value = value !== 'false'; - } - setArg(key, value, arg); - } - else if (/^--no-.+/.test(arg)) { - var key = arg.match(/^--no-(.+)/)[1]; - setArg(key, false, arg); - } - else if (/^--.+/.test(arg)) { - var key = arg.match(/^--(.+)/)[1]; - var next = args[i + 1]; - if (next !== undefined && !/^-/.test(next) - && !flags.bools[key] - && !flags.allBools - && (aliases[key] ? !aliasIsBoolean(key) : true)) { - setArg(key, next, arg); - i++; - } - else if (/^(true|false)$/.test(next)) { - setArg(key, next === 'true', arg); - i++; - } - else { - setArg(key, flags.strings[key] ? '' : true, arg); - } - } - else if (/^-[^-]+/.test(arg)) { - var letters = arg.slice(1,-1).split(''); - - var broken = false; - for (var j = 0; j < letters.length; j++) { - var next = arg.slice(j+2); - - if (next === '-') { - setArg(letters[j], next, arg) - continue; - } - - if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { - setArg(letters[j], next.split('=')[1], arg); - broken = true; - break; - } - - if (/[A-Za-z]/.test(letters[j]) - && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { - setArg(letters[j], next, arg); - broken = true; - break; - } - - if (letters[j+1] && letters[j+1].match(/\W/)) { - setArg(letters[j], arg.slice(j+2), arg); - broken = true; - break; - } - else { - setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); - } - } - - var key = arg.slice(-1)[0]; - if (!broken && key !== '-') { - if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) - && !flags.bools[key] - && (aliases[key] ? !aliasIsBoolean(key) : true)) { - setArg(key, args[i+1], arg); - i++; - } - else if (args[i+1] && /^(true|false)$/.test(args[i+1])) { - setArg(key, args[i+1] === 'true', arg); - i++; - } - else { - setArg(key, flags.strings[key] ? '' : true, arg); - } - } - } - else { - if (!flags.unknownFn || flags.unknownFn(arg) !== false) { - argv._.push( - flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) - ); - } - if (opts.stopEarly) { - argv._.push.apply(argv._, args.slice(i + 1)); - break; - } - } - } - - Object.keys(defaults).forEach(function (key) { - if (!hasKey(argv, key.split('.'))) { - setKey(argv, key.split('.'), defaults[key]); - - (aliases[key] || []).forEach(function (x) { - setKey(argv, x.split('.'), defaults[key]); - }); + const onDone = item.onDone; + item.onDone = function () { + events.onProgress(item.dest); + if (onDone) { + onDone(); } - }); - - if (opts['--']) { - argv['--'] = new Array(); - notFlags.forEach(function(key) { - argv['--'].push(key); - }); + }; } - else { - notFlags.forEach(function(key) { - argv._.push(key); - }); + events.onStart(queue.length); + + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; + + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); } - return argv; -}; + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref3; -function hasKey (obj, keys) { - var o = obj; - keys.slice(0,-1).forEach(function (key) { - o = (o[key] || {}); - }); + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref3 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref3 = _i2.value; + } - var key = keys[keys.length - 1]; - return key in o; -} + const file = _ref3; -function isNumber (x) { - if (typeof x === 'number') return true; - if (/^0x[0-9a-f]+$/i.test(x)) return true; - return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); -} + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); + } + } + for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref4; + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref4 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref4 = _i3.value; + } -/***/ }), -/* 278 */ -/***/ (function(module) { + const loc = _ref4; -module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham \",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); + } + } -/***/ }), -/* 279 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + return actions; + }); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "workspacePackagePaths", function() { return workspacePackagePaths; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return copyWorkspacePackages; }); -/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(146); -/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(glob__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(164); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(145); -/* - * 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. - */ + return function buildActionsForCopy(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); + }; +})(); +let buildActionsForHardlink = (() => { + var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { + // + let build = (() => { + var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest; + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; + if (files.has(dest.toLowerCase())) { + // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 + // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, + // package-linker passes that modules A1 and B1 need to be hardlinked, + // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case + // an exception. + onDone(); + return; + } + files.add(dest.toLowerCase()); + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; + } + const srcStat = yield lstat(src); + let srcFiles; + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); + } -const glob = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(glob__WEBPACK_IMPORTED_MODULE_0___default.a); -async function workspacePackagePaths(rootPath) { - const rootPkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["readPackageJson"])(rootPath); + const destExists = yield exists(dest); + if (destExists) { + const destStat = yield lstat(dest); - if (!rootPkgJson.workspaces) { - return []; - } + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); - const workspacesPathsPatterns = rootPkgJson.workspaces.packages; - let workspaceProjectsPaths = []; + if (srcStat.mode !== destStat.mode) { + try { + yield access(dest, srcStat.mode); + } catch (err) { + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. + reporter.verbose(err); + } + } - for (const pattern of workspacesPathsPatterns) { - workspaceProjectsPaths = workspaceProjectsPaths.concat(await packagesFromGlobPattern({ - pattern, - rootPath - })); - } // Filter out exclude glob patterns + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } + // correct hardlink + if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); + return; + } - for (const pattern of workspacesPathsPatterns) { - if (pattern.startsWith('!')) { - const pathToRemove = path__WEBPACK_IMPORTED_MODULE_1___default.a.join(rootPath, pattern.slice(1), 'package.json'); - workspaceProjectsPaths = workspaceProjectsPaths.filter(p => p !== pathToRemove); - } - } + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } - return workspaceProjectsPaths; -} -async function copyWorkspacePackages(rootPath) { - const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ - rootPath - }); - const projects = await Object(_projects__WEBPACK_IMPORTED_MODULE_6__["getProjects"])(rootPath, projectPaths); + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); - for (const project of projects.values()) { - const dest = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(rootPath, 'node_modules', project.name); + for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref14; - if ((await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["isSymlink"])(dest)) === false) { - continue; - } // Remove the symlink + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref14 = _iterator10[_i10++]; + } else { + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref14 = _i10.value; + } + const file = _ref14; - await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["unlink"])(dest); // Copy in the package + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); - await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["copyDirectory"])(project.path, dest); - } -} + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { + var _ref15; -function packagesFromGlobPattern({ - pattern, - rootPath -}) { - const globOptions = { - cwd: rootPath, - // Should throw in case of unusual errors when reading the file system - strict: true, - // Always returns absolute paths for matched files - absolute: true, - // Do not match ** against multiple filenames - // (This is only specified because we currently don't have a need for it.) - noglobstar: true - }; - return glob(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(pattern, 'package.json'), globOptions); -} - -/***/ }), -/* 280 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_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. - */ + if (_isArray11) { + if (_i11 >= _iterator11.length) break; + _ref15 = _iterator11[_i11++]; + } else { + _i11 = _iterator11.next(); + if (_i11.done) break; + _ref15 = _i11.value; + } + const file = _ref15; -/** - * Returns all the paths where plugins are located - */ -function getProjectPaths({ - rootPath, - ossOnly, - skipKibanaPlugins -}) { - const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared - // plugin functional used in the selenium functional tests. - // As we are now using the webpack dll for the client vendors dependencies - // when we run the plugin functional tests against the distributable - // dependencies used by such plugins like @eui, react and react-dom can't - // be loaded from the dll as the context is different from the one declared - // into the webpack dll reference plugin. - // In anyway, have a plugin declaring their own dependencies is the - // correct and the expect behavior. + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } + } - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); - if (!ossOnly) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); - } + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } - if (!skipKibanaPlugins) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); - } + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { + var _ref16; - return projectPaths; -} + if (_isArray12) { + if (_i12 >= _iterator12.length) break; + _ref16 = _iterator12[_i12++]; + } else { + _i12 = _iterator12.next(); + if (_i12.done) break; + _ref16 = _i12.value; + } -/***/ }), -/* 281 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + const file = _ref16; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(282); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(236); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(283); -/* - * 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. - */ + queue.push({ + onFresh, + src: (_path || _load_path()).default.join(src, file), + dest: (_path || _load_path()).default.join(dest, file), + onDone: function (_onDone2) { + function onDone() { + return _onDone2.apply(this, arguments); + } + onDone.toString = function () { + return _onDone2.toString(); + }; + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.link.push({ + src, + dest, + removeDest: destExists + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); + return function build(_x10) { + return _ref13.apply(this, arguments); + }; + })(); + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); -const statAsync = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(fs__WEBPACK_IMPORTED_MODULE_0___default.a.stat); + // initialise events + for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + var _ref10; -const projectBySpecificitySorter = (a, b) => b.path.length - a.path.length; -/** Get the changed files for a set of projects */ + if (_isArray7) { + if (_i7 >= _iterator7.length) break; + _ref10 = _iterator7[_i7++]; + } else { + _i7 = _iterator7.next(); + if (_i7.done) break; + _ref10 = _i7.value; + } + const item = _ref10; -async function getChangesForProjects(projects, kbn, log) { - log.verbose('getting changed files'); - const { - stdout - } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmto', '--exclude-standard', '--', ...Array.from(projects.values()).filter(p => kbn.isPartOfRepo(p)).map(p => p.path)], { - cwd: kbn.getAbsolute() - }); - const output = stdout.trim(); - const unassignedChanges = new Map(); + const onDone = item.onDone || noop; + item.onDone = function () { + events.onProgress(item.dest); + onDone(); + }; + } + events.onStart(queue.length); - if (output) { - for (const line of output.split('\n')) { - const [tag, ...pathParts] = line.trim().split(' '); - const path = pathParts.join(' '); + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; - switch (tag) { - case 'M': - case 'C': - // for some reason ls-files returns deleted files as both deleted - // and modified, so make sure not to overwrite changes already - // tracked as "deleted" - if (unassignedChanges.get(path) !== 'deleted') { - unassignedChanges.set(path, 'modified'); - } + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); + } - break; + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref11; - case 'R': - unassignedChanges.set(path, 'deleted'); - break; + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref11 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref11 = _i8.value; + } - case '?': - unassignedChanges.set(path, 'untracked'); - break; + const file = _ref11; - case 'H': - case 'S': - case 'K': - default: - log.warning(`unexpected modification status "${tag}" for ${path}, please report this!`); - unassignedChanges.set(path, 'invalid'); - break; + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); } } - } - const sortedRelevantProjects = Array.from(projects.values()).sort(projectBySpecificitySorter); - const changesByProject = new Map(); + for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref12; - for (const project of sortedRelevantProjects) { - if (kbn.isOutsideRepo(project)) { - changesByProject.set(project, undefined); - continue; - } + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref12 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref12 = _i9.value; + } - const ownChanges = new Map(); - const prefix = kbn.getRelative(project.path); + const loc = _ref12; - for (const [path, type] of unassignedChanges) { - if (path.startsWith(prefix)) { - ownChanges.set(path, type); - unassignedChanges.delete(path); + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); } } - log.verbose(`[${project.name}] found ${ownChanges.size} changes`); - changesByProject.set(project, ownChanges); - } + return actions; + }); - if (unassignedChanges.size) { - throw new Error(`unable to assign all change paths to a project: ${JSON.stringify(Array.from(unassignedChanges.entries()))}`); - } + return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { + return _ref9.apply(this, arguments); + }; +})(); - return changesByProject; -} -/** Get the latest commit sha for a project */ +let copyBulk = exports.copyBulk = (() => { + var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + ignoreBasenames: _events && _events.ignoreBasenames || [], + artifactFiles: _events && _events.artifactFiles || [] + }; + const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); -async function getLatestSha(project, kbn) { - if (kbn.isOutsideRepo(project)) { - return; - } + const fileActions = actions.file; - const { - stdout - } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], { - cwd: kbn.getAbsolute() - }); - return stdout.trim() || undefined; -} -/** - * Get a list of the absolute dependencies of this project, as resolved - * in the yarn.lock file, does not include other projects in the workspace - * or their dependencies - */ + const currentlyWriting = new Map(); + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + let writePromise; + while (writePromise = currentlyWriting.get(data.dest)) { + yield writePromise; + } -function resolveDepsForProject(project, yarnLock, kbn, log) { - /** map of [name@range, name@resolved] */ - const resolved = new Map(); - const queue = Object.entries(project.allDependencies); + reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); + const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { + return currentlyWriting.delete(data.dest); + }); + currentlyWriting.set(data.dest, copier); + events.onProgress(data.dest); + return copier; + }); - while (queue.length) { - const [name, versionRange] = queue.shift(); - const req = `${name}@${versionRange}`; + return function (_x14) { + return _ref18.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); - if (resolved.has(req)) { - continue; - } + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); - if (!kbn.hasProject(name)) { - const pkg = yarnLock[req]; + return function copyBulk(_x11, _x12, _x13) { + return _ref17.apply(this, arguments); + }; +})(); - if (!pkg) { - log.warning('yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching'); - return; - } +let hardlinkBulk = exports.hardlinkBulk = (() => { + var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + artifactFiles: _events && _events.artifactFiles || [], + ignoreBasenames: [] + }; - const res = `${name}@${pkg.version}`; - resolved.set(req, res); - const allDepsEntries = [...Object.entries(pkg.dependencies || {}), ...Object.entries(pkg.optionalDependencies || {})]; + const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); - for (const [childName, childVersionRange] of allDepsEntries) { - queue.push([childName, childVersionRange]); - } - } - } + const fileActions = actions.link; - return Array.from(resolved.values()).sort((a, b) => a.localeCompare(b)); -} -/** - * Get the checksum for a specific project in the workspace - */ + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); + if (data.removeDest) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); + } + yield link(data.src, data.dest); + }); + return function (_x18) { + return _ref20.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); -async function getChecksum(project, changes, yarnLock, kbn, log) { - const sha = await getLatestSha(project, kbn); + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); - if (sha) { - log.verbose(`[${project.name}] local sha:`, sha); - } + return function hardlinkBulk(_x15, _x16, _x17) { + return _ref19.apply(this, arguments); + }; +})(); - if (!changes || Array.from(changes.values()).includes('invalid')) { - log.warning(`[${project.name}] unable to determine local changes, caching disabled`); - return; - } +let readFileAny = exports.readFileAny = (() => { + var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { + for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { + var _ref22; - const changesSummary = await Promise.all(Array.from(changes).sort((a, b) => a[0].localeCompare(b[0])).map(async ([path, type]) => { - if (type === 'deleted') { - return `${path}:deleted`; - } + if (_isArray13) { + if (_i13 >= _iterator13.length) break; + _ref22 = _iterator13[_i13++]; + } else { + _i13 = _iterator13.next(); + if (_i13.done) break; + _ref22 = _i13.value; + } - const stats = await statAsync(kbn.getAbsolute(path)); - log.verbose(`[${project.name}] modified time ${stats.mtimeMs} for ${path}`); - return `${path}:${stats.mtimeMs}`; - })); - const deps = await resolveDepsForProject(project, yarnLock, kbn, log); + const file = _ref22; - if (!deps) { - return; - } + if (yield exists(file)) { + return readFile(file); + } + } + return null; + }); - log.verbose(`[${project.name}] resolved %d deps`, deps.length); - const checksum = JSON.stringify({ - sha, - changes: changesSummary, - deps - }, null, 2); + return function readFileAny(_x19) { + return _ref21.apply(this, arguments); + }; +})(); - if (process.env.BOOTSTRAP_CACHE_DEBUG_CHECKSUM) { - return checksum; - } +let readJson = exports.readJson = (() => { + var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + return (yield readJsonAndFile(loc)).object; + }); - const hash = crypto__WEBPACK_IMPORTED_MODULE_1___default.a.createHash('sha1'); - hash.update(checksum); - return hash.digest('hex'); -} -/** - * Calculate checksums for all projects in the workspace based on - * - last git commit to project directory - * - un-committed changes - * - resolved dependencies from yarn.lock referenced by project package.json - */ + return function readJson(_x20) { + return _ref23.apply(this, arguments); + }; +})(); +let readJsonAndFile = exports.readJsonAndFile = (() => { + var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const file = yield readFile(loc); + try { + return { + object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), + content: file + }; + } catch (err) { + err.message = `${loc}: ${err.message}`; + throw err; + } + }); -async function getAllChecksums(kbn, log) { - const projects = kbn.getAllProjects(); - const changesByProject = await getChangesForProjects(projects, kbn, log); - const yarnLock = await Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["readYarnLock"])(kbn); - /** map of [project.name, cacheKey] */ - - const cacheKeys = new Map(); - await Promise.all(Array.from(projects.values()).map(async project => { - cacheKeys.set(project.name, await getChecksum(project, changesByProject.get(project), yarnLock, kbn, log)); - })); - return cacheKeys; -} + return function readJsonAndFile(_x21) { + return _ref24.apply(this, arguments); + }; +})(); -/***/ }), -/* 282 */ -/***/ (function(module, exports) { +let find = exports.find = (() => { + var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { + const parts = dir.split((_path || _load_path()).default.sep); -module.exports = require("crypto"); + while (parts.length) { + const loc = parts.concat(filename).join((_path || _load_path()).default.sep); -/***/ }), -/* 283 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (yield exists(loc)) { + return loc; + } else { + parts.pop(); + } + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(284); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(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. - */ -// @ts-ignore published types are worthless + return false; + }); + return function find(_x22, _x23) { + return _ref25.apply(this, arguments); + }; +})(); -async function readYarnLock(kbn) { - try { - const contents = await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_1__["readFile"])(kbn.getAbsolute('yarn.lock'), 'utf8'); - const yarnLock = Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["parse"])(contents); +let symlink = exports.symlink = (() => { + var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { + try { + const stats = yield lstat(dest); + if (stats.isSymbolicLink()) { + const resolved = yield realpath(dest); + if (resolved === src) { + return; + } + } + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + // We use rimraf for unlink which never throws an ENOENT on missing target + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - if (yarnLock.type === 'success') { - return yarnLock.object; + if (process.platform === 'win32') { + // use directory junctions if possible on win32, this requires absolute paths + yield fsSymlink(src, dest, 'junction'); + } else { + // use relative paths otherwise which will be retained if the directory is moved + let relative; + try { + relative = (_path || _load_path()).default.relative((_fs || _load_fs()).default.realpathSync((_path || _load_path()).default.dirname(dest)), (_fs || _load_fs()).default.realpathSync(src)); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + relative = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); + } + // When path.relative returns an empty string for the current directory, we should instead use + // '.', which is a valid fs.symlink target. + yield fsSymlink(relative || '.', dest); } + }); - throw new Error('unable to read yarn.lock file, please run `yarn kbn bootstrap`'); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; + return function symlink(_x24, _x25) { + return _ref26.apply(this, arguments); + }; +})(); + +let walk = exports.walk = (() => { + var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { + let files = []; + + let filenames = yield readdir(dir); + if (ignoreBasenames.size) { + filenames = filenames.filter(function (name) { + return !ignoreBasenames.has(name); + }); } - } - return {}; -} + for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { + var _ref28; -/***/ }), -/* 284 */ -/***/ (function(module, exports, __webpack_require__) { + if (_isArray14) { + if (_i14 >= _iterator14.length) break; + _ref28 = _iterator14[_i14++]; + } else { + _i14 = _iterator14.next(); + if (_i14.done) break; + _ref28 = _i14.value; + } -module.exports = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 14); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports) { + const name = _ref28; -module.exports = __webpack_require__(4); + const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; + const loc = (_path || _load_path()).default.join(dir, name); + const stat = yield lstat(loc); -/***/ }), -/* 1 */ -/***/ (function(module, exports, __webpack_require__) { + files.push({ + relative, + basename: name, + absolute: loc, + mtime: +stat.mtime + }); -"use strict"; + if (stat.isDirectory()) { + files = files.concat((yield walk(loc, relative, ignoreBasenames))); + } + } + return files; + }); -exports.__esModule = true; + return function walk(_x26, _x27) { + return _ref27.apply(this, arguments); + }; +})(); -var _promise = __webpack_require__(173); +let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { + var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const stat = yield lstat(loc); + const size = stat.size, + blockSize = stat.blksize; -var _promise2 = _interopRequireDefault(_promise); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + return Math.ceil(size / blockSize) * blockSize; + }); -exports.default = function (fn) { - return function () { - var gen = fn.apply(this, arguments); - return new _promise2.default(function (resolve, reject) { - function step(key, arg) { - try { - var info = gen[key](arg); - var value = info.value; - } catch (error) { - reject(error); - return; - } + return function getFileSizeOnDisk(_x28) { + return _ref29.apply(this, arguments); + }; +})(); - if (info.done) { - resolve(value); - } else { - return _promise2.default.resolve(value).then(function (value) { - step("next", value); - }, function (err) { - step("throw", err); - }); - } +let getEolFromFile = (() => { + var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { + if (!(yield exists(path))) { + return undefined; + } + + const buffer = yield readFileBuffer(path); + + for (let i = 0; i < buffer.length; ++i) { + if (buffer[i] === cr) { + return '\r\n'; + } + if (buffer[i] === lf) { + return '\n'; } + } + return undefined; + }); - return step("next"); - }); + return function getEolFromFile(_x29) { + return _ref30.apply(this, arguments); }; -}; +})(); -/***/ }), -/* 2 */ -/***/ (function(module, exports) { +let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { + var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { + const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; + if (eol !== '\n') { + data = data.replace(/\n/g, eol); + } + yield writeFile(path, data); + }); -module.exports = __webpack_require__(111); + return function writeFilePreservingEol(_x30, _x31) { + return _ref31.apply(this, arguments); + }; +})(); -/***/ }), -/* 3 */ -/***/ (function(module, exports) { +let hardlinksWork = exports.hardlinksWork = (() => { + var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { + const filename = 'test-file' + Math.random(); + const file = (_path || _load_path()).default.join(dir, filename); + const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); + try { + yield writeFile(file, 'test'); + yield link(file, fileLink); + } catch (err) { + return false; + } finally { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); + } + return true; + }); -module.exports = __webpack_require__(133); + return function hardlinksWork(_x32) { + return _ref32.apply(this, arguments); + }; +})(); -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { +// not a strict polyfill for Node's fs.mkdtemp -"use strict"; +let makeTempDir = exports.makeTempDir = (() => { + var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { + const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); + yield mkdirp(dir); + return dir; + }); -Object.defineProperty(exports, "__esModule", { - value: true -}); -class MessageError extends Error { - constructor(msg, code) { - super(msg); - this.code = code; - } + return function makeTempDir(_x33) { + return _ref33.apply(this, arguments); + }; +})(); -} +let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { + var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { + for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { + var _ref35; -exports.MessageError = MessageError; -class ProcessSpawnError extends MessageError { - constructor(msg, code, process) { - super(msg, code); - this.process = process; - } + if (_isArray15) { + if (_i15 >= _iterator15.length) break; + _ref35 = _iterator15[_i15++]; + } else { + _i15 = _iterator15.next(); + if (_i15.done) break; + _ref35 = _i15.value; + } -} + const path = _ref35; -exports.ProcessSpawnError = ProcessSpawnError; -class SecurityError extends MessageError {} + try { + const fd = yield open(path, 'r'); + return (_fs || _load_fs()).default.createReadStream(path, { fd }); + } catch (err) { + // Try the next one + } + } + return null; + }); -exports.SecurityError = SecurityError; -class ProcessTermError extends MessageError {} + return function readFirstAvailableStream(_x34) { + return _ref34.apply(this, arguments); + }; +})(); -exports.ProcessTermError = ProcessTermError; -class ResponseError extends Error { - constructor(msg, responseCode) { - super(msg); - this.responseCode = responseCode; - } +let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { + var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { + const result = { + skipped: [], + folder: null + }; -} -exports.ResponseError = ResponseError; + for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { + var _ref37; -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { + if (_isArray16) { + if (_i16 >= _iterator16.length) break; + _ref37 = _iterator16[_i16++]; + } else { + _i16 = _iterator16.next(); + if (_i16.done) break; + _ref37 = _i16.value; + } -"use strict"; + const folder = _ref37; + try { + yield mkdirp(folder); + yield access(folder, mode); -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; + result.folder = folder; -var _asyncToGenerator2; + return result; + } catch (error) { + result.skipped.push({ + error, + folder + }); + } + } + return result; + }); -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); + return function getFirstSuitableFolder(_x35) { + return _ref36.apply(this, arguments); + }; +})(); + +exports.copy = copy; +exports.readFile = readFile; +exports.readFileRaw = readFileRaw; +exports.normalizeOS = normalizeOS; + +var _fs; + +function _load_fs() { + return _fs = _interopRequireDefault(__webpack_require__(3)); } -let buildActionsForCopy = (() => { - var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { +var _glob; - // - let build = (() => { - var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest, - type = data.type; +function _load_glob() { + return _glob = _interopRequireDefault(__webpack_require__(75)); +} - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; +var _os; - // TODO https://github.com/yarnpkg/yarn/issues/3751 - // related to bundled dependencies handling - if (files.has(dest.toLowerCase())) { - reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); - } else { - files.add(dest.toLowerCase()); - } +function _load_os() { + return _os = _interopRequireDefault(__webpack_require__(36)); +} - if (type === 'symlink') { - yield mkdirp((_path || _load_path()).default.dirname(dest)); - onFresh(); - actions.symlink.push({ - dest, - linkname: src - }); - onDone(); - return; - } +var _path; - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } +function _load_path() { + return _path = _interopRequireDefault(__webpack_require__(0)); +} - const srcStat = yield lstat(src); - let srcFiles; +var _blockingQueue; - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } +function _load_blockingQueue() { + return _blockingQueue = _interopRequireDefault(__webpack_require__(84)); +} - let destStat; - try { - // try accessing the destination - destStat = yield lstat(dest); - } catch (e) { - // proceed if destination doesn't exist, otherwise error - if (e.code !== 'ENOENT') { - throw e; - } - } - - // if destination exists - if (destStat) { - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); +var _promise; - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. +function _load_promise() { + return _promise = _interopRequireWildcard(__webpack_require__(40)); +} - /* if (srcStat.mode !== destStat.mode) { - try { - await access(dest, srcStat.mode); - } catch (err) {} - } */ +var _promise2; - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } +function _load_promise2() { + return _promise2 = __webpack_require__(40); +} - if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { - // we can safely assume this is the same file - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); - return; - } +var _map; - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); +} - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); +var _fsNormalized; - for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref6; +function _load_fsNormalized() { + return _fsNormalized = __webpack_require__(164); +} - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref6 = _iterator4[_i4++]; - } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref6 = _i4.value; - } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - const file = _ref6; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); +const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { + R_OK: (_fs || _load_fs()).default.R_OK, + W_OK: (_fs || _load_fs()).default.W_OK, + X_OK: (_fs || _load_fs()).default.X_OK +}; - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref7; +const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref7 = _iterator5[_i5++]; - } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref7 = _i5.value; - } +const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); +const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); +const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); +const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); +const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); +const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); +const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); +const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); +const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); +const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(116)); +const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); +const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); +const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); +const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); +const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); +exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; - const file = _ref7; +// fs.copyFile uses the native file copying instructions on the system, performing much better +// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the +// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } - } - } +const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; - if (destStat && destStat.isSymbolicLink()) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - destStat = null; - } +const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); +const invariant = __webpack_require__(7); +const stripBOM = __webpack_require__(122); - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - if (!destStat) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); - } +const noop = () => {}; - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } +function copy(src, dest, reporter) { + return copyBulk([{ src, dest }], reporter); +} - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref8; +function _readFile(loc, encoding) { + return new Promise((resolve, reject) => { + (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { + if (err) { + reject(err); + } else { + resolve(content); + } + }); + }); +} - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref8 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref8 = _i6.value; - } +function readFile(loc) { + return _readFile(loc, 'utf8').then(normalizeOS); +} - const file = _ref8; +function readFileRaw(loc) { + return _readFile(loc, 'binary'); +} - queue.push({ - dest: (_path || _load_path()).default.join(dest, file), - onFresh, - onDone: function (_onDone) { - function onDone() { - return _onDone.apply(this, arguments); - } +function normalizeOS(body) { + return body.replace(/\r\n/g, '\n'); +} - onDone.toString = function () { - return _onDone.toString(); - }; +const cr = '\r'.charCodeAt(0); +const lf = '\n'.charCodeAt(0); - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }), - src: (_path || _load_path()).default.join(src, file) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.file.push({ - src, - dest, - atime: srcStat.atime, - mtime: srcStat.mtime, - mode: srcStat.mode - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { - return function build(_x5) { - return _ref5.apply(this, arguments); - }; - })(); +"use strict"; - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); - // initialise events - for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref2; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPathKey = getPathKey; +const os = __webpack_require__(36); +const path = __webpack_require__(0); +const userHome = __webpack_require__(45).default; - if (_isArray) { - if (_i >= _iterator.length) break; - _ref2 = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref2 = _i.value; - } +var _require = __webpack_require__(171); - const item = _ref2; +const getCacheDir = _require.getCacheDir, + getConfigDir = _require.getConfigDir, + getDataDir = _require.getDataDir; - const onDone = item.onDone; - item.onDone = function () { - events.onProgress(item.dest); - if (onDone) { - onDone(); - } - }; - } - events.onStart(queue.length); +const isWebpackBundle = __webpack_require__(227); - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; +const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; +const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; +const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); - } +const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref3; +const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref3 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref3 = _i2.value; - } +const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; +const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; +const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; - const file = _ref3; +const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } +// cache version, bump whenever we make backwards incompatible changes +const CACHE_VERSION = exports.CACHE_VERSION = 2; - for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref4; +// lockfile version, bump whenever we make backwards incompatible changes +const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref4 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref4 = _i3.value; - } +// max amount of network requests to perform concurrently +const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; - const loc = _ref4; +// HTTP timeout used when downloading packages +const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } - } +// max amount of child processes to execute concurrently +const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; - return actions; - }); +const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; - return function buildActionsForCopy(_x, _x2, _x3, _x4) { - return _ref.apply(this, arguments); - }; -})(); +function getPreferredCacheDirectories() { + const preferredCacheDirectories = [getCacheDir()]; -let buildActionsForHardlink = (() => { - var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { + if (process.getuid) { + // $FlowFixMe: process.getuid exists, dammit + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); + } - // - let build = (() => { - var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest; + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; - if (files.has(dest.toLowerCase())) { - // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 - // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, - // package-linker passes that modules A1 and B1 need to be hardlinked, - // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case - // an exception. - onDone(); - return; - } - files.add(dest.toLowerCase()); + return preferredCacheDirectories; +} - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } +const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); +const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); +const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); +const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); +const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); - const srcStat = yield lstat(src); - let srcFiles; +const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; +const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } +// Webpack needs to be configured with node.__dirname/__filename = false +function getYarnBinPath() { + if (isWebpackBundle) { + return __filename; + } else { + return path.join(__dirname, '..', 'bin', 'yarn.js'); + } +} - const destExists = yield exists(dest); - if (destExists) { - const destStat = yield lstat(dest); +const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; +const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); +const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; +const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); - if (srcStat.mode !== destStat.mode) { - try { - yield access(dest, srcStat.mode); - } catch (err) { - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. - reporter.verbose(err); - } - } +const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; +const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; +const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; +const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; +const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; +const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } +const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; +const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; - // correct hardlink - if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); - return; - } +const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; +const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; +const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } +const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); +function getPathKey(platform, env) { + let pathKey = 'PATH'; - for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { - var _ref14; + // windows calls its path "Path" usually, but this is not guaranteed. + if (platform === 'win32') { + pathKey = 'Path'; - if (_isArray10) { - if (_i10 >= _iterator10.length) break; - _ref14 = _iterator10[_i10++]; - } else { - _i10 = _iterator10.next(); - if (_i10.done) break; - _ref14 = _i10.value; - } + for (const key in env) { + if (key.toLowerCase() === 'path') { + pathKey = key; + } + } + } - const file = _ref14; + return pathKey; +} - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); +const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { + major: 'red', + premajor: 'red', + minor: 'yellow', + preminor: 'yellow', + patch: 'green', + prepatch: 'green', + prerelease: 'red', + unchanged: 'white', + unknown: 'red' +}; - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { - var _ref15; +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { - if (_isArray11) { - if (_i11 >= _iterator11.length) break; - _ref15 = _iterator11[_i11++]; - } else { - _i11 = _iterator11.next(); - if (_i11.done) break; - _ref15 = _i11.value; - } +"use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ - const file = _ref15; - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } - } - } - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); +/** + * Use invariant() to assert state which your program assumes to be true. + * + * Provide sprintf-style format (only %s is supported) and arguments + * to provide information about what broke and what you were + * expecting. + * + * The invariant message will be stripped in production, but the invariant + * will remain to ensure logic does not differ in production. + */ - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } +var NODE_ENV = "none"; - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { - var _ref16; +var invariant = function(condition, format, a, b, c, d, e, f) { + if (NODE_ENV !== 'production') { + if (format === undefined) { + throw new Error('invariant requires an error message argument'); + } + } - if (_isArray12) { - if (_i12 >= _iterator12.length) break; - _ref16 = _iterator12[_i12++]; - } else { - _i12 = _iterator12.next(); - if (_i12.done) break; - _ref16 = _i12.value; - } + if (!condition) { + var error; + if (format === undefined) { + error = new Error( + 'Minified exception occurred; use the non-minified dev environment ' + + 'for the full error message and additional helpful warnings.' + ); + } else { + var args = [a, b, c, d, e, f]; + var argIndex = 0; + error = new Error( + format.replace(/%s/g, function() { return args[argIndex++]; }) + ); + error.name = 'Invariant Violation'; + } - const file = _ref16; + error.framesToPop = 1; // we don't care about invariant's own frame + throw error; + } +}; - queue.push({ - onFresh, - src: (_path || _load_path()).default.join(src, file), - dest: (_path || _load_path()).default.join(dest, file), - onDone: function (_onDone2) { - function onDone() { - return _onDone2.apply(this, arguments); - } +module.exports = invariant; - onDone.toString = function () { - return _onDone2.toString(); - }; - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.link.push({ - src, - dest, - removeDest: destExists - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); +/***/ }), +/* 8 */, +/* 9 */ +/***/ (function(module, exports) { - return function build(_x10) { - return _ref13.apply(this, arguments); - }; - })(); +module.exports = __webpack_require__(278); - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); +/***/ }), +/* 10 */, +/* 11 */ +/***/ (function(module, exports) { - // initialise events - for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { - var _ref10; +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); +if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef - if (_isArray7) { - if (_i7 >= _iterator7.length) break; - _ref10 = _iterator7[_i7++]; - } else { - _i7 = _iterator7.next(); - if (_i7.done) break; - _ref10 = _i7.value; - } - const item = _ref10; +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { - const onDone = item.onDone || noop; - item.onDone = function () { - events.onProgress(item.dest); - onDone(); - }; - } - events.onStart(queue.length); +"use strict"; - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sortAlpha = sortAlpha; +exports.entries = entries; +exports.removePrefix = removePrefix; +exports.removeSuffix = removeSuffix; +exports.addSuffix = addSuffix; +exports.hyphenate = hyphenate; +exports.camelCase = camelCase; +exports.compareSortedArrays = compareSortedArrays; +exports.sleep = sleep; +const _camelCase = __webpack_require__(176); + +function sortAlpha(a, b) { + // sort alphabetically in a deterministic way + const shortLen = Math.min(a.length, b.length); + for (let i = 0; i < shortLen; i++) { + const aChar = a.charCodeAt(i); + const bChar = b.charCodeAt(i); + if (aChar !== bChar) { + return aChar - bChar; } + } + return a.length - b.length; +} - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { - var _ref11; +function entries(obj) { + const entries = []; + if (obj) { + for (const key in obj) { + entries.push([key, obj[key]]); + } + } + return entries; +} - if (_isArray8) { - if (_i8 >= _iterator8.length) break; - _ref11 = _iterator8[_i8++]; - } else { - _i8 = _iterator8.next(); - if (_i8.done) break; - _ref11 = _i8.value; - } +function removePrefix(pattern, prefix) { + if (pattern.startsWith(prefix)) { + pattern = pattern.slice(prefix.length); + } - const file = _ref11; + return pattern; +} - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } +function removeSuffix(pattern, suffix) { + if (pattern.endsWith(suffix)) { + return pattern.slice(0, -suffix.length); + } - for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref12; + return pattern; +} - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref12 = _iterator9[_i9++]; - } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref12 = _i9.value; - } +function addSuffix(pattern, suffix) { + if (!pattern.endsWith(suffix)) { + return pattern + suffix; + } - const loc = _ref12; + return pattern; +} - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } +function hyphenate(str) { + return str.replace(/[A-Z]/g, match => { + return '-' + match.charAt(0).toLowerCase(); + }); +} + +function camelCase(str) { + if (/[A-Z]/.test(str)) { + return null; + } else { + return _camelCase(str); + } +} + +function compareSortedArrays(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + for (let i = 0, len = array1.length; i < len; i++) { + if (array1[i] !== array2[i]) { + return false; } + } + return true; +} - return actions; +function sleep(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); }); +} - return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { - return _ref9.apply(this, arguments); - }; -})(); +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { -let copyBulk = exports.copyBulk = (() => { - var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - ignoreBasenames: _events && _events.ignoreBasenames || [], - artifactFiles: _events && _events.artifactFiles || [] - }; +var store = __webpack_require__(107)('wks'); +var uid = __webpack_require__(111); +var Symbol = __webpack_require__(11).Symbol; +var USE_SYMBOL = typeof Symbol == 'function'; - const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); +var $exports = module.exports = function (name) { + return store[name] || (store[name] = + USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); +}; - const fileActions = actions.file; +$exports.store = store; - const currentlyWriting = new Map(); - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - let writePromise; - while (writePromise = currentlyWriting.get(data.dest)) { - yield writePromise; - } +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { - reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); - const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { - return currentlyWriting.delete(data.dest); - }); - currentlyWriting.set(data.dest, copier); - events.onProgress(data.dest); - return copier; - }); +"use strict"; - return function (_x14) { - return _ref18.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.stringify = exports.parse = undefined; - return function copyBulk(_x11, _x12, _x13) { - return _ref17.apply(this, arguments); - }; -})(); +var _asyncToGenerator2; -let hardlinkBulk = exports.hardlinkBulk = (() => { - var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - artifactFiles: _events && _events.artifactFiles || [], - ignoreBasenames: [] - }; +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +} - const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); +var _parse; - const fileActions = actions.link; +function _load_parse() { + return _parse = __webpack_require__(81); +} - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); - if (data.removeDest) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); - } - yield link(data.src, data.dest); - }); +Object.defineProperty(exports, 'parse', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_parse || _load_parse()).default; + } +}); - return function (_x18) { - return _ref20.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); +var _stringify; - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); +function _load_stringify() { + return _stringify = __webpack_require__(150); +} - return function hardlinkBulk(_x15, _x16, _x17) { - return _ref19.apply(this, arguments); - }; -})(); +Object.defineProperty(exports, 'stringify', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_stringify || _load_stringify()).default; + } +}); +exports.implodeEntry = implodeEntry; +exports.explodeEntry = explodeEntry; -let readFileAny = exports.readFileAny = (() => { - var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { - for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { - var _ref22; +var _misc; - if (_isArray13) { - if (_i13 >= _iterator13.length) break; - _ref22 = _iterator13[_i13++]; - } else { - _i13 = _iterator13.next(); - if (_i13.done) break; - _ref22 = _i13.value; - } +function _load_misc() { + return _misc = __webpack_require__(12); +} - const file = _ref22; +var _normalizePattern; - if (yield exists(file)) { - return readFile(file); - } - } - return null; - }); +function _load_normalizePattern() { + return _normalizePattern = __webpack_require__(29); +} - return function readFileAny(_x19) { - return _ref21.apply(this, arguments); - }; -})(); +var _parse2; -let readJson = exports.readJson = (() => { - var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - return (yield readJsonAndFile(loc)).object; - }); +function _load_parse2() { + return _parse2 = _interopRequireDefault(__webpack_require__(81)); +} - return function readJson(_x20) { - return _ref23.apply(this, arguments); - }; -})(); +var _constants; -let readJsonAndFile = exports.readJsonAndFile = (() => { - var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const file = yield readFile(loc); - try { - return { - object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), - content: file - }; - } catch (err) { - err.message = `${loc}: ${err.message}`; - throw err; - } - }); +function _load_constants() { + return _constants = __webpack_require__(6); +} - return function readJsonAndFile(_x21) { - return _ref24.apply(this, arguments); - }; -})(); +var _fs; -let find = exports.find = (() => { - var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { - const parts = dir.split((_path || _load_path()).default.sep); +function _load_fs() { + return _fs = _interopRequireWildcard(__webpack_require__(5)); +} - while (parts.length) { - const loc = parts.concat(filename).join((_path || _load_path()).default.sep); +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - if (yield exists(loc)) { - return loc; - } else { - parts.pop(); - } - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - return false; - }); +const invariant = __webpack_require__(7); - return function find(_x22, _x23) { - return _ref25.apply(this, arguments); - }; -})(); +const path = __webpack_require__(0); +const ssri = __webpack_require__(55); -let symlink = exports.symlink = (() => { - var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { - try { - const stats = yield lstat(dest); - if (stats.isSymbolicLink()) { - const resolved = yield realpath(dest); - if (resolved === src) { - return; - } - } - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - } - // We use rimraf for unlink which never throws an ENOENT on missing target - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); +function getName(pattern) { + return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; +} - if (process.platform === 'win32') { - // use directory junctions if possible on win32, this requires absolute paths - yield fsSymlink(src, dest, 'junction'); - } else { - // use relative paths otherwise which will be retained if the directory is moved - let relative; - try { - relative = (_path || _load_path()).default.relative((_fs || _load_fs()).default.realpathSync((_path || _load_path()).default.dirname(dest)), (_fs || _load_fs()).default.realpathSync(src)); - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - relative = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); - } - // When path.relative returns an empty string for the current directory, we should instead use - // '.', which is a valid fs.symlink target. - yield fsSymlink(relative || '.', dest); - } - }); +function blankObjectUndefined(obj) { + return obj && Object.keys(obj).length ? obj : undefined; +} - return function symlink(_x24, _x25) { - return _ref26.apply(this, arguments); - }; -})(); +function keyForRemote(remote) { + return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); +} -let walk = exports.walk = (() => { - var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { - let files = []; +function serializeIntegrity(integrity) { + // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output + // See https://git.io/vx2Hy + return integrity.toString().split(' ').sort().join(' '); +} - let filenames = yield readdir(dir); - if (ignoreBasenames.size) { - filenames = filenames.filter(function (name) { - return !ignoreBasenames.has(name); - }); - } +function implodeEntry(pattern, obj) { + const inferredName = getName(pattern); + const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; + const imploded = { + name: inferredName === obj.name ? undefined : obj.name, + version: obj.version, + uid: obj.uid === obj.version ? undefined : obj.uid, + resolved: obj.resolved, + registry: obj.registry === 'npm' ? undefined : obj.registry, + dependencies: blankObjectUndefined(obj.dependencies), + optionalDependencies: blankObjectUndefined(obj.optionalDependencies), + permissions: blankObjectUndefined(obj.permissions), + prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) + }; + if (integrity) { + imploded.integrity = integrity; + } + return imploded; +} - for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { - var _ref28; +function explodeEntry(pattern, obj) { + obj.optionalDependencies = obj.optionalDependencies || {}; + obj.dependencies = obj.dependencies || {}; + obj.uid = obj.uid || obj.version; + obj.permissions = obj.permissions || {}; + obj.registry = obj.registry || 'npm'; + obj.name = obj.name || getName(pattern); + const integrity = obj.integrity; + if (integrity && integrity.isIntegrity) { + obj.integrity = ssri.parse(integrity); + } + return obj; +} - if (_isArray14) { - if (_i14 >= _iterator14.length) break; - _ref28 = _iterator14[_i14++]; - } else { - _i14 = _iterator14.next(); - if (_i14.done) break; - _ref28 = _i14.value; - } +class Lockfile { + constructor({ cache, source, parseResultType } = {}) { + this.source = source || ''; + this.cache = cache; + this.parseResultType = parseResultType; + } - const name = _ref28; + // source string if the `cache` was parsed - const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; - const loc = (_path || _load_path()).default.join(dir, name); - const stat = yield lstat(loc); - files.push({ - relative, - basename: name, - absolute: loc, - mtime: +stat.mtime - }); + // if true, we're parsing an old yarn file and need to update integrity fields + hasEntriesExistWithoutIntegrity() { + if (!this.cache) { + return false; + } - if (stat.isDirectory()) { - files = files.concat((yield walk(loc, relative, ignoreBasenames))); + for (const key in this.cache) { + // $FlowFixMe - `this.cache` is clearly defined at this point + if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { + return true; } } - return files; - }); + return false; + } - return function walk(_x26, _x27) { - return _ref27.apply(this, arguments); - }; -})(); + static fromDirectory(dir, reporter) { + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // read the manifest in this directory + const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); -let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { - var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const stat = yield lstat(loc); - const size = stat.size, - blockSize = stat.blksize; + let lockfile; + let rawLockfile = ''; + let parseResult; + + if (yield (_fs || _load_fs()).exists(lockfileLoc)) { + rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); + parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); + if (reporter) { + if (parseResult.type === 'merge') { + reporter.info(reporter.lang('lockfileMerged')); + } else if (parseResult.type === 'conflict') { + reporter.warn(reporter.lang('lockfileConflict')); + } + } - return Math.ceil(size / blockSize) * blockSize; - }); + lockfile = parseResult.object; + } else if (reporter) { + reporter.info(reporter.lang('noLockfileFound')); + } - return function getFileSizeOnDisk(_x28) { - return _ref29.apply(this, arguments); - }; -})(); + return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); + })(); + } -let getEolFromFile = (() => { - var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { - if (!(yield exists(path))) { + getLocked(pattern) { + const cache = this.cache; + if (!cache) { return undefined; } - const buffer = yield readFileBuffer(path); + const shrunk = pattern in cache && cache[pattern]; - for (let i = 0; i < buffer.length; ++i) { - if (buffer[i] === cr) { - return '\r\n'; - } - if (buffer[i] === lf) { - return '\n'; - } + if (typeof shrunk === 'string') { + return this.getLocked(shrunk); + } else if (shrunk) { + explodeEntry(pattern, shrunk); + return shrunk; } - return undefined; - }); - return function getEolFromFile(_x29) { - return _ref30.apply(this, arguments); - }; -})(); + return undefined; + } -let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { - var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { - const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; - if (eol !== '\n') { - data = data.replace(/\n/g, eol); + removePattern(pattern) { + const cache = this.cache; + if (!cache) { + return; } - yield writeFile(path, data); - }); + delete cache[pattern]; + } - return function writeFilePreservingEol(_x30, _x31) { - return _ref31.apply(this, arguments); - }; -})(); + getLockfile(patterns) { + const lockfile = {}; + const seen = new Map(); -let hardlinksWork = exports.hardlinksWork = (() => { - var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { - const filename = 'test-file' + Math.random(); - const file = (_path || _load_path()).default.join(dir, filename); - const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); - try { - yield writeFile(file, 'test'); - yield link(file, fileLink); - } catch (err) { - return false; - } finally { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); - } - return true; - }); + // order by name so that lockfile manifest is assigned to the first dependency with this manifest + // the others that have the same remoteKey will just refer to the first + // ordering allows for consistency in lockfile when it is serialized + const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); - return function hardlinksWork(_x32) { - return _ref32.apply(this, arguments); - }; -})(); + for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; -// not a strict polyfill for Node's fs.mkdtemp + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + const pattern = _ref; -let makeTempDir = exports.makeTempDir = (() => { - var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { - const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); - yield mkdirp(dir); - return dir; - }); + const pkg = patterns[pattern]; + const remote = pkg._remote, + ref = pkg._reference; - return function makeTempDir(_x33) { - return _ref33.apply(this, arguments); - }; -})(); + invariant(ref, 'Package is missing a reference'); + invariant(remote, 'Package is missing a remote'); -let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { - var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { - for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { - var _ref35; + const remoteKey = keyForRemote(remote); + const seenPattern = remoteKey && seen.get(remoteKey); + if (seenPattern) { + // no point in duplicating it + lockfile[pattern] = seenPattern; - if (_isArray15) { - if (_i15 >= _iterator15.length) break; - _ref35 = _iterator15[_i15++]; - } else { - _i15 = _iterator15.next(); - if (_i15.done) break; - _ref35 = _i15.value; + // if we're relying on our name being inferred and two of the patterns have + // different inferred names then we need to set it + if (!seenPattern.name && getName(pattern) !== pkg.name) { + seenPattern.name = pkg.name; + } + continue; } + const obj = implodeEntry(pattern, { + name: pkg.name, + version: pkg.version, + uid: pkg._uid, + resolved: remote.resolved, + integrity: remote.integrity, + registry: remote.registry, + dependencies: pkg.dependencies, + peerDependencies: pkg.peerDependencies, + optionalDependencies: pkg.optionalDependencies, + permissions: ref.permissions, + prebuiltVariants: pkg.prebuiltVariants + }); - const path = _ref35; + lockfile[pattern] = obj; - try { - const fd = yield open(path, 'r'); - return (_fs || _load_fs()).default.createReadStream(path, { fd }); - } catch (err) { - // Try the next one + if (remoteKey) { + seen.set(remoteKey, obj); } } - return null; - }); - return function readFirstAvailableStream(_x34) { - return _ref34.apply(this, arguments); - }; -})(); + return lockfile; + } +} +exports.default = Lockfile; -let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { - var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { - const result = { - skipped: [], - folder: null - }; +/***/ }), +/* 15 */, +/* 16 */, +/* 17 */ +/***/ (function(module, exports) { - for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { - var _ref37; +module.exports = __webpack_require__(137); - if (_isArray16) { - if (_i16 >= _iterator16.length) break; - _ref37 = _iterator16[_i16++]; +/***/ }), +/* 18 */, +/* 19 */, +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = nullify; +function nullify(obj = {}) { + if (Array.isArray(obj)) { + for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; } else { - _i16 = _iterator16.next(); - if (_i16.done) break; - _ref37 = _i16.value; + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; } - const folder = _ref37; - - try { - yield mkdirp(folder); - yield access(folder, mode); + const item = _ref; - result.folder = folder; + nullify(item); + } + } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { + Object.setPrototypeOf(obj, null); - return result; - } catch (error) { - result.skipped.push({ - error, - folder - }); + // for..in can only be applied to 'object', not 'function' + if (typeof obj === 'object') { + for (const key in obj) { + nullify(obj[key]); } } - return result; - }); + } - return function getFirstSuitableFolder(_x35) { - return _ref36.apply(this, arguments); - }; -})(); + return obj; +} -exports.copy = copy; -exports.readFile = readFile; -exports.readFileRaw = readFileRaw; -exports.normalizeOS = normalizeOS; +/***/ }), +/* 21 */, +/* 22 */ +/***/ (function(module, exports) { -var _fs; +module.exports = __webpack_require__(139); -function _load_fs() { - return _fs = _interopRequireDefault(__webpack_require__(3)); -} +/***/ }), +/* 23 */ +/***/ (function(module, exports) { -var _glob; +var core = module.exports = { version: '2.5.7' }; +if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef -function _load_glob() { - return _glob = _interopRequireDefault(__webpack_require__(75)); -} -var _os; +/***/ }), +/* 24 */, +/* 25 */, +/* 26 */, +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { -function _load_os() { - return _os = _interopRequireDefault(__webpack_require__(36)); -} +var isObject = __webpack_require__(34); +module.exports = function (it) { + if (!isObject(it)) throw TypeError(it + ' is not an object!'); + return it; +}; -var _path; -function _load_path() { - return _path = _interopRequireDefault(__webpack_require__(0)); -} +/***/ }), +/* 28 */, +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { -var _blockingQueue; +"use strict"; -function _load_blockingQueue() { - return _blockingQueue = _interopRequireDefault(__webpack_require__(84)); -} -var _promise; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.normalizePattern = normalizePattern; -function _load_promise() { - return _promise = _interopRequireWildcard(__webpack_require__(40)); -} +/** + * Explode and normalize a pattern into its name and range. + */ -var _promise2; +function normalizePattern(pattern) { + let hasVersion = false; + let range = 'latest'; + let name = pattern; -function _load_promise2() { - return _promise2 = __webpack_require__(40); -} + // if we're a scope then remove the @ and add it back later + let isScoped = false; + if (name[0] === '@') { + isScoped = true; + name = name.slice(1); + } -var _map; + // take first part as the name + const parts = name.split('@'); + if (parts.length > 1) { + name = parts.shift(); + range = parts.join('@'); -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); -} + if (range) { + hasVersion = true; + } else { + range = '*'; + } + } -var _fsNormalized; + // add back @ scope suffix + if (isScoped) { + name = `@${name}`; + } -function _load_fsNormalized() { - return _fsNormalized = __webpack_require__(164); + return { name, range, hasVersion }; } -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/***/ }), +/* 30 */, +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { -const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { - R_OK: (_fs || _load_fs()).default.R_OK, - W_OK: (_fs || _load_fs()).default.W_OK, - X_OK: (_fs || _load_fs()).default.X_OK +var dP = __webpack_require__(50); +var createDesc = __webpack_require__(106); +module.exports = __webpack_require__(33) ? function (object, key, value) { + return dP.f(object, key, createDesc(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; }; -const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); - -const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); -const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); -const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); -const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); -const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); -const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); -const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); -const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); -const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); -const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(116)); -const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); -const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); -const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); -const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); -const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); -exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; -// fs.copyFile uses the native file copying instructions on the system, performing much better -// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the -// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. - -const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { -const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); -const invariant = __webpack_require__(7); -const stripBOM = __webpack_require__(122); +/* eslint-disable node/no-deprecated-api */ +var buffer = __webpack_require__(63) +var Buffer = buffer.Buffer -const noop = () => {}; +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} -function copy(src, dest, reporter) { - return copyBulk([{ src, dest }], reporter); +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) } -function _readFile(loc, encoding) { - return new Promise((resolve, reject) => { - (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { - if (err) { - reject(err); - } else { - resolve(content); - } - }); - }); +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) + +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) } -function readFile(loc) { - return _readFile(loc, 'utf8').then(normalizeOS); +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) + } + } else { + buf.fill(0) + } + return buf } -function readFileRaw(loc) { - return _readFile(loc, 'binary'); +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) } -function normalizeOS(body) { - return body.replace(/\r\n/g, '\n'); +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) } -const cr = '\r'.charCodeAt(0); -const lf = '\n'.charCodeAt(0); /***/ }), -/* 6 */ +/* 33 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true +// Thank's IE8 for his funny defineProperty +module.exports = !__webpack_require__(85)(function () { + return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; }); -exports.getPathKey = getPathKey; -const os = __webpack_require__(36); -const path = __webpack_require__(0); -const userHome = __webpack_require__(45).default; -var _require = __webpack_require__(171); -const getCacheDir = _require.getCacheDir, - getConfigDir = _require.getConfigDir, - getDataDir = _require.getDataDir; +/***/ }), +/* 34 */ +/***/ (function(module, exports) { -const isWebpackBundle = __webpack_require__(227); +module.exports = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; -const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; -const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; -const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; -const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; +/***/ }), +/* 35 */ +/***/ (function(module, exports) { -const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; +module.exports = {}; -const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; -const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; -const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; -const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; +/***/ }), +/* 36 */ +/***/ (function(module, exports) { -// cache version, bump whenever we make backwards incompatible changes -const CACHE_VERSION = exports.CACHE_VERSION = 2; +module.exports = __webpack_require__(120); -// lockfile version, bump whenever we make backwards incompatible changes -const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; +/***/ }), +/* 37 */, +/* 38 */, +/* 39 */, +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { -// max amount of network requests to perform concurrently -const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; +"use strict"; -// HTTP timeout used when downloading packages -const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds -// max amount of child processes to execute concurrently -const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.wait = wait; +exports.promisify = promisify; +exports.queue = queue; +function wait(delay) { + return new Promise(resolve => { + setTimeout(resolve, delay); + }); +} -const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; +function promisify(fn, firstData) { + return function (...args) { + return new Promise(function (resolve, reject) { + args.push(function (err, ...result) { + let res = result; -function getPreferredCacheDirectories() { - const preferredCacheDirectories = [getCacheDir()]; + if (result.length <= 1) { + res = result[0]; + } - if (process.getuid) { - // $FlowFixMe: process.getuid exists, dammit - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); - } + if (firstData) { + res = err; + err = null; + } - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); + if (err) { + reject(err); + } else { + resolve(res); + } + }); - return preferredCacheDirectories; + fn.apply(null, args); + }); + }; } -const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); -const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); -const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); -const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); -const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); +function queue(arr, promiseProducer, concurrency = Infinity) { + concurrency = Math.min(concurrency, arr.length); -const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; -const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); + // clone + arr = arr.slice(); -// Webpack needs to be configured with node.__dirname/__filename = false -function getYarnBinPath() { - if (isWebpackBundle) { - return __filename; - } else { - return path.join(__dirname, '..', 'bin', 'yarn.js'); + const results = []; + let total = arr.length; + if (!total) { + return Promise.resolve(results); } -} - -const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; -const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; -const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; -const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); - -const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; -const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; -const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; -const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; -const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; -const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; + return new Promise((resolve, reject) => { + for (let i = 0; i < concurrency; i++) { + next(); + } -const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; -const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; + function next() { + const item = arr.shift(); + const promise = promiseProducer(item); -const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; -const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; -const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; + promise.then(function (result) { + results.push(result); -const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); + total--; + if (total === 0) { + resolve(results); + } else { + if (arr.length) { + next(); + } + } + }, reject); + } + }); +} -function getPathKey(platform, env) { - let pathKey = 'PATH'; +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { - // windows calls its path "Path" usually, but this is not guaranteed. - if (platform === 'win32') { - pathKey = 'Path'; +var global = __webpack_require__(11); +var core = __webpack_require__(23); +var ctx = __webpack_require__(48); +var hide = __webpack_require__(31); +var has = __webpack_require__(49); +var PROTOTYPE = 'prototype'; - for (const key in env) { - if (key.toLowerCase() === 'path') { - pathKey = key; - } +var $export = function (type, name, source) { + var IS_FORCED = type & $export.F; + var IS_GLOBAL = type & $export.G; + var IS_STATIC = type & $export.S; + var IS_PROTO = type & $export.P; + var IS_BIND = type & $export.B; + var IS_WRAP = type & $export.W; + var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); + var expProto = exports[PROTOTYPE]; + var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; + var key, own, out; + if (IS_GLOBAL) source = name; + for (key in source) { + // contains in native + own = !IS_FORCED && target && target[key] !== undefined; + if (own && has(exports, key)) continue; + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function (C) { + var F = function (a, b, c) { + if (this instanceof C) { + switch (arguments.length) { + case 0: return new C(); + case 1: return new C(a); + case 2: return new C(a, b); + } return new C(a, b, c); + } return C.apply(this, arguments); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% + if (IS_PROTO) { + (exports.virtual || (exports.virtual = {}))[key] = out; + // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% + if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); } } +}; +// type bitmap +$export.F = 1; // forced +$export.G = 2; // global +$export.S = 4; // static +$export.P = 8; // proto +$export.B = 16; // bind +$export.W = 32; // wrap +$export.U = 64; // safe +$export.R = 128; // real proto method for `library` +module.exports = $export; - return pathKey; + +/***/ }), +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { + +try { + var util = __webpack_require__(2); + if (typeof util.inherits !== 'function') throw ''; + module.exports = util.inherits; +} catch (e) { + module.exports = __webpack_require__(224); } -const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { - major: 'red', - premajor: 'red', - minor: 'yellow', - preminor: 'yellow', - patch: 'green', - prepatch: 'green', - prerelease: 'red', - unchanged: 'white', - unknown: 'red' -}; /***/ }), -/* 7 */ +/* 43 */, +/* 44 */, +/* 45 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.home = undefined; -/** - * Use invariant() to assert state which your program assumes to be true. - * - * Provide sprintf-style format (only %s is supported) and arguments - * to provide information about what broke and what you were - * expecting. - * - * The invariant message will be stripped in production, but the invariant - * will remain to ensure logic does not differ in production. - */ +var _rootUser; -var NODE_ENV = "none"; +function _load_rootUser() { + return _rootUser = _interopRequireDefault(__webpack_require__(169)); +} -var invariant = function(condition, format, a, b, c, d, e, f) { - if (NODE_ENV !== 'production') { - if (format === undefined) { - throw new Error('invariant requires an error message argument'); - } - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - if (!condition) { - var error; - if (format === undefined) { - error = new Error( - 'Minified exception occurred; use the non-minified dev environment ' + - 'for the full error message and additional helpful warnings.' - ); - } else { - var args = [a, b, c, d, e, f]; - var argIndex = 0; - error = new Error( - format.replace(/%s/g, function() { return args[argIndex++]; }) - ); - error.name = 'Invariant Violation'; - } +const path = __webpack_require__(0); - error.framesToPop = 1; // we don't care about invariant's own frame - throw error; - } -}; +const home = exports.home = __webpack_require__(36).homedir(); -module.exports = invariant; +const userHomeDir = (_rootUser || _load_rootUser()).default ? path.resolve('/usr/local/share') : home; +exports.default = userHomeDir; /***/ }), -/* 8 */, -/* 9 */ +/* 46 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(282); +module.exports = function (it) { + if (typeof it != 'function') throw TypeError(it + ' is not a function!'); + return it; +}; + /***/ }), -/* 10 */, -/* 11 */ +/* 47 */ /***/ (function(module, exports) { -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self - // eslint-disable-next-line no-new-func - : Function('return this')(); -if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef +var toString = {}.toString; + +module.exports = function (it) { + return toString.call(it).slice(8, -1); +}; /***/ }), -/* 12 */ +/* 48 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.sortAlpha = sortAlpha; -exports.entries = entries; -exports.removePrefix = removePrefix; -exports.removeSuffix = removeSuffix; -exports.addSuffix = addSuffix; -exports.hyphenate = hyphenate; -exports.camelCase = camelCase; -exports.compareSortedArrays = compareSortedArrays; -exports.sleep = sleep; -const _camelCase = __webpack_require__(176); - -function sortAlpha(a, b) { - // sort alphabetically in a deterministic way - const shortLen = Math.min(a.length, b.length); - for (let i = 0; i < shortLen; i++) { - const aChar = a.charCodeAt(i); - const bChar = b.charCodeAt(i); - if (aChar !== bChar) { - return aChar - bChar; - } - } - return a.length - b.length; -} - -function entries(obj) { - const entries = []; - if (obj) { - for (const key in obj) { - entries.push([key, obj[key]]); - } +// optional / simple context binding +var aFunction = __webpack_require__(46); +module.exports = function (fn, that, length) { + aFunction(fn); + if (that === undefined) return fn; + switch (length) { + case 1: return function (a) { + return fn.call(that, a); + }; + case 2: return function (a, b) { + return fn.call(that, a, b); + }; + case 3: return function (a, b, c) { + return fn.call(that, a, b, c); + }; } - return entries; -} + return function (/* ...args */) { + return fn.apply(that, arguments); + }; +}; -function removePrefix(pattern, prefix) { - if (pattern.startsWith(prefix)) { - pattern = pattern.slice(prefix.length); - } - return pattern; -} +/***/ }), +/* 49 */ +/***/ (function(module, exports) { -function removeSuffix(pattern, suffix) { - if (pattern.endsWith(suffix)) { - return pattern.slice(0, -suffix.length); - } +var hasOwnProperty = {}.hasOwnProperty; +module.exports = function (it, key) { + return hasOwnProperty.call(it, key); +}; - return pattern; -} -function addSuffix(pattern, suffix) { - if (!pattern.endsWith(suffix)) { - return pattern + suffix; - } +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { - return pattern; -} +var anObject = __webpack_require__(27); +var IE8_DOM_DEFINE = __webpack_require__(184); +var toPrimitive = __webpack_require__(201); +var dP = Object.defineProperty; -function hyphenate(str) { - return str.replace(/[A-Z]/g, match => { - return '-' + match.charAt(0).toLowerCase(); - }); -} +exports.f = __webpack_require__(33) ? Object.defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPrimitive(P, true); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return dP(O, P, Attributes); + } catch (e) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; +}; -function camelCase(str) { - if (/[A-Z]/.test(str)) { - return null; - } else { - return _camelCase(str); - } -} -function compareSortedArrays(array1, array2) { - if (array1.length !== array2.length) { - return false; - } - for (let i = 0, len = array1.length; i < len; i++) { - if (array1[i] !== array2[i]) { - return false; - } - } - return true; -} +/***/ }), +/* 51 */, +/* 52 */, +/* 53 */, +/* 54 */ +/***/ (function(module, exports) { -function sleep(ms) { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -} +module.exports = __webpack_require__(155); /***/ }), -/* 13 */ +/* 55 */ /***/ (function(module, exports, __webpack_require__) { -var store = __webpack_require__(107)('wks'); -var uid = __webpack_require__(111); -var Symbol = __webpack_require__(11).Symbol; -var USE_SYMBOL = typeof Symbol == 'function'; - -var $exports = module.exports = function (name) { - return store[name] || (store[name] = - USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); -}; +"use strict"; -$exports.store = store; +const Buffer = __webpack_require__(32).Buffer -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { +const crypto = __webpack_require__(9) +const Transform = __webpack_require__(17).Transform -"use strict"; +const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] +const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i +const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ +const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ +const VCHAR_REGEX = /^[\x21-\x7E]+$/ -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.stringify = exports.parse = undefined; +class Hash { + get isHash () { return true } + constructor (hash, opts) { + const strict = !!(opts && opts.strict) + this.source = hash.trim() + // 3.1. Integrity metadata (called "Hash" by ssri) + // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description + const match = this.source.match( + strict + ? STRICT_SRI_REGEX + : SRI_REGEX + ) + if (!match) { return } + if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return } + this.algorithm = match[1] + this.digest = match[2] -var _asyncToGenerator2; + const rawOpts = match[3] + this.options = rawOpts ? rawOpts.slice(1).split('?') : [] + } + hexDigest () { + return this.digest && Buffer.from(this.digest, 'base64').toString('hex') + } + toJSON () { + return this.toString() + } + toString (opts) { + if (opts && opts.strict) { + // Strict mode enforces the standard as close to the foot of the + // letter as it can. + if (!( + // The spec has very restricted productions for algorithms. + // https://www.w3.org/TR/CSP2/#source-list-syntax + SPEC_ALGORITHMS.some(x => x === this.algorithm) && + // Usually, if someone insists on using a "different" base64, we + // leave it as-is, since there's multiple standards, and the + // specified is not a URL-safe variant. + // https://www.w3.org/TR/CSP2/#base64_value + this.digest.match(BASE64_REGEX) && + // Option syntax is strictly visual chars. + // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression + // https://tools.ietf.org/html/rfc5234#appendix-B.1 + (this.options || []).every(opt => opt.match(VCHAR_REGEX)) + )) { + return '' + } + } + const options = this.options && this.options.length + ? `?${this.options.join('?')}` + : '' + return `${this.algorithm}-${this.digest}${options}` + } +} -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +class Integrity { + get isIntegrity () { return true } + toJSON () { + return this.toString() + } + toString (opts) { + opts = opts || {} + let sep = opts.sep || ' ' + if (opts.strict) { + // Entries must be separated by whitespace, according to spec. + sep = sep.replace(/\S+/g, ' ') + } + return Object.keys(this).map(k => { + return this[k].map(hash => { + return Hash.prototype.toString.call(hash, opts) + }).filter(x => x.length).join(sep) + }).filter(x => x.length).join(sep) + } + concat (integrity, opts) { + const other = typeof integrity === 'string' + ? integrity + : stringify(integrity, opts) + return parse(`${this.toString(opts)} ${other}`, opts) + } + hexDigest () { + return parse(this, {single: true}).hexDigest() + } + match (integrity, opts) { + const other = parse(integrity, opts) + const algo = other.pickAlgorithm(opts) + return ( + this[algo] && + other[algo] && + this[algo].find(hash => + other[algo].find(otherhash => + hash.digest === otherhash.digest + ) + ) + ) || false + } + pickAlgorithm (opts) { + const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash + const keys = Object.keys(this) + if (!keys.length) { + throw new Error(`No algorithms available for ${ + JSON.stringify(this.toString()) + }`) + } + return keys.reduce((acc, algo) => { + return pickAlgorithm(acc, algo) || acc + }) + } } -var _parse; +module.exports.parse = parse +function parse (sri, opts) { + opts = opts || {} + if (typeof sri === 'string') { + return _parse(sri, opts) + } else if (sri.algorithm && sri.digest) { + const fullSri = new Integrity() + fullSri[sri.algorithm] = [sri] + return _parse(stringify(fullSri, opts), opts) + } else { + return _parse(stringify(sri, opts), opts) + } +} -function _load_parse() { - return _parse = __webpack_require__(81); +function _parse (integrity, opts) { + // 3.4.3. Parse metadata + // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata + if (opts.single) { + return new Hash(integrity, opts) + } + return integrity.trim().split(/\s+/).reduce((acc, string) => { + const hash = new Hash(string, opts) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) + } + return acc + }, new Integrity()) } -Object.defineProperty(exports, 'parse', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_parse || _load_parse()).default; +module.exports.stringify = stringify +function stringify (obj, opts) { + if (obj.algorithm && obj.digest) { + return Hash.prototype.toString.call(obj, opts) + } else if (typeof obj === 'string') { + return stringify(parse(obj, opts), opts) + } else { + return Integrity.prototype.toString.call(obj, opts) } -}); +} -var _stringify; +module.exports.fromHex = fromHex +function fromHex (hexDigest, algorithm, opts) { + const optString = (opts && opts.options && opts.options.length) + ? `?${opts.options.join('?')}` + : '' + return parse( + `${algorithm}-${ + Buffer.from(hexDigest, 'hex').toString('base64') + }${optString}`, opts + ) +} -function _load_stringify() { - return _stringify = __webpack_require__(150); +module.exports.fromData = fromData +function fromData (data, opts) { + opts = opts || {} + const algorithms = opts.algorithms || ['sha512'] + const optString = opts.options && opts.options.length + ? `?${opts.options.join('?')}` + : '' + return algorithms.reduce((acc, algo) => { + const digest = crypto.createHash(algo).update(data).digest('base64') + const hash = new Hash( + `${algo}-${digest}${optString}`, + opts + ) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) + } + return acc + }, new Integrity()) } -Object.defineProperty(exports, 'stringify', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_stringify || _load_stringify()).default; +module.exports.fromStream = fromStream +function fromStream (stream, opts) { + opts = opts || {} + const P = opts.Promise || Promise + const istream = integrityStream(opts) + return new P((resolve, reject) => { + stream.pipe(istream) + stream.on('error', reject) + istream.on('error', reject) + let sri + istream.on('integrity', s => { sri = s }) + istream.on('end', () => resolve(sri)) + istream.on('data', () => {}) + }) +} + +module.exports.checkData = checkData +function checkData (data, sri, opts) { + opts = opts || {} + sri = parse(sri, opts) + if (!Object.keys(sri).length) { + if (opts.error) { + throw Object.assign( + new Error('No valid integrity hashes to check against'), { + code: 'EINTEGRITY' + } + ) + } else { + return false + } } -}); -exports.implodeEntry = implodeEntry; -exports.explodeEntry = explodeEntry; + const algorithm = sri.pickAlgorithm(opts) + const digest = crypto.createHash(algorithm).update(data).digest('base64') + const newSri = parse({algorithm, digest}) + const match = newSri.match(sri, opts) + if (match || !opts.error) { + return match + } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { + const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`) + err.code = 'EBADSIZE' + err.found = data.length + err.expected = opts.size + err.sri = sri + throw err + } else { + const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`) + err.code = 'EINTEGRITY' + err.found = newSri + err.expected = sri + err.algorithm = algorithm + err.sri = sri + throw err + } +} -var _misc; +module.exports.checkStream = checkStream +function checkStream (stream, sri, opts) { + opts = opts || {} + const P = opts.Promise || Promise + const checker = integrityStream(Object.assign({}, opts, { + integrity: sri + })) + return new P((resolve, reject) => { + stream.pipe(checker) + stream.on('error', reject) + checker.on('error', reject) + let sri + checker.on('verified', s => { sri = s }) + checker.on('end', () => resolve(sri)) + checker.on('data', () => {}) + }) +} -function _load_misc() { - return _misc = __webpack_require__(12); +module.exports.integrityStream = integrityStream +function integrityStream (opts) { + opts = opts || {} + // For verification + const sri = opts.integrity && parse(opts.integrity, opts) + const goodSri = sri && Object.keys(sri).length + const algorithm = goodSri && sri.pickAlgorithm(opts) + const digests = goodSri && sri[algorithm] + // Calculating stream + const algorithms = Array.from( + new Set( + (opts.algorithms || ['sha512']) + .concat(algorithm ? [algorithm] : []) + ) + ) + const hashes = algorithms.map(crypto.createHash) + let streamSize = 0 + const stream = new Transform({ + transform (chunk, enc, cb) { + streamSize += chunk.length + hashes.forEach(h => h.update(chunk, enc)) + cb(null, chunk, enc) + } + }).on('end', () => { + const optString = (opts.options && opts.options.length) + ? `?${opts.options.join('?')}` + : '' + const newSri = parse(hashes.map((h, i) => { + return `${algorithms[i]}-${h.digest('base64')}${optString}` + }).join(' '), opts) + // Integrity verification mode + const match = goodSri && newSri.match(sri, opts) + if (typeof opts.size === 'number' && streamSize !== opts.size) { + const err = new Error(`stream size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${streamSize}`) + err.code = 'EBADSIZE' + err.found = streamSize + err.expected = opts.size + err.sri = sri + stream.emit('error', err) + } else if (opts.integrity && !match) { + const err = new Error(`${sri} integrity checksum failed when using ${algorithm}: wanted ${digests} but got ${newSri}. (${streamSize} bytes)`) + err.code = 'EINTEGRITY' + err.found = newSri + err.expected = digests + err.algorithm = algorithm + err.sri = sri + stream.emit('error', err) + } else { + stream.emit('size', streamSize) + stream.emit('integrity', newSri) + match && stream.emit('verified', match) + } + }) + return stream } -var _normalizePattern; +module.exports.create = createIntegrity +function createIntegrity (opts) { + opts = opts || {} + const algorithms = opts.algorithms || ['sha512'] + const optString = opts.options && opts.options.length + ? `?${opts.options.join('?')}` + : '' -function _load_normalizePattern() { - return _normalizePattern = __webpack_require__(29); -} + const hashes = algorithms.map(crypto.createHash) -var _parse2; + return { + update: function (chunk, enc) { + hashes.forEach(h => h.update(chunk, enc)) + return this + }, + digest: function (enc) { + const integrity = algorithms.reduce((acc, algo) => { + const digest = hashes.shift().digest('base64') + const hash = new Hash( + `${algo}-${digest}${optString}`, + opts + ) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) + } + return acc + }, new Integrity()) -function _load_parse2() { - return _parse2 = _interopRequireDefault(__webpack_require__(81)); + return integrity + } + } } -var _constants; +const NODE_HASHES = new Set(crypto.getHashes()) -function _load_constants() { - return _constants = __webpack_require__(6); +// This is a Best Effort™ at a reasonable priority for hash algos +const DEFAULT_PRIORITY = [ + 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', + // TODO - it's unclear _which_ of these Node will actually use as its name + // for the algorithm, so we guesswork it based on the OpenSSL names. + 'sha3', + 'sha3-256', 'sha3-384', 'sha3-512', + 'sha3_256', 'sha3_384', 'sha3_512' +].filter(algo => NODE_HASHES.has(algo)) + +function getPrioritizedHash (algo1, algo2) { + return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) + ? algo1 + : algo2 } -var _fs; -function _load_fs() { - return _fs = _interopRequireWildcard(__webpack_require__(5)); +/***/ }), +/* 56 */, +/* 57 */, +/* 58 */, +/* 59 */, +/* 60 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = minimatch +minimatch.Minimatch = Minimatch + +var path = { sep: '/' } +try { + path = __webpack_require__(0) +} catch (er) {} + +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = __webpack_require__(175) + +var plTypes = { + '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, + '?': { open: '(?:', close: ')?' }, + '+': { open: '(?:', close: ')+' }, + '*': { open: '(?:', close: ')*' }, + '@': { open: '(?:', close: ')' } } -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +// * => any number of characters +var star = qmark + '*?' -const invariant = __webpack_require__(7); +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' -const path = __webpack_require__(0); -const ssri = __webpack_require__(55); +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' -function getName(pattern) { - return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; -} +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') -function blankObjectUndefined(obj) { - return obj && Object.keys(obj).length ? obj : undefined; +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) } -function keyForRemote(remote) { - return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); -} +// normalizes slashes. +var slashSplit = /\/+/ -function serializeIntegrity(integrity) { - // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output - // See https://git.io/vx2Hy - return integrity.toString().split(' ').sort().join(' '); -} - -function implodeEntry(pattern, obj) { - const inferredName = getName(pattern); - const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; - const imploded = { - name: inferredName === obj.name ? undefined : obj.name, - version: obj.version, - uid: obj.uid === obj.version ? undefined : obj.uid, - resolved: obj.resolved, - registry: obj.registry === 'npm' ? undefined : obj.registry, - dependencies: blankObjectUndefined(obj.dependencies), - optionalDependencies: blankObjectUndefined(obj.optionalDependencies), - permissions: blankObjectUndefined(obj.permissions), - prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) - }; - if (integrity) { - imploded.integrity = integrity; +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) } - return imploded; } -function explodeEntry(pattern, obj) { - obj.optionalDependencies = obj.optionalDependencies || {}; - obj.dependencies = obj.dependencies || {}; - obj.uid = obj.uid || obj.version; - obj.permissions = obj.permissions || {}; - obj.registry = obj.registry || 'npm'; - obj.name = obj.name || getName(pattern); - const integrity = obj.integrity; - if (integrity && integrity.isIntegrity) { - obj.integrity = ssri.parse(integrity); - } - return obj; +function ext (a, b) { + a = a || {} + b = b || {} + var t = {} + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + return t } -class Lockfile { - constructor({ cache, source, parseResultType } = {}) { - this.source = source || ''; - this.cache = cache; - this.parseResultType = parseResultType; - } - - // source string if the `cache` was parsed - - - // if true, we're parsing an old yarn file and need to update integrity fields - hasEntriesExistWithoutIntegrity() { - if (!this.cache) { - return false; - } +minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return minimatch - for (const key in this.cache) { - // $FlowFixMe - `this.cache` is clearly defined at this point - if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { - return true; - } - } + var orig = minimatch - return false; + var m = function minimatch (p, pattern, options) { + return orig.minimatch(p, pattern, ext(def, options)) } - static fromDirectory(dir, reporter) { - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // read the manifest in this directory - const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } - let lockfile; - let rawLockfile = ''; - let parseResult; + return m +} - if (yield (_fs || _load_fs()).exists(lockfileLoc)) { - rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); - parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); +Minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return Minimatch + return minimatch.defaults(def).Minimatch +} - if (reporter) { - if (parseResult.type === 'merge') { - reporter.info(reporter.lang('lockfileMerged')); - } else if (parseResult.type === 'conflict') { - reporter.warn(reporter.lang('lockfileConflict')); - } - } +function minimatch (p, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } - lockfile = parseResult.object; - } else if (reporter) { - reporter.info(reporter.lang('noLockfileFound')); - } + if (!options) options = {} - return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); - })(); + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false } - getLocked(pattern) { - const cache = this.cache; - if (!cache) { - return undefined; - } - - const shrunk = pattern in cache && cache[pattern]; + // "" only matches "" + if (pattern.trim() === '') return p === '' - if (typeof shrunk === 'string') { - return this.getLocked(shrunk); - } else if (shrunk) { - explodeEntry(pattern, shrunk); - return shrunk; - } + return new Minimatch(pattern, options).match(p) +} - return undefined; +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) } - removePattern(pattern) { - const cache = this.cache; - if (!cache) { - return; - } - delete cache[pattern]; + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') } - getLockfile(patterns) { - const lockfile = {}; - const seen = new Map(); - - // order by name so that lockfile manifest is assigned to the first dependency with this manifest - // the others that have the same remoteKey will just refer to the first - // ordering allows for consistency in lockfile when it is serialized - const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); - - for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - const pattern = _ref; + if (!options) options = {} + pattern = pattern.trim() - const pkg = patterns[pattern]; - const remote = pkg._remote, - ref = pkg._reference; + // windows support: need to use /, not \ + if (path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } - invariant(ref, 'Package is missing a reference'); - invariant(remote, 'Package is missing a remote'); + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false - const remoteKey = keyForRemote(remote); - const seenPattern = remoteKey && seen.get(remoteKey); - if (seenPattern) { - // no point in duplicating it - lockfile[pattern] = seenPattern; + // make the set of regexps etc. + this.make() +} - // if we're relying on our name being inferred and two of the patterns have - // different inferred names then we need to set it - if (!seenPattern.name && getName(pattern) !== pkg.name) { - seenPattern.name = pkg.name; - } - continue; - } - const obj = implodeEntry(pattern, { - name: pkg.name, - version: pkg.version, - uid: pkg._uid, - resolved: remote.resolved, - integrity: remote.integrity, - registry: remote.registry, - dependencies: pkg.dependencies, - peerDependencies: pkg.peerDependencies, - optionalDependencies: pkg.optionalDependencies, - permissions: ref.permissions, - prebuiltVariants: pkg.prebuiltVariants - }); +Minimatch.prototype.debug = function () {} - lockfile[pattern] = obj; +Minimatch.prototype.make = make +function make () { + // don't do it more than once. + if (this._made) return - if (remoteKey) { - seen.set(remoteKey, obj); - } - } + var pattern = this.pattern + var options = this.options - return lockfile; + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return } -} -exports.default = Lockfile; -/***/ }), -/* 15 */, -/* 16 */, -/* 17 */ -/***/ (function(module, exports) { + // step 1: figure out negation, etc. + this.parseNegate() -module.exports = __webpack_require__(137); + // step 2: expand braces + var set = this.globSet = this.braceExpand() -/***/ }), -/* 18 */, -/* 19 */, -/* 20 */ -/***/ (function(module, exports, __webpack_require__) { + if (options.debug) this.debug = console.error -"use strict"; + this.debug(this.pattern, set) + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = nullify; -function nullify(obj = {}) { - if (Array.isArray(obj)) { - for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + this.debug(this.pattern, set) - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) - const item = _ref; + this.debug(this.pattern, set) - nullify(item); - } - } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { - Object.setPrototypeOf(obj, null); + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) - // for..in can only be applied to 'object', not 'function' - if (typeof obj === 'object') { - for (const key in obj) { - nullify(obj[key]); - } - } - } + this.debug(this.pattern, set) - return obj; + this.set = set } -/***/ }), -/* 21 */, -/* 22 */ -/***/ (function(module, exports) { - -module.exports = __webpack_require__(139); +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 -/***/ }), -/* 23 */ -/***/ (function(module, exports) { + if (options.nonegate) return -var core = module.exports = { version: '2.5.7' }; -if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate +} -/***/ }), -/* 24 */, -/* 25 */, -/* 26 */, -/* 27 */ -/***/ (function(module, exports, __webpack_require__) { +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) +} -var isObject = __webpack_require__(34); -module.exports = function (it) { - if (!isObject(it)) throw TypeError(it + ' is not an object!'); - return it; -}; +Minimatch.prototype.braceExpand = braceExpand +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } -/***/ }), -/* 28 */, -/* 29 */ -/***/ (function(module, exports, __webpack_require__) { + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern -"use strict"; + if (typeof pattern === 'undefined') { + throw new TypeError('undefined pattern') + } + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.normalizePattern = normalizePattern; + return expand(pattern) +} -/** - * Explode and normalize a pattern into its name and range. - */ +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + if (pattern.length > 1024 * 64) { + throw new TypeError('pattern is too long') + } -function normalizePattern(pattern) { - let hasVersion = false; - let range = 'latest'; - let name = pattern; + var options = this.options - // if we're a scope then remove the @ and add it back later - let isScoped = false; - if (name[0] === '@') { - isScoped = true; - name = name.slice(1); - } + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' - // take first part as the name - const parts = name.split('@'); - if (parts.length > 1) { - name = parts.shift(); - range = parts.join('@'); + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this - if (range) { - hasVersion = true; - } else { - range = '*'; + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break + } + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false } } - // add back @ scope suffix - if (isScoped) { - name = `@${name}`; - } + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) - return { name, range, hasVersion }; -} + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } -/***/ }), -/* 30 */, -/* 31 */ -/***/ (function(module, exports, __webpack_require__) { + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false -var dP = __webpack_require__(50); -var createDesc = __webpack_require__(106); -module.exports = __webpack_require__(33) ? function (object, key, value) { - return dP.f(object, key, createDesc(1, value)); -} : function (object, key, value) { - object[key] = value; - return object; -}; + case '\\': + clearStateChar() + escaping = true + continue + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) -/***/ }), -/* 32 */ -/***/ (function(module, exports, __webpack_require__) { - -/* eslint-disable node/no-deprecated-api */ -var buffer = __webpack_require__(63) -var Buffer = buffer.Buffer - -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] - } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} - -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} - -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} + case '(': + if (inClass) { + re += '(' + continue + } -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return Buffer(size) -} + if (!stateChar) { + re += '\\(' + continue + } -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - return buffer.SlowBuffer(size) -} + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue -// Thank's IE8 for his funny defineProperty -module.exports = !__webpack_require__(85)(function () { - return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; -}); + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } + clearStateChar() + re += '|' + continue -/***/ }), -/* 34 */ -/***/ (function(module, exports) { + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() -module.exports = function (it) { - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; + if (inClass) { + re += '\\' + c + continue + } + inClass = true + classStart = i + reClassStart = re.length + re += c + continue -/***/ }), -/* 35 */ -/***/ (function(module, exports) { + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } -module.exports = {}; + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } + // finish up the class. + hasMagic = true + inClass = false + re += c + continue -/***/ }), -/* 36 */ -/***/ (function(module, exports) { + default: + // swallow any state char that wasn't consumed + clearStateChar() -module.exports = __webpack_require__(120); + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } -/***/ }), -/* 37 */, -/* 38 */, -/* 39 */, -/* 40 */ -/***/ (function(module, exports, __webpack_require__) { + re += c -"use strict"; + } // switch + } // for + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.wait = wait; -exports.promisify = promisify; -exports.queue = queue; -function wait(delay) { - return new Promise(resolve => { - setTimeout(resolve, delay); - }); -} + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } -function promisify(fn, firstData) { - return function (...args) { - return new Promise(function (resolve, reject) { - args.push(function (err, ...result) { - let res = result; + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) - if (result.length <= 1) { - res = result[0]; - } + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type - if (firstData) { - res = err; - err = null; - } + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail + } - if (err) { - reject(err); - } else { - resolve(res); - } - }); + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } - fn.apply(null, args); - }); - }; -} + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } -function queue(arr, promiseProducer, concurrency = Infinity) { - concurrency = Math.min(concurrency, arr.length); + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] - // clone - arr = arr.slice(); + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) - const results = []; - let total = arr.length; - if (!total) { - return Promise.resolve(results); - } + nlLast += nlAfter - return new Promise((resolve, reject) => { - for (let i = 0; i < concurrency; i++) { - next(); + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') } + nlAfter = cleanAfter - function next() { - const item = arr.shift(); - const promise = promiseProducer(item); + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' + } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe + } - promise.then(function (result) { - results.push(result); + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } - total--; - if (total === 0) { - resolve(results); - } else { - if (arr.length) { - next(); - } - } - }, reject); - } - }); -} + if (addPatternStart) { + re = patternStart + re + } -/***/ }), -/* 41 */ -/***/ (function(module, exports, __webpack_require__) { + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } -var global = __webpack_require__(11); -var core = __webpack_require__(23); -var ctx = __webpack_require__(48); -var hide = __webpack_require__(31); -var has = __webpack_require__(49); -var PROTOTYPE = 'prototype'; + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) + } -var $export = function (type, name, source) { - var IS_FORCED = type & $export.F; - var IS_GLOBAL = type & $export.G; - var IS_STATIC = type & $export.S; - var IS_PROTO = type & $export.P; - var IS_BIND = type & $export.B; - var IS_WRAP = type & $export.W; - var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); - var expProto = exports[PROTOTYPE]; - var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; - var key, own, out; - if (IS_GLOBAL) source = name; - for (key in source) { - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if (own && has(exports, key)) continue; - // export native or passed - out = own ? target[key] : source[key]; - // prevent global pollution for namespaces - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] - // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) - // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? (function (C) { - var F = function (a, b, c) { - if (this instanceof C) { - switch (arguments.length) { - case 0: return new C(); - case 1: return new C(a); - case 2: return new C(a, b); - } return new C(a, b, c); - } return C.apply(this, arguments); - }; - F[PROTOTYPE] = C[PROTOTYPE]; - return F; - // make static versions for prototype methods - })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; - // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - if (IS_PROTO) { - (exports.virtual || (exports.virtual = {}))[key] = out; - // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); - } + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') } -}; -// type bitmap -$export.F = 1; // forced -$export.G = 2; // global -$export.S = 4; // static -$export.P = 8; // proto -$export.B = 16; // bind -$export.W = 32; // wrap -$export.U = 64; // safe -$export.R = 128; // real proto method for `library` -module.exports = $export; + regExp._glob = pattern + regExp._src = re -/***/ }), -/* 42 */ -/***/ (function(module, exports, __webpack_require__) { + return regExp +} -try { - var util = __webpack_require__(2); - if (typeof util.inherits !== 'function') throw ''; - module.exports = util.inherits; -} catch (e) { - module.exports = __webpack_require__(224); +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() } +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp -/***/ }), -/* 43 */, -/* 44 */, -/* 45 */ -/***/ (function(module, exports, __webpack_require__) { + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set -"use strict"; + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.home = undefined; + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') -var _rootUser; + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' -function _load_rootUser() { - return _rootUser = _interopRequireDefault(__webpack_require__(169)); -} + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false + } + return this.regexp +} -const path = __webpack_require__(0); +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) + } + return list +} -const home = exports.home = __webpack_require__(36).homedir(); +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' -const userHomeDir = (_rootUser || _load_rootUser()).default ? path.resolve('/usr/local/share') : home; + if (f === '/' && partial) return true + + var options = this.options + + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') + } + + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) + + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. + + var set = this.set + this.debug(this.pattern, 'set', set) + + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break + } + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate + } + } + + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate +} + +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options + + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + + this.debug('matchOne', file.length, pattern.length) + + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] + + this.debug(pattern, p, f) + + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false + + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) + + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } + + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] + + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } + + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } + + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } + + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } + + if (!hit) return false + } + + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* + + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } + + // should be unreachable. + throw new Error('wtf?') +} + +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') +} + +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') +} -exports.default = userHomeDir; /***/ }), -/* 46 */ +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { + +var wrappy = __webpack_require__(123) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + + +/***/ }), +/* 62 */, +/* 63 */ +/***/ (function(module, exports) { + +module.exports = __webpack_require__(281); + +/***/ }), +/* 64 */, +/* 65 */, +/* 66 */, +/* 67 */ /***/ (function(module, exports) { +// 7.2.1 RequireObjectCoercible(argument) module.exports = function (it) { - if (typeof it != 'function') throw TypeError(it + ' is not a function!'); + if (it == undefined) throw TypeError("Can't call method on " + it); return it; }; /***/ }), -/* 47 */ -/***/ (function(module, exports) { - -var toString = {}.toString; +/* 68 */ +/***/ (function(module, exports, __webpack_require__) { +var isObject = __webpack_require__(34); +var document = __webpack_require__(11).document; +// typeof document.createElement is 'object' in old IE +var is = isObject(document) && isObject(document.createElement); module.exports = function (it) { - return toString.call(it).slice(8, -1); + return is ? document.createElement(it) : {}; }; /***/ }), -/* 48 */ +/* 69 */ +/***/ (function(module, exports) { + +module.exports = true; + + +/***/ }), +/* 70 */ /***/ (function(module, exports, __webpack_require__) { -// optional / simple context binding +"use strict"; + +// 25.4.1.5 NewPromiseCapability(C) var aFunction = __webpack_require__(46); -module.exports = function (fn, that, length) { - aFunction(fn); - if (that === undefined) return fn; - switch (length) { - case 1: return function (a) { - return fn.call(that, a); - }; - case 2: return function (a, b) { - return fn.call(that, a, b); - }; - case 3: return function (a, b, c) { - return fn.call(that, a, b, c); - }; - } - return function (/* ...args */) { - return fn.apply(that, arguments); - }; + +function PromiseCapability(C) { + var resolve, reject; + this.promise = new C(function ($$resolve, $$reject) { + if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); + resolve = $$resolve; + reject = $$reject; + }); + this.resolve = aFunction(resolve); + this.reject = aFunction(reject); +} + +module.exports.f = function (C) { + return new PromiseCapability(C); }; /***/ }), -/* 49 */ -/***/ (function(module, exports) { +/* 71 */ +/***/ (function(module, exports, __webpack_require__) { -var hasOwnProperty = {}.hasOwnProperty; -module.exports = function (it, key) { - return hasOwnProperty.call(it, key); +var def = __webpack_require__(50).f; +var has = __webpack_require__(49); +var TAG = __webpack_require__(13)('toStringTag'); + +module.exports = function (it, tag, stat) { + if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); }; /***/ }), -/* 50 */ +/* 72 */ /***/ (function(module, exports, __webpack_require__) { -var anObject = __webpack_require__(27); -var IE8_DOM_DEFINE = __webpack_require__(184); -var toPrimitive = __webpack_require__(201); -var dP = Object.defineProperty; - -exports.f = __webpack_require__(33) ? Object.defineProperty : function defineProperty(O, P, Attributes) { - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if (IE8_DOM_DEFINE) try { - return dP(O, P, Attributes); - } catch (e) { /* empty */ } - if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); - if ('value' in Attributes) O[P] = Attributes.value; - return O; +var shared = __webpack_require__(107)('keys'); +var uid = __webpack_require__(111); +module.exports = function (key) { + return shared[key] || (shared[key] = uid(key)); }; /***/ }), -/* 51 */, -/* 52 */, -/* 53 */, -/* 54 */ +/* 73 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(155); +// 7.1.4 ToInteger +var ceil = Math.ceil; +var floor = Math.floor; +module.exports = function (it) { + return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); +}; + /***/ }), -/* 55 */ +/* 74 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +// to indexed object, toObject with fallback for non-array-like ES3 strings +var IObject = __webpack_require__(131); +var defined = __webpack_require__(67); +module.exports = function (it) { + return IObject(defined(it)); +}; -const Buffer = __webpack_require__(32).Buffer +/***/ }), +/* 75 */ +/***/ (function(module, exports, __webpack_require__) { -const crypto = __webpack_require__(9) -const Transform = __webpack_require__(17).Transform +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. -const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] +module.exports = glob -const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i -const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ -const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ -const VCHAR_REGEX = /^[\x21-\x7E]+$/ +var fs = __webpack_require__(3) +var rp = __webpack_require__(114) +var minimatch = __webpack_require__(60) +var Minimatch = minimatch.Minimatch +var inherits = __webpack_require__(42) +var EE = __webpack_require__(54).EventEmitter +var path = __webpack_require__(0) +var assert = __webpack_require__(22) +var isAbsolute = __webpack_require__(76) +var globSync = __webpack_require__(218) +var common = __webpack_require__(115) +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = __webpack_require__(223) +var util = __webpack_require__(2) +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored -class Hash { - get isHash () { return true } - constructor (hash, opts) { - const strict = !!(opts && opts.strict) - this.source = hash.trim() - // 3.1. Integrity metadata (called "Hash" by ssri) - // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description - const match = this.source.match( - strict - ? STRICT_SRI_REGEX - : SRI_REGEX - ) - if (!match) { return } - if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return } - this.algorithm = match[1] - this.digest = match[2] +var once = __webpack_require__(61) - const rawOpts = match[3] - this.options = rawOpts ? rawOpts.slice(1).split('?') : [] - } - hexDigest () { - return this.digest && Buffer.from(this.digest, 'base64').toString('hex') - } - toJSON () { - return this.toString() - } - toString (opts) { - if (opts && opts.strict) { - // Strict mode enforces the standard as close to the foot of the - // letter as it can. - if (!( - // The spec has very restricted productions for algorithms. - // https://www.w3.org/TR/CSP2/#source-list-syntax - SPEC_ALGORITHMS.some(x => x === this.algorithm) && - // Usually, if someone insists on using a "different" base64, we - // leave it as-is, since there's multiple standards, and the - // specified is not a URL-safe variant. - // https://www.w3.org/TR/CSP2/#base64_value - this.digest.match(BASE64_REGEX) && - // Option syntax is strictly visual chars. - // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression - // https://tools.ietf.org/html/rfc5234#appendix-B.1 - (this.options || []).every(opt => opt.match(VCHAR_REGEX)) - )) { - return '' - } - } - const options = this.options && this.options.length - ? `?${this.options.join('?')}` - : '' - return `${this.algorithm}-${this.digest}${options}` +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} + + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) } + + return new Glob(pattern, options, cb) } -class Integrity { - get isIntegrity () { return true } - toJSON () { - return this.toString() - } - toString (opts) { - opts = opts || {} - let sep = opts.sep || ' ' - if (opts.strict) { - // Entries must be separated by whitespace, according to spec. - sep = sep.replace(/\S+/g, ' ') - } - return Object.keys(this).map(k => { - return this[k].map(hash => { - return Hash.prototype.toString.call(hash, opts) - }).filter(x => x.length).join(sep) - }).filter(x => x.length).join(sep) - } - concat (integrity, opts) { - const other = typeof integrity === 'string' - ? integrity - : stringify(integrity, opts) - return parse(`${this.toString(opts)} ${other}`, opts) - } - hexDigest () { - return parse(this, {single: true}).hexDigest() - } - match (integrity, opts) { - const other = parse(integrity, opts) - const algo = other.pickAlgorithm(opts) - return ( - this[algo] && - other[algo] && - this[algo].find(hash => - other[algo].find(otherhash => - hash.digest === otherhash.digest - ) - ) - ) || false +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync + +// old api surface +glob.glob = glob + +function extend (origin, add) { + if (add === null || typeof add !== 'object') { + return origin } - pickAlgorithm (opts) { - const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash - const keys = Object.keys(this) - if (!keys.length) { - throw new Error(`No algorithms available for ${ - JSON.stringify(this.toString()) - }`) - } - return keys.reduce((acc, algo) => { - return pickAlgorithm(acc, algo) || acc - }) + + var keys = Object.keys(add) + var i = keys.length + while (i--) { + origin[keys[i]] = add[keys[i]] } + return origin } -module.exports.parse = parse -function parse (sri, opts) { - opts = opts || {} - if (typeof sri === 'string') { - return _parse(sri, opts) - } else if (sri.algorithm && sri.digest) { - const fullSri = new Integrity() - fullSri[sri.algorithm] = [sri] - return _parse(stringify(fullSri, opts), opts) - } else { - return _parse(stringify(sri, opts), opts) +glob.hasMagic = function (pattern, options_) { + var options = extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + + if (!pattern) + return false + + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true } + + return false } -function _parse (integrity, opts) { - // 3.4.3. Parse metadata - // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata - if (opts.single) { - return new Hash(integrity, opts) +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null } - return integrity.trim().split(/\s+/).reduce((acc, string) => { - const hash = new Hash(string, opts) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) -} -module.exports.stringify = stringify -function stringify (obj, opts) { - if (obj.algorithm && obj.digest) { - return Hash.prototype.toString.call(obj, opts) - } else if (typeof obj === 'string') { - return stringify(parse(obj, opts), opts) - } else { - return Integrity.prototype.toString.call(obj, opts) + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) } -} -module.exports.fromHex = fromHex -function fromHex (hexDigest, algorithm, opts) { - const optString = (opts && opts.options && opts.options.length) - ? `?${opts.options.join('?')}` - : '' - return parse( - `${algorithm}-${ - Buffer.from(hexDigest, 'hex').toString('base64') - }${optString}`, opts - ) -} + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) -module.exports.fromData = fromData -function fromData (data, opts) { - opts = opts || {} - const algorithms = opts.algorithms || ['sha512'] - const optString = opts.options && opts.options.length - ? `?${opts.options.join('?')}` - : '' - return algorithms.reduce((acc, algo) => { - const digest = crypto.createHash(algo).update(data).digest('base64') - const hash = new Hash( - `${algo}-${digest}${optString}`, - opts - ) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) -} + setopts(this, pattern, options) + this._didRealPath = false -module.exports.fromStream = fromStream -function fromStream (stream, opts) { - opts = opts || {} - const P = opts.Promise || Promise - const istream = integrityStream(opts) - return new P((resolve, reject) => { - stream.pipe(istream) - stream.on('error', reject) - istream.on('error', reject) - let sri - istream.on('integrity', s => { sri = s }) - istream.on('end', () => resolve(sri)) - istream.on('data', () => {}) - }) -} + // process each pattern in the minimatch set + var n = this.minimatch.set.length -module.exports.checkData = checkData -function checkData (data, sri, opts) { - opts = opts || {} - sri = parse(sri, opts) - if (!Object.keys(sri).length) { - if (opts.error) { - throw Object.assign( - new Error('No valid integrity hashes to check against'), { - code: 'EINTEGRITY' - } - ) - } else { - return false - } - } - const algorithm = sri.pickAlgorithm(opts) - const digest = crypto.createHash(algorithm).update(data).digest('base64') - const newSri = parse({algorithm, digest}) - const match = newSri.match(sri, opts) - if (match || !opts.error) { - return match - } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { - const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`) - err.code = 'EBADSIZE' - err.found = data.length - err.expected = opts.size - err.sri = sri - throw err - } else { - const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`) - err.code = 'EINTEGRITY' - err.found = newSri - err.expected = sri - err.algorithm = algorithm - err.sri = sri - throw err + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) } -} -module.exports.checkStream = checkStream -function checkStream (stream, sri, opts) { - opts = opts || {} - const P = opts.Promise || Promise - const checker = integrityStream(Object.assign({}, opts, { - integrity: sri - })) - return new P((resolve, reject) => { - stream.pipe(checker) - stream.on('error', reject) - checker.on('error', reject) - let sri - checker.on('verified', s => { sri = s }) - checker.on('end', () => resolve(sri)) - checker.on('data', () => {}) - }) -} + var self = this + this._processing = 0 -module.exports.integrityStream = integrityStream -function integrityStream (opts) { - opts = opts || {} - // For verification - const sri = opts.integrity && parse(opts.integrity, opts) - const goodSri = sri && Object.keys(sri).length - const algorithm = goodSri && sri.pickAlgorithm(opts) - const digests = goodSri && sri[algorithm] - // Calculating stream - const algorithms = Array.from( - new Set( - (opts.algorithms || ['sha512']) - .concat(algorithm ? [algorithm] : []) - ) - ) - const hashes = algorithms.map(crypto.createHash) - let streamSize = 0 - const stream = new Transform({ - transform (chunk, enc, cb) { - streamSize += chunk.length - hashes.forEach(h => h.update(chunk, enc)) - cb(null, chunk, enc) - } - }).on('end', () => { - const optString = (opts.options && opts.options.length) - ? `?${opts.options.join('?')}` - : '' - const newSri = parse(hashes.map((h, i) => { - return `${algorithms[i]}-${h.digest('base64')}${optString}` - }).join(' '), opts) - // Integrity verification mode - const match = goodSri && newSri.match(sri, opts) - if (typeof opts.size === 'number' && streamSize !== opts.size) { - const err = new Error(`stream size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${streamSize}`) - err.code = 'EBADSIZE' - err.found = streamSize - err.expected = opts.size - err.sri = sri - stream.emit('error', err) - } else if (opts.integrity && !match) { - const err = new Error(`${sri} integrity checksum failed when using ${algorithm}: wanted ${digests} but got ${newSri}. (${streamSize} bytes)`) - err.code = 'EINTEGRITY' - err.found = newSri - err.expected = digests - err.algorithm = algorithm - err.sri = sri - stream.emit('error', err) - } else { - stream.emit('size', streamSize) - stream.emit('integrity', newSri) - match && stream.emit('verified', match) - } - }) - return stream -} + this._emitQueue = [] + this._processQueue = [] + this.paused = false -module.exports.create = createIntegrity -function createIntegrity (opts) { - opts = opts || {} - const algorithms = opts.algorithms || ['sha512'] - const optString = opts.options && opts.options.length - ? `?${opts.options.join('?')}` - : '' + if (this.noprocess) + return this - const hashes = algorithms.map(crypto.createHash) + if (n === 0) + return done() - return { - update: function (chunk, enc) { - hashes.forEach(h => h.update(chunk, enc)) - return this - }, - digest: function (enc) { - const integrity = algorithms.reduce((acc, algo) => { - const digest = hashes.shift().digest('base64') - const hash = new Hash( - `${algo}-${digest}${optString}`, - opts - ) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) + var sync = true + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + sync = false - return integrity + function done () { + --self._processing + if (self._processing <= 0) { + if (sync) { + process.nextTick(function () { + self._finish() + }) + } else { + self._finish() + } } } } -const NODE_HASHES = new Set(crypto.getHashes()) +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return -// This is a Best Effort™ at a reasonable priority for hash algos -const DEFAULT_PRIORITY = [ - 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', - // TODO - it's unclear _which_ of these Node will actually use as its name - // for the algorithm, so we guesswork it based on the OpenSSL names. - 'sha3', - 'sha3-256', 'sha3-384', 'sha3-512', - 'sha3_256', 'sha3_384', 'sha3_512' -].filter(algo => NODE_HASHES.has(algo)) + if (this.realpath && !this._didRealpath) + return this._realpath() -function getPrioritizedHash (algo1, algo2) { - return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) - ? algo1 - : algo2 + common.finish(this) + this.emit('end', this.found) } +Glob.prototype._realpath = function () { + if (this._didRealpath) + return -/***/ }), -/* 56 */, -/* 57 */, -/* 58 */, -/* 59 */, -/* 60 */ -/***/ (function(module, exports, __webpack_require__) { - -module.exports = minimatch -minimatch.Minimatch = Minimatch + this._didRealpath = true -var path = { sep: '/' } -try { - path = __webpack_require__(0) -} catch (er) {} + var n = this.matches.length + if (n === 0) + return this._finish() -var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __webpack_require__(175) + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) -var plTypes = { - '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, - '?': { open: '(?:', close: ')?' }, - '+': { open: '(?:', close: ')+' }, - '*': { open: '(?:', close: ')*' }, - '@': { open: '(?:', close: ')' } + function next () { + if (--n === 0) + self._finish() + } } -// any single thing other than / -// don't need to escape / when using new RegExp() -var qmark = '[^/]' - -// * => any number of characters -var star = qmark + '*?' +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() -// ** when dots are allowed. Anything goes, except .. and . -// not (^ or / followed by one or two dots followed by $ or /), -// followed by anything, any number of times. -var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + var found = Object.keys(matchset) + var self = this + var n = found.length -// not a ^ or / followed by a dot, -// followed by anything, any number of times. -var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + if (n === 0) + return cb() -// characters that need to be escaped in RegExp. -var reSpecials = charSet('().*{}+?[]^$\\!') + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + rp.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here -// "abc" -> { a:true, b:true, c:true } -function charSet (s) { - return s.split('').reduce(function (set, c) { - set[c] = true - return set - }, {}) + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) } -// normalizes slashes. -var slashSplit = /\/+/ - -minimatch.filter = filter -function filter (pattern, options) { - options = options || {} - return function (p, i, list) { - return minimatch(p, pattern, options) - } +Glob.prototype._mark = function (p) { + return common.mark(this, p) } -function ext (a, b) { - a = a || {} - b = b || {} - var t = {} - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) - Object.keys(a).forEach(function (k) { - t[k] = a[k] - }) - return t +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) } -minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return minimatch - - var orig = minimatch +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} - var m = function minimatch (p, pattern, options) { - return orig.minimatch(p, pattern, ext(def, options)) +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') } +} - m.Minimatch = function Minimatch (pattern, options) { - return new orig.Minimatch(pattern, ext(def, options)) +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } } - - return m } -Minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return Minimatch - return minimatch.defaults(def).Minimatch -} +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') -function minimatch (p, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return } - if (!options) options = {} + //console.error('PROCESS %d', this._processing, pattern) - // shortcut: comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - return false + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ } + // now n is the index of the first one that is *not* a string. - // "" only matches "" - if (pattern.trim() === '') return p === '' + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return - return new Minimatch(pattern, options).match(p) -} + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break -function Minimatch (pattern, options) { - if (!(this instanceof Minimatch)) { - return new Minimatch(pattern, options) + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break } - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } + var remain = pattern.slice(n) - if (!options) options = {} - pattern = pattern.trim() + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix - // windows support: need to use /, not \ - if (path.sep !== '/') { - pattern = pattern.split(path.sep).join('/') - } + var abs = this._makeAbs(read) - this.options = options - this.set = [] - this.pattern = pattern - this.regexp = null - this.negate = false - this.comment = false - this.empty = false + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() - // make the set of regexps etc. - this.make() + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) } -Minimatch.prototype.debug = function () {} +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} -Minimatch.prototype.make = make -function make () { - // don't do it more than once. - if (this._made) return +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { - var pattern = this.pattern - var options = this.options + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() - // empty patterns and comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - this.comment = true - return - } - if (!pattern) { - this.empty = true - return + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } } - // step 1: figure out negation, etc. - this.parseNegate() + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) - // step 2: expand braces - var set = this.globSet = this.braceExpand() + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() - if (options.debug) this.debug = console.error + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. - this.debug(this.pattern, set) + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) - // step 3: now we have a set, so turn each one into a series of path-portion - // matching patterns. - // These will be regexps, except in the case of "**", which is - // set to the GLOBSTAR object for globstar behavior, - // and will not contain any / characters - set = this.globParts = set.map(function (s) { - return s.split(slashSplit) - }) + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } - this.debug(this.pattern, set) + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } - // glob --> regexps - set = set.map(function (s, si, set) { - return s.map(this.parse, this) - }, this) + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} - this.debug(this.pattern, set) +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return - // filter out everything that didn't compile properly. - set = set.filter(function (s) { - return s.indexOf(false) === -1 - }) + if (isIgnored(this, e)) + return - this.debug(this.pattern, set) + if (this.paused) { + this._emitQueue.push([index, e]) + return + } - this.set = set -} + var abs = isAbsolute(e) ? e : this._makeAbs(e) -Minimatch.prototype.parseNegate = parseNegate -function parseNegate () { - var pattern = this.pattern - var negate = false - var options = this.options - var negateOffset = 0 + if (this.mark) + e = this._mark(e) - if (options.nonegate) return + if (this.absolute) + e = abs - for (var i = 0, l = pattern.length - ; i < l && pattern.charAt(i) === '!' - ; i++) { - negate = !negate - negateOffset++ + if (this.matches[index][e]) + return + + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return } - if (negateOffset) this.pattern = pattern.substr(negateOffset) - this.negate = negate -} + this.matches[index][e] = true -// Brace expansion: -// a{b,c}d -> abd acd -// a{b,}c -> abc ac -// a{0..3}d -> a0d a1d a2d a3d -// a{b,c{d,e}f}g -> abg acdfg acefg -// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg -// -// Invalid sets are not expanded. -// a{2..}b -> a{2..}b -// a{b}c -> a{b}c -minimatch.braceExpand = function (pattern, options) { - return braceExpand(pattern, options) + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) } -Minimatch.prototype.braceExpand = braceExpand +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return -function braceExpand (pattern, options) { - if (!options) { - if (this instanceof Minimatch) { - options = this.options - } else { - options = {} - } - } + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) - pattern = typeof pattern === 'undefined' - ? this.pattern : pattern + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) - if (typeof pattern === 'undefined') { - throw new TypeError('undefined pattern') - } + if (lstatcb) + fs.lstat(abs, lstatcb) - if (options.nobrace || - !pattern.match(/\{.*\}/)) { - // shortcut. no need to expand. - return [pattern] - } + function lstatcb_ (er, lstat) { + if (er && er.code === 'ENOENT') + return cb() - return expand(pattern) -} + var isSym = lstat && lstat.isSymbolicLink() + self.symlinks[abs] = isSym -// parse a component of the expanded set. -// At this point, no pattern may contain "/" in it -// so we're going to return a 2d array, where each entry is the full -// pattern, split on '/', and then turned into a regular expression. -// A regexp is made at the end which joins each array with an -// escaped /, and another full one which joins each regexp with |. -// -// Following the lead of Bash 4.1, note that "**" only has special meaning -// when it is the *only* thing in a path portion. Otherwise, any series -// of * is equivalent to a single *. Globstar behavior is enabled by -// default, and can be disabled by setting options.noglobstar. -Minimatch.prototype.parse = parse -var SUBPARSE = {} -function parse (pattern, isSub) { - if (pattern.length > 1024 * 64) { - throw new TypeError('pattern is too long') + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) } +} - var options = this.options +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return - // shortcuts - if (!options.noglobstar && pattern === '**') return GLOBSTAR - if (pattern === '') return '' + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) + + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() + + if (Array.isArray(c)) + return cb(null, c) + } - var re = '' - var hasMagic = !!options.nocase - var escaping = false - // ? => one single character - var patternListStack = [] - var negativeLists = [] - var stateChar - var inClass = false - var reClassStart = -1 - var classStart = -1 - // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - var patternStart = pattern.charAt(0) === '.' ? '' // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' - : '(?!\\.)' var self = this + fs.readdir(abs, readdirCb(this, abs, cb)) +} - function clearStateChar () { - if (stateChar) { - // we had some state-tracking character - // that wasn't consumed by this pass. - switch (stateChar) { - case '*': - re += star - hasMagic = true - break - case '?': - re += qmark - hasMagic = true - break - default: - re += '\\' + stateChar - break - } - self.debug('clearStateChar %j %j', stateChar, re) - stateChar = false - } +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) } +} - for (var i = 0, len = pattern.length, c - ; (i < len) && (c = pattern.charAt(i)) - ; i++) { - this.debug('%s\t%s %s %j', pattern, i, re, c) +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return - // skip over any that are escaped. - if (escaping && reSpecials[c]) { - re += '\\' + c - escaping = false - continue + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true } + } - switch (c) { - case '/': - // completely not allowed, even escaped. - // Should already be path-split by now. - return false + this.cache[abs] = entries + return cb(null, entries) +} - case '\\': - clearStateChar() - escaping = true - continue +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return - // the various stateChar values - // for the "extglob" stuff. - case '?': - case '*': - case '+': - case '@': - case '!': - this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + this.emit('error', error) + this.abort() + } + break - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === '!' && i === classStart + 1) c = '^' - re += c - continue - } + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break - // if we already have a stateChar, then it means - // that there was something like ** or +? in there. - // Handle the stateChar, then proceed with this one. - self.debug('call clearStateChar %j', stateChar) - clearStateChar() - stateChar = c - // if extglob is disabled, then +(asdf|foo) isn't a thing. - // just clear the statechar *now*, rather than even diving into - // the patternList stuff. - if (options.noext) clearStateChar() - continue + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } - case '(': - if (inClass) { - re += '(' - continue - } + return cb() +} - if (!stateChar) { - re += '\\(' - continue - } +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} - patternListStack.push({ - type: stateChar, - start: i - 1, - reStart: re.length, - open: plTypes[stateChar].open, - close: plTypes[stateChar].close - }) - // negation is (?:(?!js)[^/]*) - re += stateChar === '!' ? '(?:(?!(?:' : '(?:' - this.debug('plType %j %j', stateChar, re) - stateChar = false - continue - case ')': - if (inClass || !patternListStack.length) { - re += '\\)' - continue - } +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) - clearStateChar() - hasMagic = true - var pl = patternListStack.pop() - // negation is (?:(?!js)[^/]*) - // The others are (?:) - re += pl.close - if (pl.type === '!') { - negativeLists.push(pl) - } - pl.reEnd = re.length - continue + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() - case '|': - if (inClass || !patternListStack.length || escaping) { - re += '\\|' - escaping = false - continue - } + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) - clearStateChar() - re += '|' - continue + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) - // these are mostly the same in regexp and glob - case '[': - // swallow any state-tracking char before the [ - clearStateChar() + var isSym = this.symlinks[abs] + var len = entries.length - if (inClass) { - re += '\\' + c - continue - } + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() - inClass = true - classStart = i - reClassStart = re.length - re += c + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) continue - case ']': - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += '\\' + c - escaping = false - continue - } + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) - // handle the case where we left a class open. - // "[z-a]" is valid, equivalent to "\[z-a\]" - if (inClass) { - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - var cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + cs + ']') - } catch (er) { - // not a valid class! - var sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' - hasMagic = hasMagic || sp[1] - inClass = false - continue - } - } + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) + } - // finish up the class. - hasMagic = true - inClass = false - re += c - continue + cb() +} - default: - // swallow any state char that wasn't consumed - clearStateChar() +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { - if (escaping) { - // no need - escaping = false - } else if (reSpecials[c] - && !(c === '^' && inClass)) { - re += '\\' - } + //console.error('ps2', prefix, exists) - re += c + if (!this.matches[index]) + this.matches[index] = Object.create(null) - } // switch - } // for + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - cs = pattern.substr(classStart + 1) - sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] - hasMagic = hasMagic || sp[1] + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } } - // handle the case where we had a +( thing at the *end* - // of the pattern. - // each pattern list stack adds 3 chars, and we need to go through - // and escape any | chars that were passed through as-is for the regexp. - // Go through and escape them, taking care not to double-escape any - // | chars that were already escaped. - for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { - var tail = re.slice(pl.reStart + pl.open.length) - this.debug('setting tail', re, pl) - // maybe some even number of \, then maybe 1 \, followed by a | - tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { - if (!$2) { - // the | isn't already escaped, so escape it. - $2 = '\\' - } + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') - // need to escape all those slashes *again*, without escaping the - // one that we need for escaping the | character. As it works out, - // escaping an even number of slashes can be done by simply repeating - // it exactly after itself. That's why this trick works. - // - // I am sorry that you have to see this. - return $1 + $1 + $2 + '|' - }) + // Mark this as a match + this._emitMatch(index, prefix) + cb() +} - this.debug('tail=%j\n %s', tail, tail, pl, re) - var t = pl.type === '*' ? star - : pl.type === '?' ? qmark - : '\\' + pl.type +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' - hasMagic = true - re = re.slice(0, pl.reStart) + t + '\\(' + tail - } + if (f.length > this.maxLength) + return cb() - // handle trailing things that only matter at the very end. - clearStateChar() - if (escaping) { - // trailing \\ - re += '\\\\' - } + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] - // only need to apply the nodot start if the re starts with - // something that could conceivably capture a dot - var addPatternStart = false - switch (re.charAt(0)) { - case '.': - case '[': - case '(': addPatternStart = true - } + if (Array.isArray(c)) + c = 'DIR' - // Hack to work around lack of negative lookbehind in JS - // A pattern like: *.!(x).!(y|z) needs to ensure that a name - // like 'a.xyz.yz' doesn't match. So, the first negative - // lookahead, has to look ALL the way ahead, to the end of - // the pattern. - for (var n = negativeLists.length - 1; n > -1; n--) { - var nl = negativeLists[n] + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) - var nlBefore = re.slice(0, nl.reStart) - var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) - var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) - var nlAfter = re.slice(nl.reEnd) + if (needDir && c === 'FILE') + return cb() - nlLast += nlAfter + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } - // Handle nested stuff like *(*.js|!(*.json)), where open parens - // mean that we should *not* include the ) in the bit that is considered - // "after" the negated section. - var openParensBefore = nlBefore.split('(').length - 1 - var cleanAfter = nlAfter - for (i = 0; i < openParensBefore; i++) { - cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) } - nlAfter = cleanAfter + } - var dollar = '' - if (nlAfter === '' && isSub !== SUBPARSE) { - dollar = '$' + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) } - var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast - re = newRe } +} - // if the re is not "" at this point, then we need to make sure - // it doesn't match against an empty path part. - // Otherwise a/* will match a/, which it should not. - if (re !== '' && hasMagic) { - re = '(?=.)' + re +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return cb() } - if (addPatternStart) { - re = patternStart + re - } + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [re, hasMagic] - } + if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) + return cb(null, false, stat) - // skip the regexp for non-magical patterns - // unescape anything in it, though, so that it'll be - // an exact match against a file etc. - if (!hasMagic) { - return globUnescape(pattern) - } + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c - var flags = options.nocase ? 'i' : '' - try { - var regExp = new RegExp('^' + re + '$', flags) - } catch (er) { - // If it was an invalid regular expression, then it can't match - // anything. This trick looks for a character after the end of - // the string, which is of course impossible, except in multi-line - // mode, but it's not a /m regex. - return new RegExp('$.') - } + if (needDir && c === 'FILE') + return cb() - regExp._glob = pattern - regExp._src = re + return cb(null, c, stat) +} - return regExp + +/***/ }), +/* 76 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +function posix(path) { + return path.charAt(0) === '/'; } -minimatch.makeRe = function (pattern, options) { - return new Minimatch(pattern, options || {}).makeRe() +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); } -Minimatch.prototype.makeRe = makeRe -function makeRe () { - if (this.regexp || this.regexp === false) return this.regexp +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; - // at this point, this.set is a 2d array of partial - // pattern strings, or "**". - // - // It's better to use .match(). This function shouldn't - // be used, really, but it's pretty convenient sometimes, - // when you just want to work with a regex. - var set = this.set - if (!set.length) { - this.regexp = false - return this.regexp - } - var options = this.options +/***/ }), +/* 77 */, +/* 78 */, +/* 79 */ +/***/ (function(module, exports) { - var twoStar = options.noglobstar ? star - : options.dot ? twoStarDot - : twoStarNoDot - var flags = options.nocase ? 'i' : '' +module.exports = __webpack_require__(121); - var re = set.map(function (pattern) { - return pattern.map(function (p) { - return (p === GLOBSTAR) ? twoStar - : (typeof p === 'string') ? regExpEscape(p) - : p._src - }).join('\\\/') - }).join('|') +/***/ }), +/* 80 */, +/* 81 */ +/***/ (function(module, exports, __webpack_require__) { - // must match entire pattern - // ending in a * or ** will make it less strict. - re = '^(?:' + re + ')$' +"use strict"; - // can match anything, as long as it's not this. - if (this.negate) re = '^(?!' + re + ').*$' - try { - this.regexp = new RegExp(re, flags) - } catch (ex) { - this.regexp = false - } - return this.regexp -} +Object.defineProperty(exports, "__esModule", { + value: true +}); -minimatch.match = function (list, pattern, options) { - options = options || {} - var mm = new Minimatch(pattern, options) - list = list.filter(function (f) { - return mm.match(f) - }) - if (mm.options.nonull && !list.length) { - list.push(pattern) - } - return list +exports.default = function (str, fileLoc = 'lockfile') { + str = (0, (_stripBom || _load_stripBom()).default)(str); + return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : { type: 'success', object: parse(str, fileLoc) }; +}; + +var _util; + +function _load_util() { + return _util = _interopRequireDefault(__webpack_require__(2)); } -Minimatch.prototype.match = match -function match (f, partial) { - this.debug('match', f, this.pattern) - // short-circuit in the case of busted things. - // comments, etc. - if (this.comment) return false - if (this.empty) return f === '' +var _invariant; - if (f === '/' && partial) return true +function _load_invariant() { + return _invariant = _interopRequireDefault(__webpack_require__(7)); +} - var options = this.options +var _stripBom; - // windows: need to use /, not \ - if (path.sep !== '/') { - f = f.split(path.sep).join('/') - } +function _load_stripBom() { + return _stripBom = _interopRequireDefault(__webpack_require__(122)); +} - // treat the test path as a set of pathparts. - f = f.split(slashSplit) - this.debug(this.pattern, 'split', f) +var _constants; - // just ONE of the pattern sets in this.set needs to match - // in order for it to be valid. If negating, then just one - // match means that we have failed. - // Either way, return on the first hit. +function _load_constants() { + return _constants = __webpack_require__(6); +} - var set = this.set - this.debug(this.pattern, 'set', set) +var _errors; - // Find the basename of the path by looking for the last non-empty segment - var filename - var i - for (i = f.length - 1; i >= 0; i--) { - filename = f[i] - if (filename) break - } +function _load_errors() { + return _errors = __webpack_require__(4); +} - for (i = 0; i < set.length; i++) { - var pattern = set[i] - var file = f - if (options.matchBase && pattern.length === 1) { - file = [filename] - } - var hit = this.matchOne(file, pattern, partial) - if (hit) { - if (options.flipNegate) return true - return !this.negate - } - } +var _map; - // didn't get any hits. this is success if it's a negative - // pattern, failure otherwise. - if (options.flipNegate) return false - return this.negate +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); } -// set partial to true to test if, for example, -// "/a/b" matches the start of "/*/b/*/d" -// Partial means, if you run out of file before you run -// out of pattern, then that's fine, as long as all -// the parts match. -Minimatch.prototype.matchOne = function (file, pattern, partial) { - var options = this.options +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - this.debug('matchOne', - { 'this': this, file: file, pattern: pattern }) +/* eslint quotes: 0 */ - this.debug('matchOne', file.length, pattern.length) +const VERSION_REGEX = /^yarn lockfile v(\d+)$/; - for (var fi = 0, - pi = 0, - fl = file.length, - pl = pattern.length - ; (fi < fl) && (pi < pl) - ; fi++, pi++) { - this.debug('matchOne loop') - var p = pattern[pi] - var f = file[fi] +const TOKEN_TYPES = { + boolean: 'BOOLEAN', + string: 'STRING', + identifier: 'IDENTIFIER', + eof: 'EOF', + colon: 'COLON', + newline: 'NEWLINE', + comment: 'COMMENT', + indent: 'INDENT', + invalid: 'INVALID', + number: 'NUMBER', + comma: 'COMMA' +}; - this.debug(pattern, p, f) +const VALID_PROP_VALUE_TOKENS = [TOKEN_TYPES.boolean, TOKEN_TYPES.string, TOKEN_TYPES.number]; - // should be impossible. - // some invalid regexp stuff in the set. - if (p === false) return false +function isValidPropValueToken(token) { + return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0; +} - if (p === GLOBSTAR) { - this.debug('GLOBSTAR', [pattern, p, f]) +function* tokenise(input) { + let lastNewline = false; + let line = 1; + let col = 0; - // "**" - // a/**/b/**/c would match the following: - // a/b/x/y/z/c - // a/x/y/z/b/c - // a/b/x/b/x/c - // a/b/c - // To do this, take the rest of the pattern after - // the **, and see if it would match the file remainder. - // If so, return success. - // If not, the ** "swallows" a segment, and try again. - // This is recursively awful. - // - // a/**/b/**/c matching a/b/x/y/z/c - // - a matches a - // - doublestar - // - matchOne(b/x/y/z/c, b/**/c) - // - b matches b - // - doublestar - // - matchOne(x/y/z/c, c) -> no - // - matchOne(y/z/c, c) -> no - // - matchOne(z/c, c) -> no - // - matchOne(c, c) yes, hit - var fr = fi - var pr = pi + 1 - if (pr === pl) { - this.debug('** at the end') - // a ** at the end will just swallow the rest. - // We have found a match. - // however, it will not swallow /.x, unless - // options.dot is set. - // . and .. are *never* matched by **, for explosively - // exponential reasons. - for (; fi < fl; fi++) { - if (file[fi] === '.' || file[fi] === '..' || - (!options.dot && file[fi].charAt(0) === '.')) return false - } - return true - } + function buildToken(type, value) { + return { line, col, type, value }; + } - // ok, let's see if we can swallow whatever we can. - while (fr < fl) { - var swallowee = file[fr] + while (input.length) { + let chop = 0; - this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + if (input[0] === '\n' || input[0] === '\r') { + chop++; + // If this is a \r\n line, ignore both chars but only add one new line + if (input[1] === '\n') { + chop++; + } + line++; + col = 0; + yield buildToken(TOKEN_TYPES.newline); + } else if (input[0] === '#') { + chop++; - // XXX remove this slice. Just pass the start index. - if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { - this.debug('globstar found match!', fr, fl, swallowee) - // found a match. - return true + let val = ''; + while (input[chop] !== '\n') { + val += input[chop]; + chop++; + } + yield buildToken(TOKEN_TYPES.comment, val); + } else if (input[0] === ' ') { + if (lastNewline) { + let indent = ''; + for (let i = 0; input[i] === ' '; i++) { + indent += input[i]; + } + + if (indent.length % 2) { + throw new TypeError('Invalid number of spaces'); } else { - // can't swallow "." or ".." ever. - // can only swallow ".foo" when explicitly asked. - if (swallowee === '.' || swallowee === '..' || - (!options.dot && swallowee.charAt(0) === '.')) { - this.debug('dot detected!', file, fr, pattern, pr) - break - } + chop = indent.length; + yield buildToken(TOKEN_TYPES.indent, indent.length / 2); + } + } else { + chop++; + } + } else if (input[0] === '"') { + let val = ''; - // ** swallows a segment, and continue. - this.debug('globstar swallow a segment, and continue') - fr++ + for (let i = 0;; i++) { + const currentChar = input[i]; + val += currentChar; + + if (i > 0 && currentChar === '"') { + const isEscaped = input[i - 1] === '\\' && input[i - 2] !== '\\'; + if (!isEscaped) { + break; + } } } - // no match was found. - // However, in partial mode, we can't say this is necessarily over. - // If there's more *pattern* left, then - if (partial) { - // ran out of file - this.debug('\n>>> no match, partial?', file, fr, pattern, pr) - if (fr === fl) return true + chop = val.length; + + try { + yield buildToken(TOKEN_TYPES.string, JSON.parse(val)); + } catch (err) { + if (err instanceof SyntaxError) { + yield buildToken(TOKEN_TYPES.invalid); + } else { + throw err; + } } - return false - } + } else if (/^[0-9]/.test(input)) { + let val = ''; + for (let i = 0; /^[0-9]$/.test(input[i]); i++) { + val += input[i]; + } + chop = val.length; - // something other than ** - // non-magic patterns just have to match exactly - // patterns with magic have been turned into regexps. - var hit - if (typeof p === 'string') { - if (options.nocase) { - hit = f.toLowerCase() === p.toLowerCase() - } else { - hit = f === p + yield buildToken(TOKEN_TYPES.number, +val); + } else if (/^true/.test(input)) { + yield buildToken(TOKEN_TYPES.boolean, true); + chop = 4; + } else if (/^false/.test(input)) { + yield buildToken(TOKEN_TYPES.boolean, false); + chop = 5; + } else if (input[0] === ':') { + yield buildToken(TOKEN_TYPES.colon); + chop++; + } else if (input[0] === ',') { + yield buildToken(TOKEN_TYPES.comma); + chop++; + } else if (/^[a-zA-Z\/-]/g.test(input)) { + let name = ''; + for (let i = 0; i < input.length; i++) { + const char = input[i]; + if (char === ':' || char === ' ' || char === '\n' || char === '\r' || char === ',') { + break; + } else { + name += char; + } } - this.debug('string match', p, f, hit) + chop = name.length; + + yield buildToken(TOKEN_TYPES.string, name); } else { - hit = f.match(p) - this.debug('pattern match', p, f, hit) + yield buildToken(TOKEN_TYPES.invalid); } - if (!hit) return false + if (!chop) { + // will trigger infinite recursion + yield buildToken(TOKEN_TYPES.invalid); + } + + col += chop; + lastNewline = input[0] === '\n' || input[0] === '\r' && input[1] === '\n'; + input = input.slice(chop); } - // Note: ending in / means that we'll get a final "" - // at the end of the pattern. This can only match a - // corresponding "" at the end of the file. - // If the file ends in /, then it can only match a - // a pattern that ends in /, unless the pattern just - // doesn't have any more for it. But, a/b/ should *not* - // match "a/b/*", even though "" matches against the - // [^/]*? pattern, except in partial mode, where it might - // simply not be reached yet. - // However, a/b/ should still satisfy a/* + yield buildToken(TOKEN_TYPES.eof); +} - // now either we fell off the end of the pattern, or we're done. - if (fi === fl && pi === pl) { - // ran out of pattern and filename at the same time. - // an exact hit! - return true - } else if (fi === fl) { - // ran out of file, but still had pattern left. - // this is ok if we're doing the match as part of - // a glob fs traversal. - return partial - } else if (pi === pl) { - // ran out of pattern, still have file left. - // this is only acceptable if we're on the very last - // empty segment of a file with a trailing slash. - // a/* should match a/b/ - var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') - return emptyFileEnd +class Parser { + constructor(input, fileLoc = 'lockfile') { + this.comments = []; + this.tokens = tokenise(input); + this.fileLoc = fileLoc; } - // should be unreachable. - throw new Error('wtf?') -} + onComment(token) { + const value = token.value; + (0, (_invariant || _load_invariant()).default)(typeof value === 'string', 'expected token value to be a string'); -// replace stuff like \* with * -function globUnescape (s) { - return s.replace(/\\(.)/g, '$1') -} + const comment = value.trim(); -function regExpEscape (s) { - return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') -} + const versionMatch = comment.match(VERSION_REGEX); + if (versionMatch) { + const version = +versionMatch[1]; + if (version > (_constants || _load_constants()).LOCKFILE_VERSION) { + throw new (_errors || _load_errors()).MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports ` + `versions up to ${(_constants || _load_constants()).LOCKFILE_VERSION}. Run \`$ yarn self-update\` to upgrade to the latest version.`); + } + } + this.comments.push(comment); + } -/***/ }), -/* 61 */ -/***/ (function(module, exports, __webpack_require__) { + next() { + const item = this.tokens.next(); + (0, (_invariant || _load_invariant()).default)(item, 'expected a token'); -var wrappy = __webpack_require__(123) -module.exports = wrappy(once) -module.exports.strict = wrappy(onceStrict) + const done = item.done, + value = item.value; -once.proto = once(function () { - Object.defineProperty(Function.prototype, 'once', { - value: function () { - return once(this) - }, - configurable: true - }) + if (done || !value) { + throw new Error('No more tokens'); + } else if (value.type === TOKEN_TYPES.comment) { + this.onComment(value); + return this.next(); + } else { + return this.token = value; + } + } - Object.defineProperty(Function.prototype, 'onceStrict', { - value: function () { - return onceStrict(this) - }, - configurable: true - }) -}) + unexpected(msg = 'Unexpected token') { + throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`); + } -function once (fn) { - var f = function () { - if (f.called) return f.value - f.called = true - return f.value = fn.apply(this, arguments) + expect(tokType) { + if (this.token.type === tokType) { + this.next(); + } else { + this.unexpected(); + } } - f.called = false - return f -} -function onceStrict (fn) { - var f = function () { - if (f.called) - throw new Error(f.onceError) - f.called = true - return f.value = fn.apply(this, arguments) + eat(tokType) { + if (this.token.type === tokType) { + this.next(); + return true; + } else { + return false; + } } - var name = fn.name || 'Function wrapped with `once`' - f.onceError = name + " shouldn't be called more than once" - f.called = false - return f -} + parse(indent = 0) { + const obj = (0, (_map || _load_map()).default)(); -/***/ }), -/* 62 */, -/* 63 */ -/***/ (function(module, exports) { + while (true) { + const propToken = this.token; -module.exports = __webpack_require__(285); + if (propToken.type === TOKEN_TYPES.newline) { + const nextToken = this.next(); + if (!indent) { + // if we have 0 indentation then the next token doesn't matter + continue; + } -/***/ }), -/* 64 */, -/* 65 */, -/* 66 */, -/* 67 */ -/***/ (function(module, exports) { + if (nextToken.type !== TOKEN_TYPES.indent) { + // if we have no indentation after a newline then we've gone down a level + break; + } -// 7.2.1 RequireObjectCoercible(argument) -module.exports = function (it) { - if (it == undefined) throw TypeError("Can't call method on " + it); - return it; -}; + if (nextToken.value === indent) { + // all is good, the indent is on our level + this.next(); + } else { + // the indentation is less than our level + break; + } + } else if (propToken.type === TOKEN_TYPES.indent) { + if (propToken.value === indent) { + this.next(); + } else { + break; + } + } else if (propToken.type === TOKEN_TYPES.eof) { + break; + } else if (propToken.type === TOKEN_TYPES.string) { + // property key + const key = propToken.value; + (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); + const keys = [key]; + this.next(); -/***/ }), -/* 68 */ -/***/ (function(module, exports, __webpack_require__) { + // support multiple keys + while (this.token.type === TOKEN_TYPES.comma) { + this.next(); // skip comma -var isObject = __webpack_require__(34); -var document = __webpack_require__(11).document; -// typeof document.createElement is 'object' in old IE -var is = isObject(document) && isObject(document.createElement); -module.exports = function (it) { - return is ? document.createElement(it) : {}; -}; + const keyToken = this.token; + if (keyToken.type !== TOKEN_TYPES.string) { + this.unexpected('Expected string'); + } + const key = keyToken.value; + (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); + keys.push(key); + this.next(); + } -/***/ }), -/* 69 */ -/***/ (function(module, exports) { + const valToken = this.token; -module.exports = true; + if (valToken.type === TOKEN_TYPES.colon) { + // object + this.next(); + // parse object + const val = this.parse(indent + 1); -/***/ }), -/* 70 */ -/***/ (function(module, exports, __webpack_require__) { + for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; -"use strict"; + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } -// 25.4.1.5 NewPromiseCapability(C) -var aFunction = __webpack_require__(46); + const key = _ref; -function PromiseCapability(C) { - var resolve, reject; - this.promise = new C(function ($$resolve, $$reject) { - if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); - resolve = $$resolve; - reject = $$reject; - }); - this.resolve = aFunction(resolve); - this.reject = aFunction(reject); -} + obj[key] = val; + } -module.exports.f = function (C) { - return new PromiseCapability(C); -}; + if (indent && this.token.type !== TOKEN_TYPES.indent) { + break; + } + } else if (isValidPropValueToken(valToken)) { + // plain value + for (var _iterator2 = keys, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref2; + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref2 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref2 = _i2.value; + } -/***/ }), -/* 71 */ -/***/ (function(module, exports, __webpack_require__) { + const key = _ref2; -var def = __webpack_require__(50).f; -var has = __webpack_require__(49); -var TAG = __webpack_require__(13)('toStringTag'); - -module.exports = function (it, tag, stat) { - if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); -}; + obj[key] = valToken.value; + } + this.next(); + } else { + this.unexpected('Invalid value type'); + } + } else { + this.unexpected(`Unknown token: ${(_util || _load_util()).default.inspect(propToken)}`); + } + } -/***/ }), -/* 72 */ -/***/ (function(module, exports, __webpack_require__) { + return obj; + } +} -var shared = __webpack_require__(107)('keys'); -var uid = __webpack_require__(111); -module.exports = function (key) { - return shared[key] || (shared[key] = uid(key)); -}; +const MERGE_CONFLICT_ANCESTOR = '|||||||'; +const MERGE_CONFLICT_END = '>>>>>>>'; +const MERGE_CONFLICT_SEP = '======='; +const MERGE_CONFLICT_START = '<<<<<<<'; +/** + * Extract the two versions of the lockfile from a merge conflict. + */ +function extractConflictVariants(str) { + const variants = [[], []]; + const lines = str.split(/\r?\n/g); + let skip = false; -/***/ }), -/* 73 */ -/***/ (function(module, exports) { + while (lines.length) { + const line = lines.shift(); + if (line.startsWith(MERGE_CONFLICT_START)) { + // get the first variant + while (lines.length) { + const conflictLine = lines.shift(); + if (conflictLine === MERGE_CONFLICT_SEP) { + skip = false; + break; + } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) { + skip = true; + continue; + } else { + variants[0].push(conflictLine); + } + } -// 7.1.4 ToInteger -var ceil = Math.ceil; -var floor = Math.floor; -module.exports = function (it) { - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); -}; + // get the second variant + while (lines.length) { + const conflictLine = lines.shift(); + if (conflictLine.startsWith(MERGE_CONFLICT_END)) { + break; + } else { + variants[1].push(conflictLine); + } + } + } else { + variants[0].push(line); + variants[1].push(line); + } + } + return [variants[0].join('\n'), variants[1].join('\n')]; +} -/***/ }), -/* 74 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Check if a lockfile has merge conflicts. + */ +function hasMergeConflicts(str) { + return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END); +} -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = __webpack_require__(131); -var defined = __webpack_require__(67); -module.exports = function (it) { - return IObject(defined(it)); -}; +/** + * Parse the lockfile. + */ +function parse(str, fileLoc) { + const parser = new Parser(str, fileLoc); + parser.next(); + return parser.parse(); +} +/** + * Parse and merge the two variants in a conflicted lockfile. + */ +function parseWithConflict(str, fileLoc) { + const variants = extractConflictVariants(str); + try { + return { type: 'merge', object: Object.assign({}, parse(variants[0], fileLoc), parse(variants[1], fileLoc)) }; + } catch (err) { + if (err instanceof SyntaxError) { + return { type: 'conflict', object: {} }; + } else { + throw err; + } + } +} /***/ }), -/* 75 */ +/* 82 */, +/* 83 */, +/* 84 */ /***/ (function(module, exports, __webpack_require__) { -// Approach: -// -// 1. Get the minimatch set -// 2. For each pattern in the set, PROCESS(pattern, false) -// 3. Store matches per-set, then uniq them -// -// PROCESS(pattern, inGlobStar) -// Get the first [n] items from pattern that are all strings -// Join these together. This is PREFIX. -// If there is no more remaining, then stat(PREFIX) and -// add to matches if it succeeds. END. -// -// If inGlobStar and PREFIX is symlink and points to dir -// set ENTRIES = [] -// else readdir(PREFIX) as ENTRIES -// If fail, END -// -// with ENTRIES -// If pattern[n] is GLOBSTAR -// // handle the case where the globstar match is empty -// // by pruning it out, and testing the resulting pattern -// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) -// // handle other cases. -// for ENTRY in ENTRIES (not dotfiles) -// // attach globstar + tail onto the entry -// // Mark that this entry is a globstar match -// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) -// -// else // not globstar -// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) -// Test ENTRY against pattern[n] -// If fails, continue -// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) -// -// Caveat: -// Cache all stats and readdirs results to minimize syscall. Since all -// we ever care about is existence and directory-ness, we can just keep -// `true` for files, and [children,...] for directories, or `false` for -// things that don't exist. - -module.exports = glob - -var fs = __webpack_require__(3) -var rp = __webpack_require__(114) -var minimatch = __webpack_require__(60) -var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(42) -var EE = __webpack_require__(54).EventEmitter -var path = __webpack_require__(0) -var assert = __webpack_require__(22) -var isAbsolute = __webpack_require__(76) -var globSync = __webpack_require__(218) -var common = __webpack_require__(115) -var alphasort = common.alphasort -var alphasorti = common.alphasorti -var setopts = common.setopts -var ownProp = common.ownProp -var inflight = __webpack_require__(223) -var util = __webpack_require__(2) -var childrenIgnored = common.childrenIgnored -var isIgnored = common.isIgnored +"use strict"; -var once = __webpack_require__(61) -function glob (pattern, options, cb) { - if (typeof options === 'function') cb = options, options = {} - if (!options) options = {} +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return globSync(pattern, options) - } +var _map; - return new Glob(pattern, options, cb) +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); } -glob.sync = globSync -var GlobSync = glob.GlobSync = globSync.GlobSync +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -// old api surface -glob.glob = glob +const debug = __webpack_require__(212)('yarn'); -function extend (origin, add) { - if (add === null || typeof add !== 'object') { - return origin - } +class BlockingQueue { + constructor(alias, maxConcurrency = Infinity) { + this.concurrencyQueue = []; + this.maxConcurrency = maxConcurrency; + this.runningCount = 0; + this.warnedStuck = false; + this.alias = alias; + this.first = true; - var keys = Object.keys(add) - var i = keys.length - while (i--) { - origin[keys[i]] = add[keys[i]] - } - return origin -} + this.running = (0, (_map || _load_map()).default)(); + this.queue = (0, (_map || _load_map()).default)(); -glob.hasMagic = function (pattern, options_) { - var options = extend({}, options_) - options.noprocess = true + this.stuckTick = this.stuckTick.bind(this); + } - var g = new Glob(pattern, options) - var set = g.minimatch.set + stillActive() { + if (this.stuckTimer) { + clearTimeout(this.stuckTimer); + } - if (!pattern) - return false + this.stuckTimer = setTimeout(this.stuckTick, 5000); - if (set.length > 1) - return true + // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 + // $FlowFixMe: Node's setInterval returns a Timeout, not a Number + this.stuckTimer.unref && this.stuckTimer.unref(); + } - for (var j = 0; j < set[0].length; j++) { - if (typeof set[0][j] !== 'string') - return true + stuckTick() { + if (this.runningCount === 1) { + this.warnedStuck = true; + debug(`The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + `without any activity with 1 worker: ${Object.keys(this.running)[0]}`); + } } - return false -} + push(key, factory) { + if (this.first) { + this.first = false; + } else { + this.stillActive(); + } -glob.Glob = Glob -inherits(Glob, EE) -function Glob (pattern, options, cb) { - if (typeof options === 'function') { - cb = options - options = null - } + return new Promise((resolve, reject) => { + // we're already running so push ourselves to the queue + const queue = this.queue[key] = this.queue[key] || []; + queue.push({ factory, resolve, reject }); - if (options && options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return new GlobSync(pattern, options) + if (!this.running[key]) { + this.shift(key); + } + }); } - if (!(this instanceof Glob)) - return new Glob(pattern, options, cb) + shift(key) { + if (this.running[key]) { + delete this.running[key]; + this.runningCount--; - setopts(this, pattern, options) - this._didRealPath = false + if (this.stuckTimer) { + clearTimeout(this.stuckTimer); + this.stuckTimer = null; + } - // process each pattern in the minimatch set - var n = this.minimatch.set.length + if (this.warnedStuck) { + this.warnedStuck = false; + debug(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); + } + } - // The matches are stored as {: true,...} so that - // duplicates are automagically pruned. - // Later, we do an Object.keys() on these. - // Keep them as a list so we can fill in when nonull is set. - this.matches = new Array(n) + const queue = this.queue[key]; + if (!queue) { + return; + } - if (typeof cb === 'function') { - cb = once(cb) - this.on('error', cb) - this.on('end', function (matches) { - cb(null, matches) - }) - } + var _queue$shift = queue.shift(); - var self = this - this._processing = 0 + const resolve = _queue$shift.resolve, + reject = _queue$shift.reject, + factory = _queue$shift.factory; - this._emitQueue = [] - this._processQueue = [] - this.paused = false + if (!queue.length) { + delete this.queue[key]; + } - if (this.noprocess) - return this + const next = () => { + this.shift(key); + this.shiftConcurrencyQueue(); + }; - if (n === 0) - return done() + const run = () => { + this.running[key] = true; + this.runningCount++; - var sync = true - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false, done) + factory().then(function (val) { + resolve(val); + next(); + return null; + }).catch(function (err) { + reject(err); + next(); + }); + }; + + this.maybePushConcurrencyQueue(run); } - sync = false - function done () { - --self._processing - if (self._processing <= 0) { - if (sync) { - process.nextTick(function () { - self._finish() - }) - } else { - self._finish() + maybePushConcurrencyQueue(run) { + if (this.runningCount < this.maxConcurrency) { + run(); + } else { + this.concurrencyQueue.push(run); + } + } + + shiftConcurrencyQueue() { + if (this.runningCount < this.maxConcurrency) { + const fn = this.concurrencyQueue.shift(); + if (fn) { + fn(); } } } } +exports.default = BlockingQueue; -Glob.prototype._finish = function () { - assert(this instanceof Glob) - if (this.aborted) - return - - if (this.realpath && !this._didRealpath) - return this._realpath() +/***/ }), +/* 85 */ +/***/ (function(module, exports) { - common.finish(this) - this.emit('end', this.found) -} +module.exports = function (exec) { + try { + return !!exec(); + } catch (e) { + return true; + } +}; -Glob.prototype._realpath = function () { - if (this._didRealpath) - return - this._didRealpath = true +/***/ }), +/* 86 */, +/* 87 */, +/* 88 */, +/* 89 */, +/* 90 */, +/* 91 */, +/* 92 */, +/* 93 */, +/* 94 */, +/* 95 */, +/* 96 */, +/* 97 */, +/* 98 */, +/* 99 */, +/* 100 */ +/***/ (function(module, exports, __webpack_require__) { - var n = this.matches.length - if (n === 0) - return this._finish() +// getting tag from 19.1.3.6 Object.prototype.toString() +var cof = __webpack_require__(47); +var TAG = __webpack_require__(13)('toStringTag'); +// ES3 wrong here +var ARG = cof(function () { return arguments; }()) == 'Arguments'; - var self = this - for (var i = 0; i < this.matches.length; i++) - this._realpathSet(i, next) +// fallback for IE11 Script Access Denied error +var tryGet = function (it, key) { + try { + return it[key]; + } catch (e) { /* empty */ } +}; - function next () { - if (--n === 0) - self._finish() - } -} +module.exports = function (it) { + var O, T, B; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T + // builtinTag case + : ARG ? cof(O) + // ES3 arguments fallback + : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; +}; -Glob.prototype._realpathSet = function (index, cb) { - var matchset = this.matches[index] - if (!matchset) - return cb() - var found = Object.keys(matchset) - var self = this - var n = found.length +/***/ }), +/* 101 */ +/***/ (function(module, exports) { - if (n === 0) - return cb() +// IE 8- don't enum bug keys +module.exports = ( + 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' +).split(','); - var set = this.matches[index] = Object.create(null) - found.forEach(function (p, i) { - // If there's a problem with the stat, then it means that - // one or more of the links in the realpath couldn't be - // resolved. just return the abs value in that case. - p = self._makeAbs(p) - rp.realpath(p, self.realpathCache, function (er, real) { - if (!er) - set[real] = true - else if (er.syscall === 'stat') - set[p] = true - else - self.emit('error', er) // srsly wtf right here - if (--n === 0) { - self.matches[index] = set - cb() - } - }) - }) -} +/***/ }), +/* 102 */ +/***/ (function(module, exports, __webpack_require__) { -Glob.prototype._mark = function (p) { - return common.mark(this, p) -} +var document = __webpack_require__(11).document; +module.exports = document && document.documentElement; -Glob.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) -} -Glob.prototype.abort = function () { - this.aborted = true - this.emit('abort') -} +/***/ }), +/* 103 */ +/***/ (function(module, exports, __webpack_require__) { -Glob.prototype.pause = function () { - if (!this.paused) { - this.paused = true - this.emit('pause') - } -} +"use strict"; -Glob.prototype.resume = function () { - if (this.paused) { - this.emit('resume') - this.paused = false - if (this._emitQueue.length) { - var eq = this._emitQueue.slice(0) - this._emitQueue.length = 0 - for (var i = 0; i < eq.length; i ++) { - var e = eq[i] - this._emitMatch(e[0], e[1]) - } - } - if (this._processQueue.length) { - var pq = this._processQueue.slice(0) - this._processQueue.length = 0 - for (var i = 0; i < pq.length; i ++) { - var p = pq[i] - this._processing-- - this._process(p[0], p[1], p[2], p[3]) - } - } - } -} - -Glob.prototype._process = function (pattern, index, inGlobStar, cb) { - assert(this instanceof Glob) - assert(typeof cb === 'function') +var LIBRARY = __webpack_require__(69); +var $export = __webpack_require__(41); +var redefine = __webpack_require__(197); +var hide = __webpack_require__(31); +var Iterators = __webpack_require__(35); +var $iterCreate = __webpack_require__(188); +var setToStringTag = __webpack_require__(71); +var getPrototypeOf = __webpack_require__(194); +var ITERATOR = __webpack_require__(13)('iterator'); +var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` +var FF_ITERATOR = '@@iterator'; +var KEYS = 'keys'; +var VALUES = 'values'; - if (this.aborted) - return +var returnThis = function () { return this; }; - this._processing++ - if (this.paused) { - this._processQueue.push([pattern, index, inGlobStar, cb]) - return +module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { + $iterCreate(Constructor, NAME, next); + var getMethod = function (kind) { + if (!BUGGY && kind in proto) return proto[kind]; + switch (kind) { + case KEYS: return function keys() { return new Constructor(this, kind); }; + case VALUES: return function values() { return new Constructor(this, kind); }; + } return function entries() { return new Constructor(this, kind); }; + }; + var TAG = NAME + ' Iterator'; + var DEF_VALUES = DEFAULT == VALUES; + var VALUES_BUG = false; + var proto = Base.prototype; + var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; + var $default = $native || getMethod(DEFAULT); + var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; + var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; + var methods, key, IteratorPrototype; + // Fix native + if ($anyNative) { + IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); + if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { + // Set @@toStringTag to native iterators + setToStringTag(IteratorPrototype, TAG, true); + // fix for some old engines + if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis); + } } - - //console.error('PROCESS %d', this._processing, pattern) - - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === 'string') { - n ++ + // fix Array#{values, @@iterator}.name in V8 / FF + if (DEF_VALUES && $native && $native.name !== VALUES) { + VALUES_BUG = true; + $default = function values() { return $native.call(this); }; } - // now n is the index of the first one that is *not* a string. + // Define iterator + if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { + hide(proto, ITERATOR, $default); + } + // Plug for library + Iterators[NAME] = $default; + Iterators[TAG] = returnThis; + if (DEFAULT) { + methods = { + values: DEF_VALUES ? $default : getMethod(VALUES), + keys: IS_SET ? $default : getMethod(KEYS), + entries: $entries + }; + if (FORCED) for (key in methods) { + if (!(key in proto)) redefine(proto, key, methods[key]); + } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); + } + return methods; +}; - // see if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - this._processSimple(pattern.join('/'), index, cb) - return - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break +/***/ }), +/* 104 */ +/***/ (function(module, exports) { - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's 'absolute' like /foo/bar, - // or 'relative' like '../baz' - prefix = pattern.slice(0, n).join('/') - break +module.exports = function (exec) { + try { + return { e: false, v: exec() }; + } catch (e) { + return { e: true, v: e }; } +}; - var remain = pattern.slice(n) - // get the list of entries. - var read - if (prefix === null) - read = '.' - else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { - if (!prefix || !isAbsolute(prefix)) - prefix = '/' + prefix - read = prefix - } else - read = prefix +/***/ }), +/* 105 */ +/***/ (function(module, exports, __webpack_require__) { - var abs = this._makeAbs(read) +var anObject = __webpack_require__(27); +var isObject = __webpack_require__(34); +var newPromiseCapability = __webpack_require__(70); - //if ignored, skip _processing - if (childrenIgnored(this, read)) - return cb() +module.exports = function (C, x) { + anObject(C); + if (isObject(x) && x.constructor === C) return x; + var promiseCapability = newPromiseCapability.f(C); + var resolve = promiseCapability.resolve; + resolve(x); + return promiseCapability.promise; +}; - var isGlobStar = remain[0] === minimatch.GLOBSTAR - if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) - else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) -} -Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) - }) -} +/***/ }), +/* 106 */ +/***/ (function(module, exports) { -Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { +module.exports = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; +}; - // if the abs isn't a dir, then nothing can match! - if (!entries) - return cb() - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = remain[0] - var negate = !!this.minimatch.negate - var rawGlob = pn._glob - var dotOk = this.dot || rawGlob.charAt(0) === '.' +/***/ }), +/* 107 */ +/***/ (function(module, exports, __webpack_require__) { - var matchedEntries = [] - for (var i = 0; i < entries.length; i++) { - var e = entries[i] - if (e.charAt(0) !== '.' || dotOk) { - var m - if (negate && !prefix) { - m = !e.match(pn) - } else { - m = e.match(pn) - } - if (m) - matchedEntries.push(e) - } - } +var core = __webpack_require__(23); +var global = __webpack_require__(11); +var SHARED = '__core-js_shared__'; +var store = global[SHARED] || (global[SHARED] = {}); - //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) +(module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); +})('versions', []).push({ + version: core.version, + mode: __webpack_require__(69) ? 'pure' : 'global', + copyright: '© 2018 Denis Pushkarev (zloirock.ru)' +}); - var len = matchedEntries.length - // If there are no matched entries, then nothing matches. - if (len === 0) - return cb() - // if this is the last remaining pattern bit, then no need for - // an additional stat *unless* the user has specified mark or - // stat explicitly. We know they exist, since readdir returned - // them. +/***/ }), +/* 108 */ +/***/ (function(module, exports, __webpack_require__) { - if (remain.length === 1 && !this.mark && !this.stat) { - if (!this.matches[index]) - this.matches[index] = Object.create(null) +// 7.3.20 SpeciesConstructor(O, defaultConstructor) +var anObject = __webpack_require__(27); +var aFunction = __webpack_require__(46); +var SPECIES = __webpack_require__(13)('species'); +module.exports = function (O, D) { + var C = anObject(O).constructor; + var S; + return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); +}; - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } - if (e.charAt(0) === '/' && !this.nomount) { - e = path.join(this.root, e) - } - this._emitMatch(index, e) - } - // This was the last one, and no stats were needed - return cb() - } +/***/ }), +/* 109 */ +/***/ (function(module, exports, __webpack_require__) { - // now test all matched entries as stand-ins for that part - // of the pattern. - remain.shift() - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - var newPattern - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } - this._process([e].concat(remain), index, inGlobStar, cb) +var ctx = __webpack_require__(48); +var invoke = __webpack_require__(185); +var html = __webpack_require__(102); +var cel = __webpack_require__(68); +var global = __webpack_require__(11); +var process = global.process; +var setTask = global.setImmediate; +var clearTask = global.clearImmediate; +var MessageChannel = global.MessageChannel; +var Dispatch = global.Dispatch; +var counter = 0; +var queue = {}; +var ONREADYSTATECHANGE = 'onreadystatechange'; +var defer, channel, port; +var run = function () { + var id = +this; + // eslint-disable-next-line no-prototype-builtins + if (queue.hasOwnProperty(id)) { + var fn = queue[id]; + delete queue[id]; + fn(); + } +}; +var listener = function (event) { + run.call(event.data); +}; +// Node.js 0.9+ & IE10+ has setImmediate, otherwise: +if (!setTask || !clearTask) { + setTask = function setImmediate(fn) { + var args = []; + var i = 1; + while (arguments.length > i) args.push(arguments[i++]); + queue[++counter] = function () { + // eslint-disable-next-line no-new-func + invoke(typeof fn == 'function' ? fn : Function(fn), args); + }; + defer(counter); + return counter; + }; + clearTask = function clearImmediate(id) { + delete queue[id]; + }; + // Node.js 0.8- + if (__webpack_require__(47)(process) == 'process') { + defer = function (id) { + process.nextTick(ctx(run, id, 1)); + }; + // Sphere (JS game engine) Dispatch API + } else if (Dispatch && Dispatch.now) { + defer = function (id) { + Dispatch.now(ctx(run, id, 1)); + }; + // Browsers with MessageChannel, includes WebWorkers + } else if (MessageChannel) { + channel = new MessageChannel(); + port = channel.port2; + channel.port1.onmessage = listener; + defer = ctx(port.postMessage, port, 1); + // Browsers with postMessage, skip WebWorkers + // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' + } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) { + defer = function (id) { + global.postMessage(id + '', '*'); + }; + global.addEventListener('message', listener, false); + // IE8- + } else if (ONREADYSTATECHANGE in cel('script')) { + defer = function (id) { + html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () { + html.removeChild(this); + run.call(id); + }; + }; + // Rest old browsers + } else { + defer = function (id) { + setTimeout(ctx(run, id, 1), 0); + }; } - cb() } +module.exports = { + set: setTask, + clear: clearTask +}; -Glob.prototype._emitMatch = function (index, e) { - if (this.aborted) - return - if (isIgnored(this, e)) - return +/***/ }), +/* 110 */ +/***/ (function(module, exports, __webpack_require__) { - if (this.paused) { - this._emitQueue.push([index, e]) - return - } +// 7.1.15 ToLength +var toInteger = __webpack_require__(73); +var min = Math.min; +module.exports = function (it) { + return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 +}; - var abs = isAbsolute(e) ? e : this._makeAbs(e) - if (this.mark) - e = this._mark(e) +/***/ }), +/* 111 */ +/***/ (function(module, exports) { - if (this.absolute) - e = abs +var id = 0; +var px = Math.random(); +module.exports = function (key) { + return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); +}; - if (this.matches[index][e]) - return - if (this.nodir) { - var c = this.cache[abs] - if (c === 'DIR' || Array.isArray(c)) - return - } +/***/ }), +/* 112 */ +/***/ (function(module, exports, __webpack_require__) { - this.matches[index][e] = true - var st = this.statCache[abs] - if (st) - this.emit('stat', e, st) +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ - this.emit('match', e) -} +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = __webpack_require__(229); -Glob.prototype._readdirInGlobStar = function (abs, cb) { - if (this.aborted) - return +/** + * Active `debug` instances. + */ +exports.instances = []; - // follow all symlinked directories forever - // just proceed as if this is a non-globstar situation - if (this.follow) - return this._readdir(abs, false, cb) +/** + * The currently active debug mode names, and names to skip. + */ - var lstatkey = 'lstat\0' + abs - var self = this - var lstatcb = inflight(lstatkey, lstatcb_) +exports.names = []; +exports.skips = []; - if (lstatcb) - fs.lstat(abs, lstatcb) +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ - function lstatcb_ (er, lstat) { - if (er && er.code === 'ENOENT') - return cb() +exports.formatters = {}; - var isSym = lstat && lstat.isSymbolicLink() - self.symlinks[abs] = isSym +/** + * Select a color. + * @param {String} namespace + * @return {Number} + * @api private + */ - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) { - self.cache[abs] = 'FILE' - cb() - } else - self._readdir(abs, false, cb) +function selectColor(namespace) { + var hash = 0, i; + + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer } + + return exports.colors[Math.abs(hash) % exports.colors.length]; } -Glob.prototype._readdir = function (abs, inGlobStar, cb) { - if (this.aborted) - return +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ - cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) - if (!cb) - return +function createDebug(namespace) { - //console.error('RD %j %j', +inGlobStar, abs) - if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs, cb) + var prevTime; - if (ownProp(this.cache, abs)) { - var c = this.cache[abs] - if (!c || c === 'FILE') - return cb() + function debug() { + // disabled? + if (!debug.enabled) return; - if (Array.isArray(c)) - return cb(null, c) - } + var self = debug; - var self = this - fs.readdir(abs, readdirCb(this, abs, cb)) -} + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; -function readdirCb (self, abs, cb) { - return function (er, entries) { - if (er) - self._readdirError(abs, er, cb) - else - self._readdirEntries(abs, entries, cb) - } -} + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } -Glob.prototype._readdirEntries = function (abs, entries, cb) { - if (this.aborted) - return + args[0] = exports.coerce(args[0]); - // if we haven't asked to stat everything, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. - if (!this.mark && !this.stat) { - for (var i = 0; i < entries.length; i ++) { - var e = entries[i] - if (abs === '/') - e = abs + e - else - e = abs + '/' + e - this.cache[e] = true + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); } - } - - this.cache[abs] = entries - return cb(null, entries) -} -Glob.prototype._readdirError = function (f, er, cb) { - if (this.aborted) - return + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); - // handle errors, and cache the information - switch (er.code) { - case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 - case 'ENOTDIR': // totally normal. means it *does* exist. - var abs = this._makeAbs(f) - this.cache[abs] = 'FILE' - if (abs === this.cwdAbs) { - var error = new Error(er.code + ' invalid cwd ' + this.cwd) - error.path = this.cwd - error.code = er.code - this.emit('error', error) - this.abort() + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; } - break + return match; + }); - case 'ENOENT': // not terribly unusual - case 'ELOOP': - case 'ENAMETOOLONG': - case 'UNKNOWN': - this.cache[this._makeAbs(f)] = false - break + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); - default: // some unusual error. Treat as failure. - this.cache[this._makeAbs(f)] = false - if (this.strict) { - this.emit('error', er) - // If the error is handled, then we abort - // if not, we threw out of here - this.abort() - } - if (!this.silent) - console.error('glob error', er) - break + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); } - return cb() -} - -Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) - }) -} - + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; -Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { - //console.error('pgs2', prefix, remain[0], entries) + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } - // no entries means not a dir, so it can never have matches - // foo.txt/** doesn't match foo.txt - if (!entries) - return cb() + exports.instances.push(debug); - // test without the globstar, and with every child both below - // and replacing the globstar. - var remainWithoutGlobStar = remain.slice(1) - var gspref = prefix ? [ prefix ] : [] - var noGlobStar = gspref.concat(remainWithoutGlobStar) + return debug; +} - // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false, cb) +function destroy () { + var index = exports.instances.indexOf(this); + if (index !== -1) { + exports.instances.splice(index, 1); + return true; + } else { + return false; + } +} - var isSym = this.symlinks[abs] - var len = entries.length +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ - // If it's a symlink, and we're in a globstar, then stop - if (isSym && inGlobStar) - return cb() +function enable(namespaces) { + exports.save(namespaces); - for (var i = 0; i < len; i++) { - var e = entries[i] - if (e.charAt(0) === '.' && !this.dot) - continue + exports.names = []; + exports.skips = []; - // these two cases enter the inGlobStar state - var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true, cb) + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; - var below = gspref.concat(entries[i], remain) - this._process(below, index, true, cb) + for (i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } } - cb() -} - -Glob.prototype._processSimple = function (prefix, index, cb) { - // XXX review this. Shouldn't it be doing the mounting etc - // before doing stat? kinda weird? - var self = this - this._stat(prefix, function (er, exists) { - self._processSimple2(prefix, index, er, exists, cb) - }) + for (i = 0; i < exports.instances.length; i++) { + var instance = exports.instances[i]; + instance.enabled = exports.enabled(instance.namespace); + } } -Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { - //console.error('ps2', prefix, exists) +/** + * Disable debug output. + * + * @api public + */ - if (!this.matches[index]) - this.matches[index] = Object.create(null) +function disable() { + exports.enable(''); +} - // If it doesn't exist, then just mark the lack of results - if (!exists) - return cb() +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ - if (prefix && isAbsolute(prefix) && !this.nomount) { - var trail = /[\/\\]$/.test(prefix) - if (prefix.charAt(0) === '/') { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - if (trail) - prefix += '/' +function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; } } + return false; +} - if (process.platform === 'win32') - prefix = prefix.replace(/\\/g, '/') +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ - // Mark this as a match - this._emitMatch(index, prefix) - cb() +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; } -// Returns either 'DIR', 'FILE', or false -Glob.prototype._stat = function (f, cb) { - var abs = this._makeAbs(f) - var needDir = f.slice(-1) === '/' - if (f.length > this.maxLength) - return cb() +/***/ }), +/* 113 */, +/* 114 */ +/***/ (function(module, exports, __webpack_require__) { - if (!this.stat && ownProp(this.cache, abs)) { - var c = this.cache[abs] +module.exports = realpath +realpath.realpath = realpath +realpath.sync = realpathSync +realpath.realpathSync = realpathSync +realpath.monkeypatch = monkeypatch +realpath.unmonkeypatch = unmonkeypatch - if (Array.isArray(c)) - c = 'DIR' +var fs = __webpack_require__(3) +var origRealpath = fs.realpath +var origRealpathSync = fs.realpathSync - // It exists, but maybe not how we need it - if (!needDir || c === 'DIR') - return cb(null, c) +var version = process.version +var ok = /^v[0-5]\./.test(version) +var old = __webpack_require__(217) - if (needDir && c === 'FILE') - return cb() +function newError (er) { + return er && er.syscall === 'realpath' && ( + er.code === 'ELOOP' || + er.code === 'ENOMEM' || + er.code === 'ENAMETOOLONG' + ) +} - // otherwise we have to stat, because maybe c=true - // if we know it exists, but not what it is. +function realpath (p, cache, cb) { + if (ok) { + return origRealpath(p, cache, cb) } - var exists - var stat = this.statCache[abs] - if (stat !== undefined) { - if (stat === false) - return cb(null, stat) - else { - var type = stat.isDirectory() ? 'DIR' : 'FILE' - if (needDir && type === 'FILE') - return cb() - else - return cb(null, type, stat) - } + if (typeof cache === 'function') { + cb = cache + cache = null } - - var self = this - var statcb = inflight('stat\0' + abs, lstatcb_) - if (statcb) - fs.lstat(abs, statcb) - - function lstatcb_ (er, lstat) { - if (lstat && lstat.isSymbolicLink()) { - // If it's a symlink, then treat it as the target, unless - // the target does not exist, then treat it as a file. - return fs.stat(abs, function (er, stat) { - if (er) - self._stat2(f, abs, null, lstat, cb) - else - self._stat2(f, abs, er, stat, cb) - }) + origRealpath(p, cache, function (er, result) { + if (newError(er)) { + old.realpath(p, cache, cb) } else { - self._stat2(f, abs, er, lstat, cb) + cb(er, result) } - } + }) } -Glob.prototype._stat2 = function (f, abs, er, stat, cb) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return cb() +function realpathSync (p, cache) { + if (ok) { + return origRealpathSync(p, cache) } - var needDir = f.slice(-1) === '/' - this.statCache[abs] = stat - - if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) - return cb(null, false, stat) - - var c = true - if (stat) - c = stat.isDirectory() ? 'DIR' : 'FILE' - this.cache[abs] = this.cache[abs] || c + try { + return origRealpathSync(p, cache) + } catch (er) { + if (newError(er)) { + return old.realpathSync(p, cache) + } else { + throw er + } + } +} - if (needDir && c === 'FILE') - return cb() +function monkeypatch () { + fs.realpath = realpath + fs.realpathSync = realpathSync +} - return cb(null, c, stat) +function unmonkeypatch () { + fs.realpath = origRealpath + fs.realpathSync = origRealpathSync } /***/ }), -/* 76 */ +/* 115 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +exports.alphasort = alphasort +exports.alphasorti = alphasorti +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored -function posix(path) { - return path.charAt(0) === '/'; +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) } -function win32(path) { - // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 - var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; - var result = splitDeviceRe.exec(path); - var device = result[1] || ''; - var isUnc = Boolean(device && device.charAt(1) !== ':'); +var path = __webpack_require__(0) +var minimatch = __webpack_require__(60) +var isAbsolute = __webpack_require__(76) +var Minimatch = minimatch.Minimatch - // UNC paths are always absolute - return Boolean(result[2] || isUnc); +function alphasorti (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()) } -module.exports = process.platform === 'win32' ? win32 : posix; -module.exports.posix = posix; -module.exports.win32 = win32; - +function alphasort (a, b) { + return a.localeCompare(b) +} -/***/ }), -/* 77 */, -/* 78 */, -/* 79 */ -/***/ (function(module, exports) { +function setupIgnores (self, options) { + self.ignore = options.ignore || [] -module.exports = __webpack_require__(121); + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] -/***/ }), -/* 80 */, -/* 81 */ -/***/ (function(module, exports, __webpack_require__) { + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} -"use strict"; +// ignore patterns are always in dot:true mode. +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern, { dot: true }) + } + return { + matcher: new Minimatch(pattern, { dot: true }), + gmatcher: gmatcher + } +} -Object.defineProperty(exports, "__esModule", { - value: true -}); +function setopts (self, pattern, options) { + if (!options) + options = {} -exports.default = function (str, fileLoc = 'lockfile') { - str = (0, (_stripBom || _load_stripBom()).default)(str); - return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : { type: 'success', object: parse(str, fileLoc) }; -}; + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } -var _util; + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + self.absolute = !!options.absolute -function _load_util() { - return _util = _interopRequireDefault(__webpack_require__(2)); -} + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) -var _invariant; + setupIgnores(self, options) -function _load_invariant() { - return _invariant = _interopRequireDefault(__webpack_require__(7)); -} + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = path.resolve(options.cwd) + self.changedCwd = self.cwd !== cwd + } -var _stripBom; + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") -function _load_stripBom() { - return _stripBom = _interopRequireDefault(__webpack_require__(122)); -} + // TODO: is an absolute `cwd` supposed to be resolved against `root`? + // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') + self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) + if (process.platform === "win32") + self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") + self.nomount = !!options.nomount -var _constants; + // disable comments and negation in Minimatch. + // Note that they are not supported in Glob itself anyway. + options.nonegate = true + options.nocomment = true -function _load_constants() { - return _constants = __webpack_require__(6); + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options } -var _errors; +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) -function _load_errors() { - return _errors = __webpack_require__(4); -} + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } -var _map; + if (!nou) + all = Object.keys(all) -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); + if (!self.nosort) + all = all.sort(self.nocase ? alphasorti : alphasort) + + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + var notDir = !(/\/$/.test(e)) + var c = self.cache[e] || self.cache[makeAbs(self, e)] + if (notDir && c) + notDir = c !== 'DIR' && !Array.isArray(c) + return notDir + }) + } + } + + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) + + self.found = all } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' -/* eslint quotes: 0 */ + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) -const VERSION_REGEX = /^yarn lockfile v(\d+)$/; + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } -const TOKEN_TYPES = { - boolean: 'BOOLEAN', - string: 'STRING', - identifier: 'IDENTIFIER', - eof: 'EOF', - colon: 'COLON', - newline: 'NEWLINE', - comment: 'COMMENT', - indent: 'INDENT', - invalid: 'INVALID', - number: 'NUMBER', - comma: 'COMMA' -}; + return m +} -const VALID_PROP_VALUE_TOKENS = [TOKEN_TYPES.boolean, TOKEN_TYPES.string, TOKEN_TYPES.number]; +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } -function isValidPropValueToken(token) { - return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0; + if (process.platform === 'win32') + abs = abs.replace(/\\/g, '/') + + return abs } -function* tokenise(input) { - let lastNewline = false; - let line = 1; - let col = 0; - function buildToken(type, value) { - return { line, col, type, value }; - } +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false - while (input.length) { - let chop = 0; + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) +} - if (input[0] === '\n' || input[0] === '\r') { - chop++; - // If this is a \r\n line, ignore both chars but only add one new line - if (input[1] === '\n') { - chop++; - } - line++; - col = 0; - yield buildToken(TOKEN_TYPES.newline); - } else if (input[0] === '#') { - chop++; +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false - let val = ''; - while (input[chop] !== '\n') { - val += input[chop]; - chop++; - } - yield buildToken(TOKEN_TYPES.comment, val); - } else if (input[0] === ' ') { - if (lastNewline) { - let indent = ''; - for (let i = 0; input[i] === ' '; i++) { - indent += input[i]; - } + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} - if (indent.length % 2) { - throw new TypeError('Invalid number of spaces'); - } else { - chop = indent.length; - yield buildToken(TOKEN_TYPES.indent, indent.length / 2); - } - } else { - chop++; - } - } else if (input[0] === '"') { - let val = ''; - for (let i = 0;; i++) { - const currentChar = input[i]; - val += currentChar; +/***/ }), +/* 116 */ +/***/ (function(module, exports, __webpack_require__) { - if (i > 0 && currentChar === '"') { - const isEscaped = input[i - 1] === '\\' && input[i - 2] !== '\\'; - if (!isEscaped) { - break; - } - } - } +var path = __webpack_require__(0); +var fs = __webpack_require__(3); +var _0777 = parseInt('0777', 8); - chop = val.length; +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; - try { - yield buildToken(TOKEN_TYPES.string, JSON.parse(val)); - } catch (err) { - if (err instanceof SyntaxError) { - yield buildToken(TOKEN_TYPES.invalid); - } else { - throw err; +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); } - } - } else if (/^[0-9]/.test(input)) { - let val = ''; - for (let i = 0; /^[0-9]$/.test(input[i]); i++) { - val += input[i]; - } - chop = val.length; + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; - yield buildToken(TOKEN_TYPES.number, +val); - } else if (/^true/.test(input)) { - yield buildToken(TOKEN_TYPES.boolean, true); - chop = 4; - } else if (/^false/.test(input)) { - yield buildToken(TOKEN_TYPES.boolean, false); - chop = 5; - } else if (input[0] === ':') { - yield buildToken(TOKEN_TYPES.colon); - chop++; - } else if (input[0] === ',') { - yield buildToken(TOKEN_TYPES.comma); - chop++; - } else if (/^[a-zA-Z\/-]/g.test(input)) { - let name = ''; - for (let i = 0; i < input.length; i++) { - const char = input[i]; - if (char === ':' || char === ' ' || char === '\n' || char === '\r' || char === ',') { - break; - } else { - name += char; + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; } - } - chop = name.length; + }); +} - yield buildToken(TOKEN_TYPES.string, name); - } else { - yield buildToken(TOKEN_TYPES.invalid); +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); } + if (!made) made = null; - if (!chop) { - // will trigger infinite recursion - yield buildToken(TOKEN_TYPES.invalid); + p = path.resolve(p); + + try { + xfs.mkdirSync(p, mode); + made = made || p; } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; - col += chop; - lastNewline = input[0] === '\n' || input[0] === '\r' && input[1] === '\n'; - input = input.slice(chop); - } + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } - yield buildToken(TOKEN_TYPES.eof); -} + return made; +}; -class Parser { - constructor(input, fileLoc = 'lockfile') { - this.comments = []; - this.tokens = tokenise(input); - this.fileLoc = fileLoc; - } - onComment(token) { - const value = token.value; - (0, (_invariant || _load_invariant()).default)(typeof value === 'string', 'expected token value to be a string'); +/***/ }), +/* 117 */, +/* 118 */, +/* 119 */, +/* 120 */, +/* 121 */, +/* 122 */ +/***/ (function(module, exports, __webpack_require__) { - const comment = value.trim(); +"use strict"; - const versionMatch = comment.match(VERSION_REGEX); - if (versionMatch) { - const version = +versionMatch[1]; - if (version > (_constants || _load_constants()).LOCKFILE_VERSION) { - throw new (_errors || _load_errors()).MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports ` + `versions up to ${(_constants || _load_constants()).LOCKFILE_VERSION}. Run \`$ yarn self-update\` to upgrade to the latest version.`); - } - } +module.exports = x => { + if (typeof x !== 'string') { + throw new TypeError('Expected a string, got ' + typeof x); + } - this.comments.push(comment); - } + // Catches EFBBBF (UTF-8 BOM) because the buffer-to-string + // conversion translates it to FEFF (UTF-16 BOM) + if (x.charCodeAt(0) === 0xFEFF) { + return x.slice(1); + } - next() { - const item = this.tokens.next(); - (0, (_invariant || _load_invariant()).default)(item, 'expected a token'); + return x; +}; - const done = item.done, - value = item.value; - if (done || !value) { - throw new Error('No more tokens'); - } else if (value.type === TOKEN_TYPES.comment) { - this.onComment(value); - return this.next(); - } else { - return this.token = value; - } - } +/***/ }), +/* 123 */ +/***/ (function(module, exports) { - unexpected(msg = 'Unexpected token') { - throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`); - } +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) - expect(tokType) { - if (this.token.type === tokType) { - this.next(); - } else { - this.unexpected(); - } - } + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') - eat(tokType) { - if (this.token.type === tokType) { - this.next(); - return true; - } else { - return false; - } - } + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) - parse(indent = 0) { - const obj = (0, (_map || _load_map()).default)(); + return wrapper - while (true) { - const propToken = this.token; + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} - if (propToken.type === TOKEN_TYPES.newline) { - const nextToken = this.next(); - if (!indent) { - // if we have 0 indentation then the next token doesn't matter - continue; - } - if (nextToken.type !== TOKEN_TYPES.indent) { - // if we have no indentation after a newline then we've gone down a level - break; - } +/***/ }), +/* 124 */, +/* 125 */, +/* 126 */, +/* 127 */, +/* 128 */, +/* 129 */, +/* 130 */, +/* 131 */ +/***/ (function(module, exports, __webpack_require__) { - if (nextToken.value === indent) { - // all is good, the indent is on our level - this.next(); - } else { - // the indentation is less than our level - break; - } - } else if (propToken.type === TOKEN_TYPES.indent) { - if (propToken.value === indent) { - this.next(); - } else { - break; - } - } else if (propToken.type === TOKEN_TYPES.eof) { - break; - } else if (propToken.type === TOKEN_TYPES.string) { - // property key - const key = propToken.value; - (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); +// fallback for non-array-like ES3 and non-enumerable old V8 strings +var cof = __webpack_require__(47); +// eslint-disable-next-line no-prototype-builtins +module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { + return cof(it) == 'String' ? it.split('') : Object(it); +}; - const keys = [key]; - this.next(); - // support multiple keys - while (this.token.type === TOKEN_TYPES.comma) { - this.next(); // skip comma +/***/ }), +/* 132 */ +/***/ (function(module, exports, __webpack_require__) { - const keyToken = this.token; - if (keyToken.type !== TOKEN_TYPES.string) { - this.unexpected('Expected string'); - } +// 19.1.2.14 / 15.2.3.14 Object.keys(O) +var $keys = __webpack_require__(195); +var enumBugKeys = __webpack_require__(101); - const key = keyToken.value; - (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); - keys.push(key); - this.next(); - } +module.exports = Object.keys || function keys(O) { + return $keys(O, enumBugKeys); +}; - const valToken = this.token; - if (valToken.type === TOKEN_TYPES.colon) { - // object - this.next(); +/***/ }), +/* 133 */ +/***/ (function(module, exports, __webpack_require__) { - // parse object - const val = this.parse(indent + 1); +// 7.1.13 ToObject(argument) +var defined = __webpack_require__(67); +module.exports = function (it) { + return Object(defined(it)); +}; - for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } +/***/ }), +/* 134 */, +/* 135 */, +/* 136 */, +/* 137 */, +/* 138 */, +/* 139 */, +/* 140 */, +/* 141 */, +/* 142 */, +/* 143 */, +/* 144 */, +/* 145 */ +/***/ (function(module, exports) { - const key = _ref; +module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.10.0-0","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^2.2.4","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^3.0.1","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.3","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.24","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.10.0","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^3.9.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","gulp-util":"^3.0.7","gulp-watch":"^5.0.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} - obj[key] = val; - } +/***/ }), +/* 146 */, +/* 147 */, +/* 148 */, +/* 149 */, +/* 150 */ +/***/ (function(module, exports, __webpack_require__) { - if (indent && this.token.type !== TOKEN_TYPES.indent) { - break; - } - } else if (isValidPropValueToken(valToken)) { - // plain value - for (var _iterator2 = keys, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; +"use strict"; - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - const key = _ref2; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = stringify; - obj[key] = valToken.value; - } +var _misc; - this.next(); - } else { - this.unexpected('Invalid value type'); - } - } else { - this.unexpected(`Unknown token: ${(_util || _load_util()).default.inspect(propToken)}`); - } - } +function _load_misc() { + return _misc = __webpack_require__(12); +} - return obj; +var _constants; + +function _load_constants() { + return _constants = __webpack_require__(6); +} + +var _package; + +function _load_package() { + return _package = __webpack_require__(145); +} + +const NODE_VERSION = process.version; + +function shouldWrapKey(str) { + return str.indexOf('true') === 0 || str.indexOf('false') === 0 || /[:\s\n\\",\[\]]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str); +} + +function maybeWrap(str) { + if (typeof str === 'boolean' || typeof str === 'number' || shouldWrapKey(str)) { + return JSON.stringify(str); + } else { + return str; } } -const MERGE_CONFLICT_ANCESTOR = '|||||||'; -const MERGE_CONFLICT_END = '>>>>>>>'; -const MERGE_CONFLICT_SEP = '======='; -const MERGE_CONFLICT_START = '<<<<<<<'; +const priorities = { + name: 1, + version: 2, + uid: 3, + resolved: 4, + integrity: 5, + registry: 6, + dependencies: 7 +}; -/** - * Extract the two versions of the lockfile from a merge conflict. - */ -function extractConflictVariants(str) { - const variants = [[], []]; - const lines = str.split(/\r?\n/g); - let skip = false; +function priorityThenAlphaSort(a, b) { + if (priorities[a] || priorities[b]) { + return (priorities[a] || 100) > (priorities[b] || 100) ? 1 : -1; + } else { + return (0, (_misc || _load_misc()).sortAlpha)(a, b); + } +} - while (lines.length) { - const line = lines.shift(); - if (line.startsWith(MERGE_CONFLICT_START)) { - // get the first variant - while (lines.length) { - const conflictLine = lines.shift(); - if (conflictLine === MERGE_CONFLICT_SEP) { - skip = false; - break; - } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) { - skip = true; - continue; - } else { - variants[0].push(conflictLine); - } - } +function _stringify(obj, options) { + if (typeof obj !== 'object') { + throw new TypeError(); + } - // get the second variant - while (lines.length) { - const conflictLine = lines.shift(); - if (conflictLine.startsWith(MERGE_CONFLICT_END)) { - break; - } else { - variants[1].push(conflictLine); + const indent = options.indent; + const lines = []; + + // Sorting order needs to be consistent between runs, we run native sort by name because there are no + // problems with it being unstable because there are no to keys the same + // However priorities can be duplicated and native sort can shuffle things from run to run + const keys = Object.keys(obj).sort(priorityThenAlphaSort); + + let addedKeys = []; + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const val = obj[key]; + if (val == null || addedKeys.indexOf(key) >= 0) { + continue; + } + + const valKeys = [key]; + + // get all keys that have the same value equality, we only want this for objects + if (typeof val === 'object') { + for (let j = i + 1; j < keys.length; j++) { + const key = keys[j]; + if (val === obj[key]) { + valKeys.push(key); } } + } + + const keyLine = valKeys.sort((_misc || _load_misc()).sortAlpha).map(maybeWrap).join(', '); + + if (typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number') { + lines.push(`${keyLine} ${maybeWrap(val)}`); + } else if (typeof val === 'object') { + lines.push(`${keyLine}:\n${_stringify(val, { indent: indent + ' ' })}` + (options.topLevel ? '\n' : '')); } else { - variants[0].push(line); - variants[1].push(line); + throw new TypeError(); } - } - return [variants[0].join('\n'), variants[1].join('\n')]; -} + addedKeys = addedKeys.concat(valKeys); + } -/** - * Check if a lockfile has merge conflicts. - */ -function hasMergeConflicts(str) { - return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END); + return indent + lines.join(`\n${indent}`); } -/** - * Parse the lockfile. - */ -function parse(str, fileLoc) { - const parser = new Parser(str, fileLoc); - parser.next(); - return parser.parse(); -} +function stringify(obj, noHeader, enableVersions) { + const val = _stringify(obj, { + indent: '', + topLevel: true + }); + if (noHeader) { + return val; + } -/** - * Parse and merge the two variants in a conflicted lockfile. - */ -function parseWithConflict(str, fileLoc) { - const variants = extractConflictVariants(str); - try { - return { type: 'merge', object: Object.assign({}, parse(variants[0], fileLoc), parse(variants[1], fileLoc)) }; - } catch (err) { - if (err instanceof SyntaxError) { - return { type: 'conflict', object: {} }; - } else { - throw err; - } + const lines = []; + lines.push('# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.'); + lines.push(`# yarn lockfile v${(_constants || _load_constants()).LOCKFILE_VERSION}`); + if (enableVersions) { + lines.push(`# yarn v${(_package || _load_package()).version}`); + lines.push(`# node ${NODE_VERSION}`); } + lines.push('\n'); + lines.push(val); + + return lines.join('\n'); } /***/ }), -/* 82 */, -/* 83 */, -/* 84 */ +/* 151 */, +/* 152 */, +/* 153 */, +/* 154 */, +/* 155 */, +/* 156 */, +/* 157 */, +/* 158 */, +/* 159 */, +/* 160 */, +/* 161 */, +/* 162 */, +/* 163 */, +/* 164 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -34461,15466 +34719,12937 @@ function parseWithConflict(str, fileLoc) { Object.defineProperty(exports, "__esModule", { value: true }); +exports.fileDatesEqual = exports.copyFile = exports.unlink = undefined; -var _map; +var _asyncToGenerator2; -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const debug = __webpack_require__(212)('yarn'); +// We want to preserve file timestamps when copying a file, since yarn uses them to decide if a file has +// changed compared to the cache. +// There are some OS specific cases here: +// * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. +// * On windows, you must open a file with write permissions to call `fs.futimes`. +// * On OSX you can open with read permissions and still call `fs.futimes`. +let fixTimes = (() => { + var _ref3 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (fd, dest, data) { + const doOpen = fd === undefined; + let openfd = fd ? fd : -1; -class BlockingQueue { - constructor(alias, maxConcurrency = Infinity) { - this.concurrencyQueue = []; - this.maxConcurrency = maxConcurrency; - this.runningCount = 0; - this.warnedStuck = false; - this.alias = alias; - this.first = true; + if (disableTimestampCorrection === undefined) { + // if timestamps match already, no correction is needed. + // the need to correct timestamps varies based on OS and node versions. + const destStat = yield lstat(dest); + disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); + } - this.running = (0, (_map || _load_map()).default)(); - this.queue = (0, (_map || _load_map()).default)(); + if (disableTimestampCorrection) { + return; + } - this.stuckTick = this.stuckTick.bind(this); - } + if (doOpen) { + try { + openfd = yield open(dest, 'a', data.mode); + } catch (er) { + // file is likely read-only + try { + openfd = yield open(dest, 'r', data.mode); + } catch (err) { + // We can't even open this file for reading. + return; + } + } + } - stillActive() { - if (this.stuckTimer) { - clearTimeout(this.stuckTimer); + try { + if (openfd) { + yield futimes(openfd, data.atime, data.mtime); + } + } catch (er) { + // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. + // In this case we can just return. The incorrect timestamp will just cause that file to be recopied + // on subsequent installs, which will effect yarn performance but not break anything. + } finally { + if (doOpen && openfd) { + yield close(openfd); + } } + }); - this.stuckTimer = setTimeout(this.stuckTick, 5000); + return function fixTimes(_x7, _x8, _x9) { + return _ref3.apply(this, arguments); + }; +})(); - // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 - // $FlowFixMe: Node's setInterval returns a Timeout, not a Number - this.stuckTimer.unref && this.stuckTimer.unref(); - } +// Compare file timestamps. +// Some versions of Node on windows zero the milliseconds when utime is used. - stuckTick() { - if (this.runningCount === 1) { - this.warnedStuck = true; - debug(`The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + `without any activity with 1 worker: ${Object.keys(this.running)[0]}`); - } - } - push(key, factory) { - if (this.first) { - this.first = false; - } else { - this.stillActive(); - } +var _fs; - return new Promise((resolve, reject) => { - // we're already running so push ourselves to the queue - const queue = this.queue[key] = this.queue[key] || []; - queue.push({ factory, resolve, reject }); +function _load_fs() { + return _fs = _interopRequireDefault(__webpack_require__(3)); +} - if (!this.running[key]) { - this.shift(key); - } - }); - } +var _promise; - shift(key) { - if (this.running[key]) { - delete this.running[key]; - this.runningCount--; +function _load_promise() { + return _promise = __webpack_require__(40); +} - if (this.stuckTimer) { - clearTimeout(this.stuckTimer); - this.stuckTimer = null; - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - if (this.warnedStuck) { - this.warnedStuck = false; - debug(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); - } - } +// This module serves as a wrapper for file operations that are inconsistant across node and OS versions. - const queue = this.queue[key]; - if (!queue) { - return; - } +let disableTimestampCorrection = undefined; // OS dependent. will be detected on first file copy. - var _queue$shift = queue.shift(); +const readFileBuffer = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.readFile); +const close = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.close); +const lstat = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.lstat); +const open = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.open); +const futimes = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.futimes); - const resolve = _queue$shift.resolve, - reject = _queue$shift.reject, - factory = _queue$shift.factory; +const write = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.write); - if (!queue.length) { - delete this.queue[key]; - } +const unlink = exports.unlink = (0, (_promise || _load_promise()).promisify)(__webpack_require__(233)); - const next = () => { - this.shift(key); - this.shiftConcurrencyQueue(); - }; +/** + * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems + * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). + */ +const copyFile = exports.copyFile = (() => { + var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data, cleanup) { + try { + yield unlink(data.dest); + yield copyFilePoly(data.src, data.dest, 0, data); + } finally { + if (cleanup) { + cleanup(); + } + } + }); - const run = () => { - this.running[key] = true; - this.runningCount++; + return function copyFile(_x, _x2) { + return _ref.apply(this, arguments); + }; +})(); - factory().then(function (val) { - resolve(val); - next(); - return null; - }).catch(function (err) { +// Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. +// Otherwise we fall back to reading and writing files as buffers. +const copyFilePoly = (src, dest, flags, data) => { + if ((_fs || _load_fs()).default.copyFile) { + return new Promise((resolve, reject) => (_fs || _load_fs()).default.copyFile(src, dest, flags, err => { + if (err) { reject(err); - next(); - }); - }; - - this.maybePushConcurrencyQueue(run); + } else { + fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); + } + })); + } else { + return copyWithBuffer(src, dest, flags, data); } +}; - maybePushConcurrencyQueue(run) { - if (this.runningCount < this.maxConcurrency) { - run(); - } else { - this.concurrencyQueue.push(run); +const copyWithBuffer = (() => { + var _ref2 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest, flags, data) { + // Use open -> write -> futimes -> close sequence to avoid opening the file twice: + // one with writeFile and one with utimes + const fd = yield open(dest, 'w', data.mode); + try { + const buffer = yield readFileBuffer(src); + yield write(fd, buffer, 0, buffer.length); + yield fixTimes(fd, dest, data); + } finally { + yield close(fd); } + }); + + return function copyWithBuffer(_x3, _x4, _x5, _x6) { + return _ref2.apply(this, arguments); + }; +})();const fileDatesEqual = exports.fileDatesEqual = (a, b) => { + const aTime = a.getTime(); + const bTime = b.getTime(); + + if (process.platform !== 'win32') { + return aTime === bTime; } - shiftConcurrencyQueue() { - if (this.runningCount < this.maxConcurrency) { - const fn = this.concurrencyQueue.shift(); - if (fn) { - fn(); - } - } + // See https://github.com/nodejs/node/pull/12607 + // Submillisecond times from stat and utimes are truncated on Windows, + // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 + // and making it impossible to update these files to their correct timestamps. + if (Math.abs(aTime - bTime) <= 1) { + return true; } -} -exports.default = BlockingQueue; -/***/ }), -/* 85 */ -/***/ (function(module, exports) { + const aTimeSec = Math.floor(aTime / 1000); + const bTimeSec = Math.floor(bTime / 1000); -module.exports = function (exec) { - try { - return !!exec(); - } catch (e) { - return true; + // See https://github.com/nodejs/node/issues/2069 + // Some versions of Node on windows zero the milliseconds when utime is used + // So if any of the time has a milliseconds part of zero we suspect that the + // bug is present and compare only seconds. + if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { + return aTimeSec === bTimeSec; } -}; + return aTime === bTime; +}; /***/ }), -/* 86 */, -/* 87 */, -/* 88 */, -/* 89 */, -/* 90 */, -/* 91 */, -/* 92 */, -/* 93 */, -/* 94 */, -/* 95 */, -/* 96 */, -/* 97 */, -/* 98 */, -/* 99 */, -/* 100 */ +/* 165 */, +/* 166 */, +/* 167 */, +/* 168 */, +/* 169 */ /***/ (function(module, exports, __webpack_require__) { -// getting tag from 19.1.3.6 Object.prototype.toString() -var cof = __webpack_require__(47); -var TAG = __webpack_require__(13)('toStringTag'); -// ES3 wrong here -var ARG = cof(function () { return arguments; }()) == 'Arguments'; - -// fallback for IE11 Script Access Denied error -var tryGet = function (it, key) { - try { - return it[key]; - } catch (e) { /* empty */ } -}; - -module.exports = function (it) { - var O, T, B; - return it === undefined ? 'Undefined' : it === null ? 'Null' - // @@toStringTag case - : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T - // builtinTag case - : ARG ? cof(O) - // ES3 arguments fallback - : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; -}; +"use strict"; -/***/ }), -/* 101 */ -/***/ (function(module, exports) { +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isFakeRoot = isFakeRoot; +exports.isRootUser = isRootUser; +function getUid() { + if (process.platform !== 'win32' && process.getuid) { + return process.getuid(); + } + return null; +} -// IE 8- don't enum bug keys -module.exports = ( - 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' -).split(','); +exports.default = isRootUser(getUid()) && !isFakeRoot(); +function isFakeRoot() { + return Boolean(process.env.FAKEROOTKEY); +} +function isRootUser(uid) { + return uid === 0; +} /***/ }), -/* 102 */ +/* 170 */, +/* 171 */ /***/ (function(module, exports, __webpack_require__) { -var document = __webpack_require__(11).document; -module.exports = document && document.documentElement; - +"use strict"; -/***/ }), -/* 103 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDataDir = getDataDir; +exports.getCacheDir = getCacheDir; +exports.getConfigDir = getConfigDir; +const path = __webpack_require__(0); +const userHome = __webpack_require__(45).default; -var LIBRARY = __webpack_require__(69); -var $export = __webpack_require__(41); -var redefine = __webpack_require__(197); -var hide = __webpack_require__(31); -var Iterators = __webpack_require__(35); -var $iterCreate = __webpack_require__(188); -var setToStringTag = __webpack_require__(71); -var getPrototypeOf = __webpack_require__(194); -var ITERATOR = __webpack_require__(13)('iterator'); -var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` -var FF_ITERATOR = '@@iterator'; -var KEYS = 'keys'; -var VALUES = 'values'; +const FALLBACK_CONFIG_DIR = path.join(userHome, '.config', 'yarn'); +const FALLBACK_CACHE_DIR = path.join(userHome, '.cache', 'yarn'); -var returnThis = function () { return this; }; +function getDataDir() { + if (process.platform === 'win32') { + const WIN32_APPDATA_DIR = getLocalAppDataDir(); + return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Data'); + } else if (process.env.XDG_DATA_HOME) { + return path.join(process.env.XDG_DATA_HOME, 'yarn'); + } else { + // This could arguably be ~/Library/Application Support/Yarn on Macs, + // but that feels unintuitive for a cli tool -module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { - $iterCreate(Constructor, NAME, next); - var getMethod = function (kind) { - if (!BUGGY && kind in proto) return proto[kind]; - switch (kind) { - case KEYS: return function keys() { return new Constructor(this, kind); }; - case VALUES: return function values() { return new Constructor(this, kind); }; - } return function entries() { return new Constructor(this, kind); }; - }; - var TAG = NAME + ' Iterator'; - var DEF_VALUES = DEFAULT == VALUES; - var VALUES_BUG = false; - var proto = Base.prototype; - var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; - var $default = $native || getMethod(DEFAULT); - var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; - var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; - var methods, key, IteratorPrototype; - // Fix native - if ($anyNative) { - IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); - if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { - // Set @@toStringTag to native iterators - setToStringTag(IteratorPrototype, TAG, true); - // fix for some old engines - if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis); - } - } - // fix Array#{values, @@iterator}.name in V8 / FF - if (DEF_VALUES && $native && $native.name !== VALUES) { - VALUES_BUG = true; - $default = function values() { return $native.call(this); }; - } - // Define iterator - if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { - hide(proto, ITERATOR, $default); - } - // Plug for library - Iterators[NAME] = $default; - Iterators[TAG] = returnThis; - if (DEFAULT) { - methods = { - values: DEF_VALUES ? $default : getMethod(VALUES), - keys: IS_SET ? $default : getMethod(KEYS), - entries: $entries - }; - if (FORCED) for (key in methods) { - if (!(key in proto)) redefine(proto, key, methods[key]); - } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); + // Instead, use our prior fallback. Some day this could be + // path.join(userHome, '.local', 'share', 'yarn') + // or return path.join(WIN32_APPDATA_DIR, 'Data') on win32 + return FALLBACK_CONFIG_DIR; } - return methods; -}; - +} -/***/ }), -/* 104 */ -/***/ (function(module, exports) { +function getCacheDir() { + if (process.platform === 'win32') { + // process.env.TEMP also exists, but most apps put caches here + return path.join(getLocalAppDataDir() || path.join(userHome, 'AppData', 'Local', 'Yarn'), 'Cache'); + } else if (process.env.XDG_CACHE_HOME) { + return path.join(process.env.XDG_CACHE_HOME, 'yarn'); + } else if (process.platform === 'darwin') { + return path.join(userHome, 'Library', 'Caches', 'Yarn'); + } else { + return FALLBACK_CACHE_DIR; + } +} -module.exports = function (exec) { - try { - return { e: false, v: exec() }; - } catch (e) { - return { e: true, v: e }; +function getConfigDir() { + if (process.platform === 'win32') { + // Use our prior fallback. Some day this could be + // return path.join(WIN32_APPDATA_DIR, 'Config') + const WIN32_APPDATA_DIR = getLocalAppDataDir(); + return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Config'); + } else if (process.env.XDG_CONFIG_HOME) { + return path.join(process.env.XDG_CONFIG_HOME, 'yarn'); + } else { + return FALLBACK_CONFIG_DIR; } -}; +} +function getLocalAppDataDir() { + return process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Yarn') : null; +} /***/ }), -/* 105 */ +/* 172 */, +/* 173 */ /***/ (function(module, exports, __webpack_require__) { -var anObject = __webpack_require__(27); -var isObject = __webpack_require__(34); -var newPromiseCapability = __webpack_require__(70); - -module.exports = function (C, x) { - anObject(C); - if (isObject(x) && x.constructor === C) return x; - var promiseCapability = newPromiseCapability.f(C); - var resolve = promiseCapability.resolve; - resolve(x); - return promiseCapability.promise; -}; - +module.exports = { "default": __webpack_require__(179), __esModule: true }; /***/ }), -/* 106 */ -/***/ (function(module, exports) { +/* 174 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = function (bitmap, value) { - return { - enumerable: !(bitmap & 1), - configurable: !(bitmap & 2), - writable: !(bitmap & 4), - value: value - }; -}; +"use strict"; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); -/***/ }), -/* 107 */ -/***/ (function(module, exports, __webpack_require__) { + var r = range(a, b, str); -var core = __webpack_require__(23); -var global = __webpack_require__(11); -var SHARED = '__core-js_shared__'; -var store = global[SHARED] || (global[SHARED] = {}); + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} -(module.exports = function (key, value) { - return store[key] || (store[key] = value !== undefined ? value : {}); -})('versions', []).push({ - version: core.version, - mode: __webpack_require__(69) ? 'pure' : 'global', - copyright: '© 2018 Denis Pushkarev (zloirock.ru)' -}); +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; +} +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; -/***/ }), -/* 108 */ -/***/ (function(module, exports, __webpack_require__) { + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; -// 7.3.20 SpeciesConstructor(O, defaultConstructor) -var anObject = __webpack_require__(27); -var aFunction = __webpack_require__(46); -var SPECIES = __webpack_require__(13)('species'); -module.exports = function (O, D) { - var C = anObject(O).constructor; - var S; - return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); -}; + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; + } + bi = str.indexOf(b, i + 1); + } -/***/ }), -/* 109 */ -/***/ (function(module, exports, __webpack_require__) { + i = ai < bi && ai >= 0 ? ai : bi; + } -var ctx = __webpack_require__(48); -var invoke = __webpack_require__(185); -var html = __webpack_require__(102); -var cel = __webpack_require__(68); -var global = __webpack_require__(11); -var process = global.process; -var setTask = global.setImmediate; -var clearTask = global.clearImmediate; -var MessageChannel = global.MessageChannel; -var Dispatch = global.Dispatch; -var counter = 0; -var queue = {}; -var ONREADYSTATECHANGE = 'onreadystatechange'; -var defer, channel, port; -var run = function () { - var id = +this; - // eslint-disable-next-line no-prototype-builtins - if (queue.hasOwnProperty(id)) { - var fn = queue[id]; - delete queue[id]; - fn(); - } -}; -var listener = function (event) { - run.call(event.data); -}; -// Node.js 0.9+ & IE10+ has setImmediate, otherwise: -if (!setTask || !clearTask) { - setTask = function setImmediate(fn) { - var args = []; - var i = 1; - while (arguments.length > i) args.push(arguments[i++]); - queue[++counter] = function () { - // eslint-disable-next-line no-new-func - invoke(typeof fn == 'function' ? fn : Function(fn), args); - }; - defer(counter); - return counter; - }; - clearTask = function clearImmediate(id) { - delete queue[id]; - }; - // Node.js 0.8- - if (__webpack_require__(47)(process) == 'process') { - defer = function (id) { - process.nextTick(ctx(run, id, 1)); - }; - // Sphere (JS game engine) Dispatch API - } else if (Dispatch && Dispatch.now) { - defer = function (id) { - Dispatch.now(ctx(run, id, 1)); - }; - // Browsers with MessageChannel, includes WebWorkers - } else if (MessageChannel) { - channel = new MessageChannel(); - port = channel.port2; - channel.port1.onmessage = listener; - defer = ctx(port.postMessage, port, 1); - // Browsers with postMessage, skip WebWorkers - // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' - } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) { - defer = function (id) { - global.postMessage(id + '', '*'); - }; - global.addEventListener('message', listener, false); - // IE8- - } else if (ONREADYSTATECHANGE in cel('script')) { - defer = function (id) { - html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () { - html.removeChild(this); - run.call(id); - }; - }; - // Rest old browsers - } else { - defer = function (id) { - setTimeout(ctx(run, id, 1), 0); - }; + if (begs.length) { + result = [ left, right ]; + } } + + return result; } -module.exports = { - set: setTask, - clear: clearTask -}; /***/ }), -/* 110 */ +/* 175 */ /***/ (function(module, exports, __webpack_require__) { -// 7.1.15 ToLength -var toInteger = __webpack_require__(73); -var min = Math.min; -module.exports = function (it) { - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 -}; - +var concatMap = __webpack_require__(178); +var balanced = __webpack_require__(174); -/***/ }), -/* 111 */ -/***/ (function(module, exports) { +module.exports = expandTop; -var id = 0; -var px = Math.random(); -module.exports = function (key) { - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); -}; +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); +} -/***/ }), -/* 112 */ -/***/ (function(module, exports, __webpack_require__) { +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = __webpack_require__(229); +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; -/** - * Active `debug` instances. - */ -exports.instances = []; + var parts = []; + var m = balanced('{', '}', str); -/** - * The currently active debug mode names, and names to skip. - */ + if (!m) + return str.split(','); -exports.names = []; -exports.skips = []; + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } -exports.formatters = {}; + parts.push.apply(parts, p); -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ + return parts; +} -function selectColor(namespace) { - var hash = 0, i; +function expandTop(str) { + if (!str) + return []; - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); } - return exports.colors[Math.abs(hash) % exports.colors.length]; + return expand(escapeBraces(str), true).map(unescapeBraces); } -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ +function identity(e) { + return e; +} -function createDebug(namespace) { - - var prevTime; +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} - function debug() { - // disabled? - if (!debug.enabled) return; +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} - var self = debug; +function expand(str, isTop) { + var expansions = []; - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); } + return [str]; + } - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); + } } + } - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); + var N; - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - debug.destroy = destroy; + N = []; - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); } - exports.instances.push(debug); + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } - return debug; + return expansions; } -function destroy () { - var index = exports.instances.indexOf(this); - if (index !== -1) { - exports.instances.splice(index, 1); - return true; - } else { - return false; - } -} -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ -function enable(namespaces) { - exports.save(namespaces); +/***/ }), +/* 176 */ +/***/ (function(module, exports, __webpack_require__) { - exports.names = []; - exports.skips = []; +"use strict"; - var i; - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - for (i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } +function preserveCamelCase(str) { + let isLastCharLower = false; + let isLastCharUpper = false; + let isLastLastCharUpper = false; - for (i = 0; i < exports.instances.length; i++) { - var instance = exports.instances[i]; - instance.enabled = exports.enabled(instance.namespace); - } -} + for (let i = 0; i < str.length; i++) { + const c = str[i]; -/** - * Disable debug output. - * - * @api public - */ + if (isLastCharLower && /[a-zA-Z]/.test(c) && c.toUpperCase() === c) { + str = str.substr(0, i) + '-' + str.substr(i); + isLastCharLower = false; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = true; + i++; + } else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(c) && c.toLowerCase() === c) { + str = str.substr(0, i - 1) + '-' + str.substr(i - 1); + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = false; + isLastCharLower = true; + } else { + isLastCharLower = c.toLowerCase() === c; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = c.toUpperCase() === c; + } + } -function disable() { - exports.enable(''); + return str; } -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ +module.exports = function (str) { + if (arguments.length > 1) { + str = Array.from(arguments) + .map(x => x.trim()) + .filter(x => x.length) + .join('-'); + } else { + str = str.trim(); + } -function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; -} + if (str.length === 0) { + return ''; + } -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ + if (str.length === 1) { + return str.toLowerCase(); + } -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} + if (/^[a-z0-9]+$/.test(str)) { + return str; + } + + const hasUpperCase = str !== str.toLowerCase(); + + if (hasUpperCase) { + str = preserveCamelCase(str); + } + + return str + .replace(/^[_.\- ]+/, '') + .toLowerCase() + .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); +}; /***/ }), -/* 113 */, -/* 114 */ -/***/ (function(module, exports, __webpack_require__) { +/* 177 */, +/* 178 */ +/***/ (function(module, exports) { -module.exports = realpath -realpath.realpath = realpath -realpath.sync = realpathSync -realpath.realpathSync = realpathSync -realpath.monkeypatch = monkeypatch -realpath.unmonkeypatch = unmonkeypatch +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; +}; -var fs = __webpack_require__(3) -var origRealpath = fs.realpath -var origRealpathSync = fs.realpathSync +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; +}; -var version = process.version -var ok = /^v[0-5]\./.test(version) -var old = __webpack_require__(217) -function newError (er) { - return er && er.syscall === 'realpath' && ( - er.code === 'ELOOP' || - er.code === 'ENOMEM' || - er.code === 'ENAMETOOLONG' - ) -} +/***/ }), +/* 179 */ +/***/ (function(module, exports, __webpack_require__) { -function realpath (p, cache, cb) { - if (ok) { - return origRealpath(p, cache, cb) - } +__webpack_require__(205); +__webpack_require__(207); +__webpack_require__(210); +__webpack_require__(206); +__webpack_require__(208); +__webpack_require__(209); +module.exports = __webpack_require__(23).Promise; - if (typeof cache === 'function') { - cb = cache - cache = null - } - origRealpath(p, cache, function (er, result) { - if (newError(er)) { - old.realpath(p, cache, cb) - } else { - cb(er, result) - } - }) -} -function realpathSync (p, cache) { - if (ok) { - return origRealpathSync(p, cache) - } +/***/ }), +/* 180 */ +/***/ (function(module, exports) { - try { - return origRealpathSync(p, cache) - } catch (er) { - if (newError(er)) { - return old.realpathSync(p, cache) - } else { - throw er - } - } -} +module.exports = function () { /* empty */ }; -function monkeypatch () { - fs.realpath = realpath - fs.realpathSync = realpathSync -} -function unmonkeypatch () { - fs.realpath = origRealpath - fs.realpathSync = origRealpathSync -} +/***/ }), +/* 181 */ +/***/ (function(module, exports) { + +module.exports = function (it, Constructor, name, forbiddenField) { + if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) { + throw TypeError(name + ': incorrect invocation!'); + } return it; +}; /***/ }), -/* 115 */ +/* 182 */ /***/ (function(module, exports, __webpack_require__) { -exports.alphasort = alphasort -exports.alphasorti = alphasorti -exports.setopts = setopts -exports.ownProp = ownProp -exports.makeAbs = makeAbs -exports.finish = finish -exports.mark = mark -exports.isIgnored = isIgnored -exports.childrenIgnored = childrenIgnored +// false -> Array#indexOf +// true -> Array#includes +var toIObject = __webpack_require__(74); +var toLength = __webpack_require__(110); +var toAbsoluteIndex = __webpack_require__(200); +module.exports = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIObject($this); + var length = toLength(O.length); + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare + if (value != value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) if (IS_INCLUDES || index in O) { + if (O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; +}; -function ownProp (obj, field) { - return Object.prototype.hasOwnProperty.call(obj, field) -} -var path = __webpack_require__(0) -var minimatch = __webpack_require__(60) -var isAbsolute = __webpack_require__(76) -var Minimatch = minimatch.Minimatch +/***/ }), +/* 183 */ +/***/ (function(module, exports, __webpack_require__) { -function alphasorti (a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()) -} +var ctx = __webpack_require__(48); +var call = __webpack_require__(187); +var isArrayIter = __webpack_require__(186); +var anObject = __webpack_require__(27); +var toLength = __webpack_require__(110); +var getIterFn = __webpack_require__(203); +var BREAK = {}; +var RETURN = {}; +var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) { + var iterFn = ITERATOR ? function () { return iterable; } : getIterFn(iterable); + var f = ctx(fn, that, entries ? 2 : 1); + var index = 0; + var length, step, iterator, result; + if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!'); + // fast case for arrays with default iterator + if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) { + result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); + if (result === BREAK || result === RETURN) return result; + } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) { + result = call(iterator, f, step.value, entries); + if (result === BREAK || result === RETURN) return result; + } +}; +exports.BREAK = BREAK; +exports.RETURN = RETURN; -function alphasort (a, b) { - return a.localeCompare(b) -} -function setupIgnores (self, options) { - self.ignore = options.ignore || [] +/***/ }), +/* 184 */ +/***/ (function(module, exports, __webpack_require__) { - if (!Array.isArray(self.ignore)) - self.ignore = [self.ignore] +module.exports = !__webpack_require__(33) && !__webpack_require__(85)(function () { + return Object.defineProperty(__webpack_require__(68)('div'), 'a', { get: function () { return 7; } }).a != 7; +}); - if (self.ignore.length) { - self.ignore = self.ignore.map(ignoreMap) - } -} -// ignore patterns are always in dot:true mode. -function ignoreMap (pattern) { - var gmatcher = null - if (pattern.slice(-3) === '/**') { - var gpattern = pattern.replace(/(\/\*\*)+$/, '') - gmatcher = new Minimatch(gpattern, { dot: true }) - } +/***/ }), +/* 185 */ +/***/ (function(module, exports) { - return { - matcher: new Minimatch(pattern, { dot: true }), - gmatcher: gmatcher - } -} +// fast apply, http://jsperf.lnkit.com/fast-apply/5 +module.exports = function (fn, args, that) { + var un = that === undefined; + switch (args.length) { + case 0: return un ? fn() + : fn.call(that); + case 1: return un ? fn(args[0]) + : fn.call(that, args[0]); + case 2: return un ? fn(args[0], args[1]) + : fn.call(that, args[0], args[1]); + case 3: return un ? fn(args[0], args[1], args[2]) + : fn.call(that, args[0], args[1], args[2]); + case 4: return un ? fn(args[0], args[1], args[2], args[3]) + : fn.call(that, args[0], args[1], args[2], args[3]); + } return fn.apply(that, args); +}; -function setopts (self, pattern, options) { - if (!options) - options = {} - // base-matching: just use globstar for that. - if (options.matchBase && -1 === pattern.indexOf("/")) { - if (options.noglobstar) { - throw new Error("base matching requires globstar") - } - pattern = "**/" + pattern - } - - self.silent = !!options.silent - self.pattern = pattern - self.strict = options.strict !== false - self.realpath = !!options.realpath - self.realpathCache = options.realpathCache || Object.create(null) - self.follow = !!options.follow - self.dot = !!options.dot - self.mark = !!options.mark - self.nodir = !!options.nodir - if (self.nodir) - self.mark = true - self.sync = !!options.sync - self.nounique = !!options.nounique - self.nonull = !!options.nonull - self.nosort = !!options.nosort - self.nocase = !!options.nocase - self.stat = !!options.stat - self.noprocess = !!options.noprocess - self.absolute = !!options.absolute +/***/ }), +/* 186 */ +/***/ (function(module, exports, __webpack_require__) { - self.maxLength = options.maxLength || Infinity - self.cache = options.cache || Object.create(null) - self.statCache = options.statCache || Object.create(null) - self.symlinks = options.symlinks || Object.create(null) +// check on default Array iterator +var Iterators = __webpack_require__(35); +var ITERATOR = __webpack_require__(13)('iterator'); +var ArrayProto = Array.prototype; - setupIgnores(self, options) +module.exports = function (it) { + return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); +}; - self.changedCwd = false - var cwd = process.cwd() - if (!ownProp(options, "cwd")) - self.cwd = cwd - else { - self.cwd = path.resolve(options.cwd) - self.changedCwd = self.cwd !== cwd - } - self.root = options.root || path.resolve(self.cwd, "/") - self.root = path.resolve(self.root) - if (process.platform === "win32") - self.root = self.root.replace(/\\/g, "/") +/***/ }), +/* 187 */ +/***/ (function(module, exports, __webpack_require__) { - // TODO: is an absolute `cwd` supposed to be resolved against `root`? - // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') - self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) - if (process.platform === "win32") - self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") - self.nomount = !!options.nomount +// call something on iterator step with safe closing on error +var anObject = __webpack_require__(27); +module.exports = function (iterator, fn, value, entries) { + try { + return entries ? fn(anObject(value)[0], value[1]) : fn(value); + // 7.4.6 IteratorClose(iterator, completion) + } catch (e) { + var ret = iterator['return']; + if (ret !== undefined) anObject(ret.call(iterator)); + throw e; + } +}; - // disable comments and negation in Minimatch. - // Note that they are not supported in Glob itself anyway. - options.nonegate = true - options.nocomment = true - self.minimatch = new Minimatch(pattern, options) - self.options = self.minimatch.options -} +/***/ }), +/* 188 */ +/***/ (function(module, exports, __webpack_require__) { -function finish (self) { - var nou = self.nounique - var all = nou ? [] : Object.create(null) +"use strict"; - for (var i = 0, l = self.matches.length; i < l; i ++) { - var matches = self.matches[i] - if (!matches || Object.keys(matches).length === 0) { - if (self.nonull) { - // do like the shell, and spit out the literal glob - var literal = self.minimatch.globSet[i] - if (nou) - all.push(literal) - else - all[literal] = true - } - } else { - // had matches - var m = Object.keys(matches) - if (nou) - all.push.apply(all, m) - else - m.forEach(function (m) { - all[m] = true - }) - } - } +var create = __webpack_require__(192); +var descriptor = __webpack_require__(106); +var setToStringTag = __webpack_require__(71); +var IteratorPrototype = {}; - if (!nou) - all = Object.keys(all) +// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() +__webpack_require__(31)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; }); - if (!self.nosort) - all = all.sort(self.nocase ? alphasorti : alphasort) +module.exports = function (Constructor, NAME, next) { + Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); + setToStringTag(Constructor, NAME + ' Iterator'); +}; - // at *some* point we statted all of these - if (self.mark) { - for (var i = 0; i < all.length; i++) { - all[i] = self._mark(all[i]) - } - if (self.nodir) { - all = all.filter(function (e) { - var notDir = !(/\/$/.test(e)) - var c = self.cache[e] || self.cache[makeAbs(self, e)] - if (notDir && c) - notDir = c !== 'DIR' && !Array.isArray(c) - return notDir - }) - } - } - if (self.ignore.length) - all = all.filter(function(m) { - return !isIgnored(self, m) - }) +/***/ }), +/* 189 */ +/***/ (function(module, exports, __webpack_require__) { - self.found = all -} +var ITERATOR = __webpack_require__(13)('iterator'); +var SAFE_CLOSING = false; -function mark (self, p) { - var abs = makeAbs(self, p) - var c = self.cache[abs] - var m = p - if (c) { - var isDir = c === 'DIR' || Array.isArray(c) - var slash = p.slice(-1) === '/' +try { + var riter = [7][ITERATOR](); + riter['return'] = function () { SAFE_CLOSING = true; }; + // eslint-disable-next-line no-throw-literal + Array.from(riter, function () { throw 2; }); +} catch (e) { /* empty */ } - if (isDir && !slash) - m += '/' - else if (!isDir && slash) - m = m.slice(0, -1) +module.exports = function (exec, skipClosing) { + if (!skipClosing && !SAFE_CLOSING) return false; + var safe = false; + try { + var arr = [7]; + var iter = arr[ITERATOR](); + iter.next = function () { return { done: safe = true }; }; + arr[ITERATOR] = function () { return iter; }; + exec(arr); + } catch (e) { /* empty */ } + return safe; +}; - if (m !== p) { - var mabs = makeAbs(self, m) - self.statCache[mabs] = self.statCache[abs] - self.cache[mabs] = self.cache[abs] - } - } - return m -} +/***/ }), +/* 190 */ +/***/ (function(module, exports) { -// lotta situps... -function makeAbs (self, f) { - var abs = f - if (f.charAt(0) === '/') { - abs = path.join(self.root, f) - } else if (isAbsolute(f) || f === '') { - abs = f - } else if (self.changedCwd) { - abs = path.resolve(self.cwd, f) - } else { - abs = path.resolve(f) - } +module.exports = function (done, value) { + return { value: value, done: !!done }; +}; - if (process.platform === 'win32') - abs = abs.replace(/\\/g, '/') - return abs -} +/***/ }), +/* 191 */ +/***/ (function(module, exports, __webpack_require__) { +var global = __webpack_require__(11); +var macrotask = __webpack_require__(109).set; +var Observer = global.MutationObserver || global.WebKitMutationObserver; +var process = global.process; +var Promise = global.Promise; +var isNode = __webpack_require__(47)(process) == 'process'; -// Return true, if pattern ends with globstar '**', for the accompanying parent directory. -// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents -function isIgnored (self, path) { - if (!self.ignore.length) - return false +module.exports = function () { + var head, last, notify; - return self.ignore.some(function(item) { - return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) - }) -} + var flush = function () { + var parent, fn; + if (isNode && (parent = process.domain)) parent.exit(); + while (head) { + fn = head.fn; + head = head.next; + try { + fn(); + } catch (e) { + if (head) notify(); + else last = undefined; + throw e; + } + } last = undefined; + if (parent) parent.enter(); + }; -function childrenIgnored (self, path) { - if (!self.ignore.length) - return false + // Node.js + if (isNode) { + notify = function () { + process.nextTick(flush); + }; + // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339 + } else if (Observer && !(global.navigator && global.navigator.standalone)) { + var toggle = true; + var node = document.createTextNode(''); + new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new + notify = function () { + node.data = toggle = !toggle; + }; + // environments with maybe non-completely correct, but existent Promise + } else if (Promise && Promise.resolve) { + // Promise.resolve without an argument throws an error in LG WebOS 2 + var promise = Promise.resolve(undefined); + notify = function () { + promise.then(flush); + }; + // for other environments - macrotask based on: + // - setImmediate + // - MessageChannel + // - window.postMessag + // - onreadystatechange + // - setTimeout + } else { + notify = function () { + // strange IE + webpack dev server bug - use .call(global) + macrotask.call(global, flush); + }; + } - return self.ignore.some(function(item) { - return !!(item.gmatcher && item.gmatcher.match(path)) - }) -} + return function (fn) { + var task = { fn: fn, next: undefined }; + if (last) last.next = task; + if (!head) { + head = task; + notify(); + } last = task; + }; +}; /***/ }), -/* 116 */ +/* 192 */ /***/ (function(module, exports, __webpack_require__) { -var path = __webpack_require__(0); -var fs = __webpack_require__(3); -var _0777 = parseInt('0777', 8); +// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) +var anObject = __webpack_require__(27); +var dPs = __webpack_require__(193); +var enumBugKeys = __webpack_require__(101); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); +var Empty = function () { /* empty */ }; +var PROTOTYPE = 'prototype'; -module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; +// Create object with fake `null` prototype: use iframe Object with cleared prototype +var createDict = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = __webpack_require__(68)('iframe'); + var i = enumBugKeys.length; + var lt = '<'; + var gt = '>'; + var iframeDocument; + iframe.style.display = 'none'; + __webpack_require__(102).appendChild(iframe); + iframe.src = 'javascript:'; // eslint-disable-line no-script-url + // createDict = iframe.contentWindow.Object; + // html.removeChild(iframe); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); + iframeDocument.close(); + createDict = iframeDocument.F; + while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; + return createDict(); +}; -function mkdirP (p, opts, f, made) { - if (typeof opts === 'function') { - f = opts; - opts = {}; - } - else if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; - - var cb = f || function () {}; - p = path.resolve(p); - - xfs.mkdir(p, mode, function (er) { - if (!er) { - made = made || p; - return cb(null, made); - } - switch (er.code) { - case 'ENOENT': - mkdirP(path.dirname(p), opts, function (er, made) { - if (er) cb(er, made); - else mkdirP(p, opts, cb, made); - }); - break; +module.exports = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + Empty[PROTOTYPE] = anObject(O); + result = new Empty(); + Empty[PROTOTYPE] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO] = O; + } else result = createDict(); + return Properties === undefined ? result : dPs(result, Properties); +}; - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - xfs.stat(p, function (er2, stat) { - // if the stat fails, then that's super weird. - // let the original error be the failure reason. - if (er2 || !stat.isDirectory()) cb(er, made) - else cb(null, made); - }); - break; - } - }); -} -mkdirP.sync = function sync (p, opts, made) { - if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; +/***/ }), +/* 193 */ +/***/ (function(module, exports, __webpack_require__) { - p = path.resolve(p); +var dP = __webpack_require__(50); +var anObject = __webpack_require__(27); +var getKeys = __webpack_require__(132); - try { - xfs.mkdirSync(p, mode); - made = made || p; - } - catch (err0) { - switch (err0.code) { - case 'ENOENT' : - made = sync(path.dirname(p), opts, made); - sync(p, opts, made); - break; +module.exports = __webpack_require__(33) ? Object.defineProperties : function defineProperties(O, Properties) { + anObject(O); + var keys = getKeys(Properties); + var length = keys.length; + var i = 0; + var P; + while (length > i) dP.f(O, P = keys[i++], Properties[P]); + return O; +}; - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - var stat; - try { - stat = xfs.statSync(p); - } - catch (err1) { - throw err0; - } - if (!stat.isDirectory()) throw err0; - break; - } - } - return made; +/***/ }), +/* 194 */ +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) +var has = __webpack_require__(49); +var toObject = __webpack_require__(133); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); +var ObjectProto = Object.prototype; + +module.exports = Object.getPrototypeOf || function (O) { + O = toObject(O); + if (has(O, IE_PROTO)) return O[IE_PROTO]; + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } return O instanceof Object ? ObjectProto : null; }; /***/ }), -/* 117 */, -/* 118 */, -/* 119 */, -/* 120 */, -/* 121 */, -/* 122 */ +/* 195 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var has = __webpack_require__(49); +var toIObject = __webpack_require__(74); +var arrayIndexOf = __webpack_require__(182)(false); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); -module.exports = x => { - if (typeof x !== 'string') { - throw new TypeError('Expected a string, got ' + typeof x); - } +module.exports = function (object, names) { + var O = toIObject(object); + var i = 0; + var result = []; + var key; + for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); + // Don't enum bug & hidden keys + while (names.length > i) if (has(O, key = names[i++])) { + ~arrayIndexOf(result, key) || result.push(key); + } + return result; +}; - // Catches EFBBBF (UTF-8 BOM) because the buffer-to-string - // conversion translates it to FEFF (UTF-16 BOM) - if (x.charCodeAt(0) === 0xFEFF) { - return x.slice(1); - } - return x; +/***/ }), +/* 196 */ +/***/ (function(module, exports, __webpack_require__) { + +var hide = __webpack_require__(31); +module.exports = function (target, src, safe) { + for (var key in src) { + if (safe && target[key]) target[key] = src[key]; + else hide(target, key, src[key]); + } return target; }; /***/ }), -/* 123 */ -/***/ (function(module, exports) { +/* 197 */ +/***/ (function(module, exports, __webpack_require__) { -// Returns a wrapper function that returns a wrapped callback -// The wrapper function should do some stuff, and return a -// presumably different callback function. -// This makes sure that own properties are retained, so that -// decorations and such are not lost along the way. -module.exports = wrappy -function wrappy (fn, cb) { - if (fn && cb) return wrappy(fn)(cb) +module.exports = __webpack_require__(31); - if (typeof fn !== 'function') - throw new TypeError('need wrapper function') - Object.keys(fn).forEach(function (k) { - wrapper[k] = fn[k] - }) +/***/ }), +/* 198 */ +/***/ (function(module, exports, __webpack_require__) { - return wrapper +"use strict"; - function wrapper() { - var args = new Array(arguments.length) - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i] - } - var ret = fn.apply(this, args) - var cb = args[args.length-1] - if (typeof ret === 'function' && ret !== cb) { - Object.keys(cb).forEach(function (k) { - ret[k] = cb[k] - }) - } - return ret - } -} +var global = __webpack_require__(11); +var core = __webpack_require__(23); +var dP = __webpack_require__(50); +var DESCRIPTORS = __webpack_require__(33); +var SPECIES = __webpack_require__(13)('species'); + +module.exports = function (KEY) { + var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; + if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, { + configurable: true, + get: function () { return this; } + }); +}; /***/ }), -/* 124 */, -/* 125 */, -/* 126 */, -/* 127 */, -/* 128 */, -/* 129 */, -/* 130 */, -/* 131 */ +/* 199 */ /***/ (function(module, exports, __webpack_require__) { -// fallback for non-array-like ES3 and non-enumerable old V8 strings -var cof = __webpack_require__(47); -// eslint-disable-next-line no-prototype-builtins -module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { - return cof(it) == 'String' ? it.split('') : Object(it); +var toInteger = __webpack_require__(73); +var defined = __webpack_require__(67); +// true -> String#at +// false -> String#codePointAt +module.exports = function (TO_STRING) { + return function (that, pos) { + var s = String(defined(that)); + var i = toInteger(pos); + var l = s.length; + var a, b; + if (i < 0 || i >= l) return TO_STRING ? '' : undefined; + a = s.charCodeAt(i); + return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff + ? TO_STRING ? s.charAt(i) : a + : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; + }; }; /***/ }), -/* 132 */ +/* 200 */ /***/ (function(module, exports, __webpack_require__) { -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = __webpack_require__(195); -var enumBugKeys = __webpack_require__(101); - -module.exports = Object.keys || function keys(O) { - return $keys(O, enumBugKeys); +var toInteger = __webpack_require__(73); +var max = Math.max; +var min = Math.min; +module.exports = function (index, length) { + index = toInteger(index); + return index < 0 ? max(index + length, 0) : min(index, length); }; /***/ }), -/* 133 */ +/* 201 */ /***/ (function(module, exports, __webpack_require__) { -// 7.1.13 ToObject(argument) -var defined = __webpack_require__(67); -module.exports = function (it) { - return Object(defined(it)); +// 7.1.1 ToPrimitive(input [, PreferredType]) +var isObject = __webpack_require__(34); +// instead of the ES6 spec version, we didn't implement @@toPrimitive case +// and the second argument - flag - preferred type is a string +module.exports = function (it, S) { + if (!isObject(it)) return it; + var fn, val; + if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; + if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + throw TypeError("Can't convert object to primitive value"); }; /***/ }), -/* 134 */, -/* 135 */, -/* 136 */, -/* 137 */, -/* 138 */, -/* 139 */, -/* 140 */, -/* 141 */, -/* 142 */, -/* 143 */, -/* 144 */, -/* 145 */ -/***/ (function(module, exports) { - -module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.10.0-0","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^2.2.4","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^3.0.1","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.3","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.24","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.10.0","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^3.9.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","gulp-util":"^3.0.7","gulp-watch":"^5.0.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} - -/***/ }), -/* 146 */, -/* 147 */, -/* 148 */, -/* 149 */, -/* 150 */ +/* 202 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = stringify; - -var _misc; - -function _load_misc() { - return _misc = __webpack_require__(12); -} - -var _constants; - -function _load_constants() { - return _constants = __webpack_require__(6); -} - -var _package; - -function _load_package() { - return _package = __webpack_require__(145); -} +var global = __webpack_require__(11); +var navigator = global.navigator; -const NODE_VERSION = process.version; +module.exports = navigator && navigator.userAgent || ''; -function shouldWrapKey(str) { - return str.indexOf('true') === 0 || str.indexOf('false') === 0 || /[:\s\n\\",\[\]]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str); -} -function maybeWrap(str) { - if (typeof str === 'boolean' || typeof str === 'number' || shouldWrapKey(str)) { - return JSON.stringify(str); - } else { - return str; - } -} +/***/ }), +/* 203 */ +/***/ (function(module, exports, __webpack_require__) { -const priorities = { - name: 1, - version: 2, - uid: 3, - resolved: 4, - integrity: 5, - registry: 6, - dependencies: 7 +var classof = __webpack_require__(100); +var ITERATOR = __webpack_require__(13)('iterator'); +var Iterators = __webpack_require__(35); +module.exports = __webpack_require__(23).getIteratorMethod = function (it) { + if (it != undefined) return it[ITERATOR] + || it['@@iterator'] + || Iterators[classof(it)]; }; -function priorityThenAlphaSort(a, b) { - if (priorities[a] || priorities[b]) { - return (priorities[a] || 100) > (priorities[b] || 100) ? 1 : -1; - } else { - return (0, (_misc || _load_misc()).sortAlpha)(a, b); - } -} - -function _stringify(obj, options) { - if (typeof obj !== 'object') { - throw new TypeError(); - } - - const indent = options.indent; - const lines = []; - - // Sorting order needs to be consistent between runs, we run native sort by name because there are no - // problems with it being unstable because there are no to keys the same - // However priorities can be duplicated and native sort can shuffle things from run to run - const keys = Object.keys(obj).sort(priorityThenAlphaSort); - - let addedKeys = []; - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const val = obj[key]; - if (val == null || addedKeys.indexOf(key) >= 0) { - continue; - } +/***/ }), +/* 204 */ +/***/ (function(module, exports, __webpack_require__) { - const valKeys = [key]; +"use strict"; - // get all keys that have the same value equality, we only want this for objects - if (typeof val === 'object') { - for (let j = i + 1; j < keys.length; j++) { - const key = keys[j]; - if (val === obj[key]) { - valKeys.push(key); - } - } - } +var addToUnscopables = __webpack_require__(180); +var step = __webpack_require__(190); +var Iterators = __webpack_require__(35); +var toIObject = __webpack_require__(74); - const keyLine = valKeys.sort((_misc || _load_misc()).sortAlpha).map(maybeWrap).join(', '); +// 22.1.3.4 Array.prototype.entries() +// 22.1.3.13 Array.prototype.keys() +// 22.1.3.29 Array.prototype.values() +// 22.1.3.30 Array.prototype[@@iterator]() +module.exports = __webpack_require__(103)(Array, 'Array', function (iterated, kind) { + this._t = toIObject(iterated); // target + this._i = 0; // next index + this._k = kind; // kind +// 22.1.5.2.1 %ArrayIteratorPrototype%.next() +}, function () { + var O = this._t; + var kind = this._k; + var index = this._i++; + if (!O || index >= O.length) { + this._t = undefined; + return step(1); + } + if (kind == 'keys') return step(0, index); + if (kind == 'values') return step(0, O[index]); + return step(0, [index, O[index]]); +}, 'values'); - if (typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number') { - lines.push(`${keyLine} ${maybeWrap(val)}`); - } else if (typeof val === 'object') { - lines.push(`${keyLine}:\n${_stringify(val, { indent: indent + ' ' })}` + (options.topLevel ? '\n' : '')); - } else { - throw new TypeError(); - } +// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) +Iterators.Arguments = Iterators.Array; - addedKeys = addedKeys.concat(valKeys); - } +addToUnscopables('keys'); +addToUnscopables('values'); +addToUnscopables('entries'); - return indent + lines.join(`\n${indent}`); -} -function stringify(obj, noHeader, enableVersions) { - const val = _stringify(obj, { - indent: '', - topLevel: true - }); - if (noHeader) { - return val; - } +/***/ }), +/* 205 */ +/***/ (function(module, exports) { - const lines = []; - lines.push('# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.'); - lines.push(`# yarn lockfile v${(_constants || _load_constants()).LOCKFILE_VERSION}`); - if (enableVersions) { - lines.push(`# yarn v${(_package || _load_package()).version}`); - lines.push(`# node ${NODE_VERSION}`); - } - lines.push('\n'); - lines.push(val); - return lines.join('\n'); -} /***/ }), -/* 151 */, -/* 152 */, -/* 153 */, -/* 154 */, -/* 155 */, -/* 156 */, -/* 157 */, -/* 158 */, -/* 159 */, -/* 160 */, -/* 161 */, -/* 162 */, -/* 163 */, -/* 164 */ +/* 206 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +var LIBRARY = __webpack_require__(69); +var global = __webpack_require__(11); +var ctx = __webpack_require__(48); +var classof = __webpack_require__(100); +var $export = __webpack_require__(41); +var isObject = __webpack_require__(34); +var aFunction = __webpack_require__(46); +var anInstance = __webpack_require__(181); +var forOf = __webpack_require__(183); +var speciesConstructor = __webpack_require__(108); +var task = __webpack_require__(109).set; +var microtask = __webpack_require__(191)(); +var newPromiseCapabilityModule = __webpack_require__(70); +var perform = __webpack_require__(104); +var userAgent = __webpack_require__(202); +var promiseResolve = __webpack_require__(105); +var PROMISE = 'Promise'; +var TypeError = global.TypeError; +var process = global.process; +var versions = process && process.versions; +var v8 = versions && versions.v8 || ''; +var $Promise = global[PROMISE]; +var isNode = classof(process) == 'process'; +var empty = function () { /* empty */ }; +var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; +var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.fileDatesEqual = exports.copyFile = exports.unlink = undefined; - -var _asyncToGenerator2; - -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); -} - -// We want to preserve file timestamps when copying a file, since yarn uses them to decide if a file has -// changed compared to the cache. -// There are some OS specific cases here: -// * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. -// * On windows, you must open a file with write permissions to call `fs.futimes`. -// * On OSX you can open with read permissions and still call `fs.futimes`. -let fixTimes = (() => { - var _ref3 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (fd, dest, data) { - const doOpen = fd === undefined; - let openfd = fd ? fd : -1; - - if (disableTimestampCorrection === undefined) { - // if timestamps match already, no correction is needed. - // the need to correct timestamps varies based on OS and node versions. - const destStat = yield lstat(dest); - disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); - } - - if (disableTimestampCorrection) { - return; - } +var USE_NATIVE = !!function () { + try { + // correct subclassing with @@species support + var promise = $Promise.resolve(1); + var FakePromise = (promise.constructor = {})[__webpack_require__(13)('species')] = function (exec) { + exec(empty, empty); + }; + // unhandled rejections tracking support, NodeJS Promise without it fails @@species test + return (isNode || typeof PromiseRejectionEvent == 'function') + && promise.then(empty) instanceof FakePromise + // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables + // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 + // we can't detect it synchronously, so just check versions + && v8.indexOf('6.6') !== 0 + && userAgent.indexOf('Chrome/66') === -1; + } catch (e) { /* empty */ } +}(); - if (doOpen) { +// helpers +var isThenable = function (it) { + var then; + return isObject(it) && typeof (then = it.then) == 'function' ? then : false; +}; +var notify = function (promise, isReject) { + if (promise._n) return; + promise._n = true; + var chain = promise._c; + microtask(function () { + var value = promise._v; + var ok = promise._s == 1; + var i = 0; + var run = function (reaction) { + var handler = ok ? reaction.ok : reaction.fail; + var resolve = reaction.resolve; + var reject = reaction.reject; + var domain = reaction.domain; + var result, then, exited; try { - openfd = yield open(dest, 'a', data.mode); - } catch (er) { - // file is likely read-only + if (handler) { + if (!ok) { + if (promise._h == 2) onHandleUnhandled(promise); + promise._h = 1; + } + if (handler === true) result = value; + else { + if (domain) domain.enter(); + result = handler(value); // may throw + if (domain) { + domain.exit(); + exited = true; + } + } + if (result === reaction.promise) { + reject(TypeError('Promise-chain cycle')); + } else if (then = isThenable(result)) { + then.call(result, resolve, reject); + } else resolve(result); + } else reject(value); + } catch (e) { + if (domain && !exited) domain.exit(); + reject(e); + } + }; + while (chain.length > i) run(chain[i++]); // variable length - can't use forEach + promise._c = []; + promise._n = false; + if (isReject && !promise._h) onUnhandled(promise); + }); +}; +var onUnhandled = function (promise) { + task.call(global, function () { + var value = promise._v; + var unhandled = isUnhandled(promise); + var result, handler, console; + if (unhandled) { + result = perform(function () { + if (isNode) { + process.emit('unhandledRejection', value, promise); + } else if (handler = global.onunhandledrejection) { + handler({ promise: promise, reason: value }); + } else if ((console = global.console) && console.error) { + console.error('Unhandled promise rejection', value); + } + }); + // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should + promise._h = isNode || isUnhandled(promise) ? 2 : 1; + } promise._a = undefined; + if (unhandled && result.e) throw result.v; + }); +}; +var isUnhandled = function (promise) { + return promise._h !== 1 && (promise._a || promise._c).length === 0; +}; +var onHandleUnhandled = function (promise) { + task.call(global, function () { + var handler; + if (isNode) { + process.emit('rejectionHandled', promise); + } else if (handler = global.onrejectionhandled) { + handler({ promise: promise, reason: promise._v }); + } + }); +}; +var $reject = function (value) { + var promise = this; + if (promise._d) return; + promise._d = true; + promise = promise._w || promise; // unwrap + promise._v = value; + promise._s = 2; + if (!promise._a) promise._a = promise._c.slice(); + notify(promise, true); +}; +var $resolve = function (value) { + var promise = this; + var then; + if (promise._d) return; + promise._d = true; + promise = promise._w || promise; // unwrap + try { + if (promise === value) throw TypeError("Promise can't be resolved itself"); + if (then = isThenable(value)) { + microtask(function () { + var wrapper = { _w: promise, _d: false }; // wrap try { - openfd = yield open(dest, 'r', data.mode); - } catch (err) { - // We can't even open this file for reading. - return; + then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); + } catch (e) { + $reject.call(wrapper, e); } - } + }); + } else { + promise._v = value; + promise._s = 1; + notify(promise, false); } + } catch (e) { + $reject.call({ _w: promise, _d: false }, e); // wrap + } +}; +// constructor polyfill +if (!USE_NATIVE) { + // 25.4.3.1 Promise(executor) + $Promise = function Promise(executor) { + anInstance(this, $Promise, PROMISE, '_h'); + aFunction(executor); + Internal.call(this); try { - if (openfd) { - yield futimes(openfd, data.atime, data.mtime); - } - } catch (er) { - // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. - // In this case we can just return. The incorrect timestamp will just cause that file to be recopied - // on subsequent installs, which will effect yarn performance but not break anything. - } finally { - if (doOpen && openfd) { - yield close(openfd); - } + executor(ctx($resolve, this, 1), ctx($reject, this, 1)); + } catch (err) { + $reject.call(this, err); + } + }; + // eslint-disable-next-line no-unused-vars + Internal = function Promise(executor) { + this._c = []; // <- awaiting reactions + this._a = undefined; // <- checked in isUnhandled reactions + this._s = 0; // <- state + this._d = false; // <- done + this._v = undefined; // <- value + this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled + this._n = false; // <- notify + }; + Internal.prototype = __webpack_require__(196)($Promise.prototype, { + // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) + then: function then(onFulfilled, onRejected) { + var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); + reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; + reaction.fail = typeof onRejected == 'function' && onRejected; + reaction.domain = isNode ? process.domain : undefined; + this._c.push(reaction); + if (this._a) this._a.push(reaction); + if (this._s) notify(this, false); + return reaction.promise; + }, + // 25.4.5.1 Promise.prototype.catch(onRejected) + 'catch': function (onRejected) { + return this.then(undefined, onRejected); } }); - - return function fixTimes(_x7, _x8, _x9) { - return _ref3.apply(this, arguments); + OwnPromiseCapability = function () { + var promise = new Internal(); + this.promise = promise; + this.resolve = ctx($resolve, promise, 1); + this.reject = ctx($reject, promise, 1); + }; + newPromiseCapabilityModule.f = newPromiseCapability = function (C) { + return C === $Promise || C === Wrapper + ? new OwnPromiseCapability(C) + : newGenericPromiseCapability(C); }; -})(); - -// Compare file timestamps. -// Some versions of Node on windows zero the milliseconds when utime is used. - - -var _fs; - -function _load_fs() { - return _fs = _interopRequireDefault(__webpack_require__(3)); } -var _promise; +$export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise }); +__webpack_require__(71)($Promise, PROMISE); +__webpack_require__(198)(PROMISE); +Wrapper = __webpack_require__(23)[PROMISE]; -function _load_promise() { - return _promise = __webpack_require__(40); -} +// statics +$export($export.S + $export.F * !USE_NATIVE, PROMISE, { + // 25.4.4.5 Promise.reject(r) + reject: function reject(r) { + var capability = newPromiseCapability(this); + var $$reject = capability.reject; + $$reject(r); + return capability.promise; + } +}); +$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { + // 25.4.4.6 Promise.resolve(x) + resolve: function resolve(x) { + return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x); + } +}); +$export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(189)(function (iter) { + $Promise.all(iter)['catch'](empty); +})), PROMISE, { + // 25.4.4.1 Promise.all(iterable) + all: function all(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var resolve = capability.resolve; + var reject = capability.reject; + var result = perform(function () { + var values = []; + var index = 0; + var remaining = 1; + forOf(iterable, false, function (promise) { + var $index = index++; + var alreadyCalled = false; + values.push(undefined); + remaining++; + C.resolve(promise).then(function (value) { + if (alreadyCalled) return; + alreadyCalled = true; + values[$index] = value; + --remaining || resolve(values); + }, reject); + }); + --remaining || resolve(values); + }); + if (result.e) reject(result.v); + return capability.promise; + }, + // 25.4.4.4 Promise.race(iterable) + race: function race(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var reject = capability.reject; + var result = perform(function () { + forOf(iterable, false, function (promise) { + C.resolve(promise).then(capability.resolve, reject); + }); + }); + if (result.e) reject(result.v); + return capability.promise; + } +}); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -// This module serves as a wrapper for file operations that are inconsistant across node and OS versions. +/***/ }), +/* 207 */ +/***/ (function(module, exports, __webpack_require__) { -let disableTimestampCorrection = undefined; // OS dependent. will be detected on first file copy. +"use strict"; -const readFileBuffer = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.readFile); -const close = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.close); -const lstat = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.lstat); -const open = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.open); -const futimes = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.futimes); +var $at = __webpack_require__(199)(true); -const write = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.write); +// 21.1.3.27 String.prototype[@@iterator]() +__webpack_require__(103)(String, 'String', function (iterated) { + this._t = String(iterated); // target + this._i = 0; // next index +// 21.1.5.2.1 %StringIteratorPrototype%.next() +}, function () { + var O = this._t; + var index = this._i; + var point; + if (index >= O.length) return { value: undefined, done: true }; + point = $at(O, index); + this._i += point.length; + return { value: point, done: false }; +}); -const unlink = exports.unlink = (0, (_promise || _load_promise()).promisify)(__webpack_require__(233)); -/** - * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems - * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). - */ -const copyFile = exports.copyFile = (() => { - var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data, cleanup) { - try { - yield unlink(data.dest); - yield copyFilePoly(data.src, data.dest, 0, data); - } finally { - if (cleanup) { - cleanup(); - } - } - }); +/***/ }), +/* 208 */ +/***/ (function(module, exports, __webpack_require__) { - return function copyFile(_x, _x2) { - return _ref.apply(this, arguments); - }; -})(); +"use strict"; +// https://github.com/tc39/proposal-promise-finally -// Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. -// Otherwise we fall back to reading and writing files as buffers. -const copyFilePoly = (src, dest, flags, data) => { - if ((_fs || _load_fs()).default.copyFile) { - return new Promise((resolve, reject) => (_fs || _load_fs()).default.copyFile(src, dest, flags, err => { - if (err) { - reject(err); - } else { - fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); - } - })); - } else { - return copyWithBuffer(src, dest, flags, data); - } -}; +var $export = __webpack_require__(41); +var core = __webpack_require__(23); +var global = __webpack_require__(11); +var speciesConstructor = __webpack_require__(108); +var promiseResolve = __webpack_require__(105); -const copyWithBuffer = (() => { - var _ref2 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest, flags, data) { - // Use open -> write -> futimes -> close sequence to avoid opening the file twice: - // one with writeFile and one with utimes - const fd = yield open(dest, 'w', data.mode); - try { - const buffer = yield readFileBuffer(src); - yield write(fd, buffer, 0, buffer.length); - yield fixTimes(fd, dest, data); - } finally { - yield close(fd); - } - }); +$export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) { + var C = speciesConstructor(this, core.Promise || global.Promise); + var isFunction = typeof onFinally == 'function'; + return this.then( + isFunction ? function (x) { + return promiseResolve(C, onFinally()).then(function () { return x; }); + } : onFinally, + isFunction ? function (e) { + return promiseResolve(C, onFinally()).then(function () { throw e; }); + } : onFinally + ); +} }); - return function copyWithBuffer(_x3, _x4, _x5, _x6) { - return _ref2.apply(this, arguments); - }; -})();const fileDatesEqual = exports.fileDatesEqual = (a, b) => { - const aTime = a.getTime(); - const bTime = b.getTime(); - if (process.platform !== 'win32') { - return aTime === bTime; - } +/***/ }), +/* 209 */ +/***/ (function(module, exports, __webpack_require__) { - // See https://github.com/nodejs/node/pull/12607 - // Submillisecond times from stat and utimes are truncated on Windows, - // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 - // and making it impossible to update these files to their correct timestamps. - if (Math.abs(aTime - bTime) <= 1) { - return true; - } +"use strict"; - const aTimeSec = Math.floor(aTime / 1000); - const bTimeSec = Math.floor(bTime / 1000); +// https://github.com/tc39/proposal-promise-try +var $export = __webpack_require__(41); +var newPromiseCapability = __webpack_require__(70); +var perform = __webpack_require__(104); - // See https://github.com/nodejs/node/issues/2069 - // Some versions of Node on windows zero the milliseconds when utime is used - // So if any of the time has a milliseconds part of zero we suspect that the - // bug is present and compare only seconds. - if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { - return aTimeSec === bTimeSec; - } +$export($export.S, 'Promise', { 'try': function (callbackfn) { + var promiseCapability = newPromiseCapability.f(this); + var result = perform(callbackfn); + (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v); + return promiseCapability.promise; +} }); - return aTime === bTime; -}; /***/ }), -/* 165 */, -/* 166 */, -/* 167 */, -/* 168 */, -/* 169 */ +/* 210 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +__webpack_require__(204); +var global = __webpack_require__(11); +var hide = __webpack_require__(31); +var Iterators = __webpack_require__(35); +var TO_STRING_TAG = __webpack_require__(13)('toStringTag'); -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isFakeRoot = isFakeRoot; -exports.isRootUser = isRootUser; -function getUid() { - if (process.platform !== 'win32' && process.getuid) { - return process.getuid(); - } - return null; -} +var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + + 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + + 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + + 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + + 'TextTrackList,TouchList').split(','); -exports.default = isRootUser(getUid()) && !isFakeRoot(); -function isFakeRoot() { - return Boolean(process.env.FAKEROOTKEY); +for (var i = 0; i < DOMIterables.length; i++) { + var NAME = DOMIterables[i]; + var Collection = global[NAME]; + var proto = Collection && Collection.prototype; + if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); + Iterators[NAME] = Iterators.Array; } -function isRootUser(uid) { - return uid === 0; -} /***/ }), -/* 170 */, -/* 171 */ +/* 211 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ +exports = module.exports = __webpack_require__(112); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getDataDir = getDataDir; -exports.getCacheDir = getCacheDir; -exports.getConfigDir = getConfigDir; -const path = __webpack_require__(0); -const userHome = __webpack_require__(45).default; +/** + * Colors. + */ -const FALLBACK_CONFIG_DIR = path.join(userHome, '.config', 'yarn'); -const FALLBACK_CACHE_DIR = path.join(userHome, '.cache', 'yarn'); +exports.colors = [ + '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', + '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', + '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', + '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', + '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', + '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', + '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', + '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', + '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', + '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', + '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' +]; -function getDataDir() { - if (process.platform === 'win32') { - const WIN32_APPDATA_DIR = getLocalAppDataDir(); - return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Data'); - } else if (process.env.XDG_DATA_HOME) { - return path.join(process.env.XDG_DATA_HOME, 'yarn'); - } else { - // This could arguably be ~/Library/Application Support/Yarn on Macs, - // but that feels unintuitive for a cli tool +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ - // Instead, use our prior fallback. Some day this could be - // path.join(userHome, '.local', 'share', 'yarn') - // or return path.join(WIN32_APPDATA_DIR, 'Data') on win32 - return FALLBACK_CONFIG_DIR; +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; } -} -function getCacheDir() { - if (process.platform === 'win32') { - // process.env.TEMP also exists, but most apps put caches here - return path.join(getLocalAppDataDir() || path.join(userHome, 'AppData', 'Local', 'Yarn'), 'Cache'); - } else if (process.env.XDG_CACHE_HOME) { - return path.join(process.env.XDG_CACHE_HOME, 'yarn'); - } else if (process.platform === 'darwin') { - return path.join(userHome, 'Library', 'Caches', 'Yarn'); - } else { - return FALLBACK_CACHE_DIR; + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; } + + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } -function getConfigDir() { - if (process.platform === 'win32') { - // Use our prior fallback. Some day this could be - // return path.join(WIN32_APPDATA_DIR, 'Config') - const WIN32_APPDATA_DIR = getLocalAppDataDir(); - return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Config'); - } else if (process.env.XDG_CONFIG_HOME) { - return path.join(process.env.XDG_CONFIG_HOME, 'yarn'); - } else { - return FALLBACK_CONFIG_DIR; +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; } -} +}; -function getLocalAppDataDir() { - return process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Yarn') : null; -} -/***/ }), -/* 172 */, -/* 173 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Colorize log arguments if enabled. + * + * @api public + */ -module.exports = { "default": __webpack_require__(179), __esModule: true }; +function formatArgs(args) { + var useColors = this.useColors; -/***/ }), -/* 174 */ -/***/ (function(module, exports, __webpack_require__) { + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); -"use strict"; + if (!useColors) return; -module.exports = balanced; -function balanced(a, b, str) { - if (a instanceof RegExp) a = maybeMatch(a, str); - if (b instanceof RegExp) b = maybeMatch(b, str); + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit') - var r = range(a, b, str); + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); - return r && { - start: r[0], - end: r[1], - pre: str.slice(0, r[0]), - body: str.slice(r[0] + a.length, r[1]), - post: str.slice(r[1] + b.length) - }; + args.splice(lastC, 0, c); } -function maybeMatch(reg, str) { - var m = str.match(reg); - return m ? m[0] : null; -} +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ -balanced.range = range; -function range(a, b, str) { - var begs, beg, left, right, result; - var ai = str.indexOf(a); - var bi = str.indexOf(b, ai + 1); - var i = ai; +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} - if (ai >= 0 && bi > 0) { - begs = []; - left = str.length; +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ - while (i >= 0 && !result) { - if (i == ai) { - begs.push(i); - ai = str.indexOf(a, i + 1); - } else if (begs.length == 1) { - result = [ begs.pop(), bi ]; - } else { - beg = begs.pop(); - if (beg < left) { - left = beg; - right = bi; - } +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} - bi = str.indexOf(b, i + 1); - } +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ - i = ai < bi && ai >= 0 ? ai : bi; - } +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} - if (begs.length) { - result = [ left, right ]; - } + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; } - return result; + return r; } +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ -/***/ }), -/* 175 */ -/***/ (function(module, exports, __webpack_require__) { +exports.enable(load()); -var concatMap = __webpack_require__(178); -var balanced = __webpack_require__(174); +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ -module.exports = expandTop; +function localstorage() { + try { + return window.localStorage; + } catch (e) {} +} -var escSlash = '\0SLASH'+Math.random()+'\0'; -var escOpen = '\0OPEN'+Math.random()+'\0'; -var escClose = '\0CLOSE'+Math.random()+'\0'; -var escComma = '\0COMMA'+Math.random()+'\0'; -var escPeriod = '\0PERIOD'+Math.random()+'\0'; -function numeric(str) { - return parseInt(str, 10) == str - ? parseInt(str, 10) - : str.charCodeAt(0); -} +/***/ }), +/* 212 */ +/***/ (function(module, exports, __webpack_require__) { -function escapeBraces(str) { - return str.split('\\\\').join(escSlash) - .split('\\{').join(escOpen) - .split('\\}').join(escClose) - .split('\\,').join(escComma) - .split('\\.').join(escPeriod); -} +/** + * Detect Electron renderer process, which is node, but we should + * treat as a browser. + */ -function unescapeBraces(str) { - return str.split(escSlash).join('\\') - .split(escOpen).join('{') - .split(escClose).join('}') - .split(escComma).join(',') - .split(escPeriod).join('.'); +if (typeof process === 'undefined' || process.type === 'renderer') { + module.exports = __webpack_require__(211); +} else { + module.exports = __webpack_require__(213); } -// Basically just str.split(","), but handling cases -// where we have nested braced sections, which should be -// treated as individual members, like {a,{b,c},d} -function parseCommaParts(str) { - if (!str) - return ['']; - - var parts = []; - var m = balanced('{', '}', str); +/***/ }), +/* 213 */ +/***/ (function(module, exports, __webpack_require__) { - if (!m) - return str.split(','); +/** + * Module dependencies. + */ - var pre = m.pre; - var body = m.body; - var post = m.post; - var p = pre.split(','); +var tty = __webpack_require__(79); +var util = __webpack_require__(2); - p[p.length-1] += '{' + body + '}'; - var postParts = parseCommaParts(post); - if (post.length) { - p[p.length-1] += postParts.shift(); - p.push.apply(p, postParts); - } +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ - parts.push.apply(parts, p); +exports = module.exports = __webpack_require__(112); +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; - return parts; -} +/** + * Colors. + */ -function expandTop(str) { - if (!str) - return []; +exports.colors = [ 6, 2, 3, 4, 5, 1 ]; - // I don't know why Bash 4.3 does this, but it does. - // Anything starting with {} will have the first two bytes preserved - // but *only* at the top level, so {},a}b will not expand to anything, - // but a{},b}c will be expanded to [a}c,abc]. - // One could argue that this is a bug in Bash, but since the goal of - // this module is to match Bash's rules, we escape a leading {} - if (str.substr(0, 2) === '{}') { - str = '\\{\\}' + str.substr(2); +try { + var supportsColor = __webpack_require__(239); + if (supportsColor && supportsColor.level >= 2) { + exports.colors = [ + 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, + 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, + 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 214, 215, 220, 221 + ]; } - - return expand(escapeBraces(str), true).map(unescapeBraces); +} catch (err) { + // swallow - we only care if `supports-color` is available; it doesn't have to be. } -function identity(e) { - return e; -} +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ -function embrace(str) { - return '{' + str + '}'; -} -function isPadded(el) { - return /^-?0\d/.test(el); -} +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // camel-case + var prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); -function lte(i, y) { - return i <= y; -} -function gte(i, y) { - return i >= y; -} + // coerce string value into JS value + var val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) val = true; + else if (/^(no|off|false|disabled)$/i.test(val)) val = false; + else if (val === 'null') val = null; + else val = Number(val); -function expand(str, isTop) { - var expansions = []; + obj[prop] = val; + return obj; +}, {}); - var m = balanced('{', '}', str); - if (!m || /\$$/.test(m.pre)) return [str]; +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ - var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); - var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); - var isSequence = isNumericSequence || isAlphaSequence; - var isOptions = m.body.indexOf(',') >= 0; - if (!isSequence && !isOptions) { - // {a},b} - if (m.post.match(/,.*\}/)) { - str = m.pre + '{' + m.body + escClose + m.post; - return expand(str); - } - return [str]; - } +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(process.stderr.fd); +} - var n; - if (isSequence) { - n = m.body.split(/\.\./); - } else { - n = parseCommaParts(m.body); - if (n.length === 1) { - // x{{a,b}}y ==> x{a}y x{b}y - n = expand(n[0], false).map(embrace); - if (n.length === 1) { - var post = m.post.length - ? expand(m.post, false) - : ['']; - return post.map(function(p) { - return m.pre + n[0] + p; - }); - } - } - } +/** + * Map %o to `util.inspect()`, all on a single line. + */ - // at this point, n is the parts, and we know it's not a comma set - // with a single entry. +exports.formatters.o = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .split('\n').map(function(str) { + return str.trim() + }).join(' '); +}; - // no need to expand pre, since it is guaranteed to be free of brace-sets - var pre = m.pre; - var post = m.post.length - ? expand(m.post, false) - : ['']; +/** + * Map %o to `util.inspect()`, allowing multiple lines if needed. + */ - var N; +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; - if (isSequence) { - var x = numeric(n[0]); - var y = numeric(n[1]); - var width = Math.max(n[0].length, n[1].length) - var incr = n.length == 3 - ? Math.abs(numeric(n[2])) - : 1; - var test = lte; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte; - } - var pad = n.some(isPadded); +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ - N = []; +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; - for (var i = x; test(i, y); i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === '\\') - c = ''; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join('0'); - if (i < 0) - c = '-' + z + c.slice(1); - else - c = z + c; - } - } - } - N.push(c); - } + if (useColors) { + var c = this.color; + var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); + var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); } else { - N = concatMap(n, function(el) { return expand(el, false) }); + args[0] = getDate() + name + ' ' + args[0]; } +} - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); - } +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } else { + return new Date().toISOString() + ' '; } - - return expansions; } +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ +function log() { + return process.stderr.write(util.format.apply(util, arguments) + '\n'); +} -/***/ }), -/* 176 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ -"use strict"; +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ -function preserveCamelCase(str) { - let isLastCharLower = false; - let isLastCharUpper = false; - let isLastLastCharUpper = false; +function load() { + return process.env.DEBUG; +} - for (let i = 0; i < str.length; i++) { - const c = str[i]; +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ - if (isLastCharLower && /[a-zA-Z]/.test(c) && c.toUpperCase() === c) { - str = str.substr(0, i) + '-' + str.substr(i); - isLastCharLower = false; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = true; - i++; - } else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(c) && c.toLowerCase() === c) { - str = str.substr(0, i - 1) + '-' + str.substr(i - 1); - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = false; - isLastCharLower = true; - } else { - isLastCharLower = c.toLowerCase() === c; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = c.toUpperCase() === c; - } - } +function init (debug) { + debug.inspectOpts = {}; - return str; + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } } -module.exports = function (str) { - if (arguments.length > 1) { - str = Array.from(arguments) - .map(x => x.trim()) - .filter(x => x.length) - .join('-'); - } else { - str = str.trim(); - } - - if (str.length === 0) { - return ''; - } +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ - if (str.length === 1) { - return str.toLowerCase(); - } +exports.enable(load()); - if (/^[a-z0-9]+$/.test(str)) { - return str; - } - const hasUpperCase = str !== str.toLowerCase(); +/***/ }), +/* 214 */, +/* 215 */, +/* 216 */, +/* 217 */ +/***/ (function(module, exports, __webpack_require__) { - if (hasUpperCase) { - str = preserveCamelCase(str); - } +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. - return str - .replace(/^[_.\- ]+/, '') - .toLowerCase() - .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); -}; +var pathModule = __webpack_require__(0); +var isWindows = process.platform === 'win32'; +var fs = __webpack_require__(3); +// JavaScript implementation of realpath, ported from node pre-v6 -/***/ }), -/* 177 */, -/* 178 */ -/***/ (function(module, exports) { +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); -module.exports = function (xs, fn) { - var res = []; - for (var i = 0; i < xs.length; i++) { - var x = fn(xs[i], i); - if (isArray(x)) res.push.apply(res, x); - else res.push(x); - } - return res; -}; +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + var callback; + if (DEBUG) { + var backtrace = new Error; + callback = debugCallback; + } else + callback = missingCallback; -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; + return callback; + function debugCallback(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + missingCallback(err); + } + } -/***/ }), -/* 179 */ -/***/ (function(module, exports, __webpack_require__) { + function missingCallback(err) { + if (err) { + if (process.throwDeprecation) + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + else if (!process.noDeprecation) { + var msg = 'fs: missing callback ' + (err.stack || err.message); + if (process.traceDeprecation) + console.trace(msg); + else + console.error(msg); + } + } + } +} -__webpack_require__(205); -__webpack_require__(207); -__webpack_require__(210); -__webpack_require__(206); -__webpack_require__(208); -__webpack_require__(209); -module.exports = __webpack_require__(23).Promise; +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} +var normalize = pathModule.normalize; -/***/ }), -/* 180 */ -/***/ (function(module, exports) { +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} -module.exports = function () { /* empty */ }; +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} +exports.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); -/***/ }), -/* 181 */ -/***/ (function(module, exports) { + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; + } -module.exports = function (it, Constructor, name, forbiddenField) { - if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) { - throw TypeError(name + ': incorrect invocation!'); - } return it; -}; + var original = p, + seenLinks = {}, + knownHard = {}; + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; -/***/ }), -/* 182 */ -/***/ (function(module, exports, __webpack_require__) { + start(); -// false -> Array#indexOf -// true -> Array#includes -var toIObject = __webpack_require__(74); -var toLength = __webpack_require__(110); -var toAbsoluteIndex = __webpack_require__(200); -module.exports = function (IS_INCLUDES) { - return function ($this, el, fromIndex) { - var O = toIObject($this); - var length = toLength(O.length); - var index = toAbsoluteIndex(fromIndex, length); - var value; - // Array#includes uses SameValueZero equality algorithm - // eslint-disable-next-line no-self-compare - if (IS_INCLUDES && el != el) while (length > index) { - value = O[index++]; - // eslint-disable-next-line no-self-compare - if (value != value) return true; - // Array#indexOf ignores holes, Array#includes - not - } else for (;length > index; index++) if (IS_INCLUDES || index in O) { - if (O[index] === el) return IS_INCLUDES || index || 0; - } return !IS_INCLUDES && -1; - }; -}; + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } + } -/***/ }), -/* 183 */ -/***/ (function(module, exports, __webpack_require__) { + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; -var ctx = __webpack_require__(48); -var call = __webpack_require__(187); -var isArrayIter = __webpack_require__(186); -var anObject = __webpack_require__(27); -var toLength = __webpack_require__(110); -var getIterFn = __webpack_require__(203); -var BREAK = {}; -var RETURN = {}; -var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) { - var iterFn = ITERATOR ? function () { return iterable; } : getIterFn(iterable); - var f = ctx(fn, that, entries ? 2 : 1); - var index = 0; - var length, step, iterator, result; - if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!'); - // fast case for arrays with default iterator - if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) { - result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); - if (result === BREAK || result === RETURN) return result; - } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) { - result = call(iterator, f, step.value, entries); - if (result === BREAK || result === RETURN) return result; + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } + + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; + } else { + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } + + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); + } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; + } + + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); } + + if (cache) cache[original] = p; + + return p; }; -exports.BREAK = BREAK; -exports.RETURN = RETURN; -/***/ }), -/* 184 */ -/***/ (function(module, exports, __webpack_require__) { +exports.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; + } -module.exports = !__webpack_require__(33) && !__webpack_require__(85)(function () { - return Object.defineProperty(__webpack_require__(68)('div'), 'a', { get: function () { return 7; } }).a != 7; -}); + // make p is absolute + p = pathModule.resolve(p); + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); + } -/***/ }), -/* 185 */ -/***/ (function(module, exports) { + var original = p, + seenLinks = {}, + knownHard = {}; -// fast apply, http://jsperf.lnkit.com/fast-apply/5 -module.exports = function (fn, args, that) { - var un = that === undefined; - switch (args.length) { - case 0: return un ? fn() - : fn.call(that); - case 1: return un ? fn(args[0]) - : fn.call(that, args[0]); - case 2: return un ? fn(args[0], args[1]) - : fn.call(that, args[0], args[1]); - case 3: return un ? fn(args[0], args[1], args[2]) - : fn.call(that, args[0], args[1], args[2]); - case 4: return un ? fn(args[0], args[1], args[2], args[3]) - : fn.call(that, args[0], args[1], args[2], args[3]); - } return fn.apply(that, args); -}; + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; + start(); -/***/ }), -/* 186 */ -/***/ (function(module, exports, __webpack_require__) { + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; -// check on default Array iterator -var Iterators = __webpack_require__(35); -var ITERATOR = __webpack_require__(13)('iterator'); -var ArrayProto = Array.prototype; + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } -module.exports = function (it) { - return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); -}; + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; -/***/ }), -/* 187 */ -/***/ (function(module, exports, __webpack_require__) { + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } -// call something on iterator step with safe closing on error -var anObject = __webpack_require__(27); -module.exports = function (iterator, fn, value, entries) { - try { - return entries ? fn(anObject(value)[0], value[1]) : fn(value); - // 7.4.6 IteratorClose(iterator, completion) - } catch (e) { - var ret = iterator['return']; - if (ret !== undefined) anObject(ret.call(iterator)); - throw e; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } + + return fs.lstat(base, gotStat); } -}; + function gotStat(err, stat) { + if (err) return cb(err); -/***/ }), -/* 188 */ -/***/ (function(module, exports, __webpack_require__) { + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } -"use strict"; + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); + } + } + fs.stat(base, function(err) { + if (err) return cb(err); -var create = __webpack_require__(192); -var descriptor = __webpack_require__(106); -var setToStringTag = __webpack_require__(71); -var IteratorPrototype = {}; + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); + } -// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() -__webpack_require__(31)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; }); + function gotTarget(err, target, base) { + if (err) return cb(err); -module.exports = function (Constructor, NAME, next) { - Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); - setToStringTag(Constructor, NAME + ' Iterator'); + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } }; /***/ }), -/* 189 */ +/* 218 */ /***/ (function(module, exports, __webpack_require__) { -var ITERATOR = __webpack_require__(13)('iterator'); -var SAFE_CLOSING = false; +module.exports = globSync +globSync.GlobSync = GlobSync -try { - var riter = [7][ITERATOR](); - riter['return'] = function () { SAFE_CLOSING = true; }; - // eslint-disable-next-line no-throw-literal - Array.from(riter, function () { throw 2; }); -} catch (e) { /* empty */ } +var fs = __webpack_require__(3) +var rp = __webpack_require__(114) +var minimatch = __webpack_require__(60) +var Minimatch = minimatch.Minimatch +var Glob = __webpack_require__(75).Glob +var util = __webpack_require__(2) +var path = __webpack_require__(0) +var assert = __webpack_require__(22) +var isAbsolute = __webpack_require__(76) +var common = __webpack_require__(115) +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored -module.exports = function (exec, skipClosing) { - if (!skipClosing && !SAFE_CLOSING) return false; - var safe = false; - try { - var arr = [7]; - var iter = arr[ITERATOR](); - iter.next = function () { return { done: safe = true }; }; - arr[ITERATOR] = function () { return iter; }; - exec(arr); - } catch (e) { /* empty */ } - return safe; -}; +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + return new GlobSync(pattern, options).found +} -/***/ }), -/* 190 */ -/***/ (function(module, exports) { +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') -module.exports = function (done, value) { - return { value: value, done: !!done }; -}; + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) -/***/ }), -/* 191 */ -/***/ (function(module, exports, __webpack_require__) { + setopts(this, pattern, options) -var global = __webpack_require__(11); -var macrotask = __webpack_require__(109).set; -var Observer = global.MutationObserver || global.WebKitMutationObserver; -var process = global.process; -var Promise = global.Promise; -var isNode = __webpack_require__(47)(process) == 'process'; + if (this.noprocess) + return this -module.exports = function () { - var head, last, notify; + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} - var flush = function () { - var parent, fn; - if (isNode && (parent = process.domain)) parent.exit(); - while (head) { - fn = head.fn; - head = head.next; - try { - fn(); - } catch (e) { - if (head) notify(); - else last = undefined; - throw e; +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = rp.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } } - } last = undefined; - if (parent) parent.enter(); - }; - - // Node.js - if (isNode) { - notify = function () { - process.nextTick(flush); - }; - // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339 - } else if (Observer && !(global.navigator && global.navigator.standalone)) { - var toggle = true; - var node = document.createTextNode(''); - new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new - notify = function () { - node.data = toggle = !toggle; - }; - // environments with maybe non-completely correct, but existent Promise - } else if (Promise && Promise.resolve) { - // Promise.resolve without an argument throws an error in LG WebOS 2 - var promise = Promise.resolve(undefined); - notify = function () { - promise.then(flush); - }; - // for other environments - macrotask based on: - // - setImmediate - // - MessageChannel - // - window.postMessag - // - onreadystatechange - // - setTimeout - } else { - notify = function () { - // strange IE + webpack dev server bug - use .call(global) - macrotask.call(global, flush); - }; + }) } + common.finish(this) +} - return function (fn) { - var task = { fn: fn, next: undefined }; - if (last) last.next = task; - if (!head) { - head = task; - notify(); - } last = task; - }; -}; +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) -/***/ }), -/* 192 */ -/***/ (function(module, exports, __webpack_require__) { + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. -// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) -var anObject = __webpack_require__(27); -var dPs = __webpack_require__(193); -var enumBugKeys = __webpack_require__(101); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); -var Empty = function () { /* empty */ }; -var PROTOTYPE = 'prototype'; + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return -// Create object with fake `null` prototype: use iframe Object with cleared prototype -var createDict = function () { - // Thrash, waste and sodomy: IE GC bug - var iframe = __webpack_require__(68)('iframe'); - var i = enumBugKeys.length; - var lt = '<'; - var gt = '>'; - var iframeDocument; - iframe.style.display = 'none'; - __webpack_require__(102).appendChild(iframe); - iframe.src = 'javascript:'; // eslint-disable-line no-script-url - // createDict = iframe.contentWindow.Object; - // html.removeChild(iframe); - iframeDocument = iframe.contentWindow.document; - iframeDocument.open(); - iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); - iframeDocument.close(); - createDict = iframeDocument.F; - while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; - return createDict(); -}; + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break -module.exports = Object.create || function create(O, Properties) { - var result; - if (O !== null) { - Empty[PROTOTYPE] = anObject(O); - result = new Empty(); - Empty[PROTOTYPE] = null; - // add "__proto__" for Object.getPrototypeOf polyfill - result[IE_PROTO] = O; - } else result = createDict(); - return Properties === undefined ? result : dPs(result, Properties); -}; + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } + var remain = pattern.slice(n) -/***/ }), -/* 193 */ -/***/ (function(module, exports, __webpack_require__) { + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix -var dP = __webpack_require__(50); -var anObject = __webpack_require__(27); -var getKeys = __webpack_require__(132); + var abs = this._makeAbs(read) -module.exports = __webpack_require__(33) ? Object.defineProperties : function defineProperties(O, Properties) { - anObject(O); - var keys = getKeys(Properties); - var length = keys.length; - var i = 0; - var P; - while (length > i) dP.f(O, P = keys[i++], Properties[P]); - return O; -}; + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} -/***/ }), -/* 194 */ -/***/ (function(module, exports, __webpack_require__) { -// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) -var has = __webpack_require__(49); -var toObject = __webpack_require__(133); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); -var ObjectProto = Object.prototype; +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) -module.exports = Object.getPrototypeOf || function (O) { - O = toObject(O); - if (has(O, IE_PROTO)) return O[IE_PROTO]; - if (typeof O.constructor == 'function' && O instanceof O.constructor) { - return O.constructor.prototype; - } return O instanceof Object ? ObjectProto : null; -}; + // if the abs isn't a dir, then nothing can match! + if (!entries) + return + + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } -/***/ }), -/* 195 */ -/***/ (function(module, exports, __webpack_require__) { + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return -var has = __webpack_require__(49); -var toIObject = __webpack_require__(74); -var arrayIndexOf = __webpack_require__(182)(false); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. -module.exports = function (object, names) { - var O = toIObject(object); - var i = 0; - var result = []; - var key; - for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); - // Don't enum bug & hidden keys - while (names.length > i) if (has(O, key = names[i++])) { - ~arrayIndexOf(result, key) || result.push(key); + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) + + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return } - return result; -}; + + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} -/***/ }), -/* 196 */ -/***/ (function(module, exports, __webpack_require__) { +GlobSync.prototype._emitMatch = function (index, e) { + if (isIgnored(this, e)) + return -var hide = __webpack_require__(31); -module.exports = function (target, src, safe) { - for (var key in src) { - if (safe && target[key]) target[key] = src[key]; - else hide(target, key, src[key]); - } return target; -}; + var abs = this._makeAbs(e) + if (this.mark) + e = this._mark(e) -/***/ }), -/* 197 */ -/***/ (function(module, exports, __webpack_require__) { + if (this.absolute) { + e = abs + } -module.exports = __webpack_require__(31); + if (this.matches[index][e]) + return + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } -/***/ }), -/* 198 */ -/***/ (function(module, exports, __webpack_require__) { + this.matches[index][e] = true -"use strict"; + if (this.stat) + this._stat(e) +} -var global = __webpack_require__(11); -var core = __webpack_require__(23); -var dP = __webpack_require__(50); -var DESCRIPTORS = __webpack_require__(33); -var SPECIES = __webpack_require__(13)('species'); -module.exports = function (KEY) { - var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; - if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, { - configurable: true, - get: function () { return this; } - }); -}; +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) + var entries + var lstat + var stat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + if (er.code === 'ENOENT') { + // lstat failed, doesn't exist + return null + } + } -/***/ }), -/* 199 */ -/***/ (function(module, exports, __webpack_require__) { + var isSym = lstat && lstat.isSymbolicLink() + this.symlinks[abs] = isSym -var toInteger = __webpack_require__(73); -var defined = __webpack_require__(67); -// true -> String#at -// false -> String#codePointAt -module.exports = function (TO_STRING) { - return function (that, pos) { - var s = String(defined(that)); - var i = toInteger(pos); - var l = s.length; - var a, b; - if (i < 0 || i >= l) return TO_STRING ? '' : undefined; - a = s.charCodeAt(i); - return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff - ? TO_STRING ? s.charAt(i) : a - : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; - }; -}; + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) + return entries +} -/***/ }), -/* 200 */ -/***/ (function(module, exports, __webpack_require__) { +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries -var toInteger = __webpack_require__(73); -var max = Math.max; -var min = Math.min; -module.exports = function (index, length) { - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); -}; + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null -/***/ }), -/* 201 */ -/***/ (function(module, exports, __webpack_require__) { + if (Array.isArray(c)) + return c + } -// 7.1.1 ToPrimitive(input [, PreferredType]) -var isObject = __webpack_require__(34); -// instead of the ES6 spec version, we didn't implement @@toPrimitive case -// and the second argument - flag - preferred type is a string -module.exports = function (it, S) { - if (!isObject(it)) return it; - var fn, val; - if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; - if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - throw TypeError("Can't convert object to primitive value"); -}; + try { + return this._readdirEntries(abs, fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} + +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } + this.cache[abs] = entries -/***/ }), -/* 202 */ -/***/ (function(module, exports, __webpack_require__) { + // mark and cache dir-ness + return entries +} -var global = __webpack_require__(11); -var navigator = global.navigator; +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + throw error + } + break -module.exports = navigator && navigator.userAgent || ''; + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} -/***/ }), -/* 203 */ -/***/ (function(module, exports, __webpack_require__) { +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { -var classof = __webpack_require__(100); -var ITERATOR = __webpack_require__(13)('iterator'); -var Iterators = __webpack_require__(35); -module.exports = __webpack_require__(23).getIteratorMethod = function (it) { - if (it != undefined) return it[ITERATOR] - || it['@@iterator'] - || Iterators[classof(it)]; -}; + var entries = this._readdir(abs, inGlobStar) + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return -/***/ }), -/* 204 */ -/***/ (function(module, exports, __webpack_require__) { + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) -"use strict"; + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) -var addToUnscopables = __webpack_require__(180); -var step = __webpack_require__(190); -var Iterators = __webpack_require__(35); -var toIObject = __webpack_require__(74); + var len = entries.length + var isSym = this.symlinks[abs] -// 22.1.3.4 Array.prototype.entries() -// 22.1.3.13 Array.prototype.keys() -// 22.1.3.29 Array.prototype.values() -// 22.1.3.30 Array.prototype[@@iterator]() -module.exports = __webpack_require__(103)(Array, 'Array', function (iterated, kind) { - this._t = toIObject(iterated); // target - this._i = 0; // next index - this._k = kind; // kind -// 22.1.5.2.1 %ArrayIteratorPrototype%.next() -}, function () { - var O = this._t; - var kind = this._k; - var index = this._i++; - if (!O || index >= O.length) { - this._t = undefined; - return step(1); + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) } - if (kind == 'keys') return step(0, index); - if (kind == 'values') return step(0, O[index]); - return step(0, [index, O[index]]); -}, 'values'); +} -// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) -Iterators.Arguments = Iterators.Array; +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) -addToUnscopables('keys'); -addToUnscopables('values'); -addToUnscopables('entries'); + if (!this.matches[index]) + this.matches[index] = Object.create(null) + // If it doesn't exist, then just mark the lack of results + if (!exists) + return -/***/ }), -/* 205 */ -/***/ (function(module, exports) { + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + // Mark this as a match + this._emitMatch(index, prefix) +} -/***/ }), -/* 206 */ -/***/ (function(module, exports, __webpack_require__) { +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' -"use strict"; + if (f.length > this.maxLength) + return false -var LIBRARY = __webpack_require__(69); -var global = __webpack_require__(11); -var ctx = __webpack_require__(48); -var classof = __webpack_require__(100); -var $export = __webpack_require__(41); -var isObject = __webpack_require__(34); -var aFunction = __webpack_require__(46); -var anInstance = __webpack_require__(181); -var forOf = __webpack_require__(183); -var speciesConstructor = __webpack_require__(108); -var task = __webpack_require__(109).set; -var microtask = __webpack_require__(191)(); -var newPromiseCapabilityModule = __webpack_require__(70); -var perform = __webpack_require__(104); -var userAgent = __webpack_require__(202); -var promiseResolve = __webpack_require__(105); -var PROMISE = 'Promise'; -var TypeError = global.TypeError; -var process = global.process; -var versions = process && process.versions; -var v8 = versions && versions.v8 || ''; -var $Promise = global[PROMISE]; -var isNode = classof(process) == 'process'; -var empty = function () { /* empty */ }; -var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; -var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] -var USE_NATIVE = !!function () { - try { - // correct subclassing with @@species support - var promise = $Promise.resolve(1); - var FakePromise = (promise.constructor = {})[__webpack_require__(13)('species')] = function (exec) { - exec(empty, empty); - }; - // unhandled rejections tracking support, NodeJS Promise without it fails @@species test - return (isNode || typeof PromiseRejectionEvent == 'function') - && promise.then(empty) instanceof FakePromise - // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables - // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 - // we can't detect it synchronously, so just check versions - && v8.indexOf('6.6') !== 0 - && userAgent.indexOf('Chrome/66') === -1; - } catch (e) { /* empty */ } -}(); + if (Array.isArray(c)) + c = 'DIR' -// helpers -var isThenable = function (it) { - var then; - return isObject(it) && typeof (then = it.then) == 'function' ? then : false; -}; -var notify = function (promise, isReject) { - if (promise._n) return; - promise._n = true; - var chain = promise._c; - microtask(function () { - var value = promise._v; - var ok = promise._s == 1; - var i = 0; - var run = function (reaction) { - var handler = ok ? reaction.ok : reaction.fail; - var resolve = reaction.resolve; - var reject = reaction.reject; - var domain = reaction.domain; - var result, then, exited; - try { - if (handler) { - if (!ok) { - if (promise._h == 2) onHandleUnhandled(promise); - promise._h = 1; - } - if (handler === true) result = value; - else { - if (domain) domain.enter(); - result = handler(value); // may throw - if (domain) { - domain.exit(); - exited = true; - } - } - if (result === reaction.promise) { - reject(TypeError('Promise-chain cycle')); - } else if (then = isThenable(result)) { - then.call(result, resolve, reject); - } else resolve(result); - } else reject(value); - } catch (e) { - if (domain && !exited) domain.exit(); - reject(e); + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return false } - }; - while (chain.length > i) run(chain[i++]); // variable length - can't use forEach - promise._c = []; - promise._n = false; - if (isReject && !promise._h) onUnhandled(promise); - }); -}; -var onUnhandled = function (promise) { - task.call(global, function () { - var value = promise._v; - var unhandled = isUnhandled(promise); - var result, handler, console; - if (unhandled) { - result = perform(function () { - if (isNode) { - process.emit('unhandledRejection', value, promise); - } else if (handler = global.onunhandledrejection) { - handler({ promise: promise, reason: value }); - } else if ((console = global.console) && console.error) { - console.error('Unhandled promise rejection', value); - } - }); - // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should - promise._h = isNode || isUnhandled(promise) ? 2 : 1; - } promise._a = undefined; - if (unhandled && result.e) throw result.v; - }); -}; -var isUnhandled = function (promise) { - return promise._h !== 1 && (promise._a || promise._c).length === 0; -}; -var onHandleUnhandled = function (promise) { - task.call(global, function () { - var handler; - if (isNode) { - process.emit('rejectionHandled', promise); - } else if (handler = global.onrejectionhandled) { - handler({ promise: promise, reason: promise._v }); } - }); -}; -var $reject = function (value) { - var promise = this; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap - promise._v = value; - promise._s = 2; - if (!promise._a) promise._a = promise._c.slice(); - notify(promise, true); -}; -var $resolve = function (value) { - var promise = this; - var then; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap - try { - if (promise === value) throw TypeError("Promise can't be resolved itself"); - if (then = isThenable(value)) { - microtask(function () { - var wrapper = { _w: promise, _d: false }; // wrap - try { - then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); - } catch (e) { - $reject.call(wrapper, e); - } - }); + + if (lstat && lstat.isSymbolicLink()) { + try { + stat = fs.statSync(abs) + } catch (er) { + stat = lstat + } } else { - promise._v = value; - promise._s = 1; - notify(promise, false); + stat = lstat } - } catch (e) { - $reject.call({ _w: promise, _d: false }, e); // wrap } -}; -// constructor polyfill -if (!USE_NATIVE) { - // 25.4.3.1 Promise(executor) - $Promise = function Promise(executor) { - anInstance(this, $Promise, PROMISE, '_h'); - aFunction(executor); - Internal.call(this); - try { - executor(ctx($resolve, this, 1), ctx($reject, this, 1)); - } catch (err) { - $reject.call(this, err); - } - }; - // eslint-disable-next-line no-unused-vars - Internal = function Promise(executor) { - this._c = []; // <- awaiting reactions - this._a = undefined; // <- checked in isUnhandled reactions - this._s = 0; // <- state - this._d = false; // <- done - this._v = undefined; // <- value - this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled - this._n = false; // <- notify - }; - Internal.prototype = __webpack_require__(196)($Promise.prototype, { - // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) - then: function then(onFulfilled, onRejected) { - var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); - reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; - reaction.fail = typeof onRejected == 'function' && onRejected; - reaction.domain = isNode ? process.domain : undefined; - this._c.push(reaction); - if (this._a) this._a.push(reaction); - if (this._s) notify(this, false); - return reaction.promise; - }, - // 25.4.5.1 Promise.prototype.catch(onRejected) - 'catch': function (onRejected) { - return this.then(undefined, onRejected); - } - }); - OwnPromiseCapability = function () { - var promise = new Internal(); - this.promise = promise; - this.resolve = ctx($resolve, promise, 1); - this.reject = ctx($reject, promise, 1); - }; - newPromiseCapabilityModule.f = newPromiseCapability = function (C) { - return C === $Promise || C === Wrapper - ? new OwnPromiseCapability(C) - : newGenericPromiseCapability(C); - }; + this.statCache[abs] = stat + + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + + this.cache[abs] = this.cache[abs] || c + + if (needDir && c === 'FILE') + return false + + return c } -$export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise }); -__webpack_require__(71)($Promise, PROMISE); -__webpack_require__(198)(PROMISE); -Wrapper = __webpack_require__(23)[PROMISE]; +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} -// statics -$export($export.S + $export.F * !USE_NATIVE, PROMISE, { - // 25.4.4.5 Promise.reject(r) - reject: function reject(r) { - var capability = newPromiseCapability(this); - var $$reject = capability.reject; - $$reject(r); - return capability.promise; - } -}); -$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { - // 25.4.4.6 Promise.resolve(x) - resolve: function resolve(x) { - return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x); - } -}); -$export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(189)(function (iter) { - $Promise.all(iter)['catch'](empty); -})), PROMISE, { - // 25.4.4.1 Promise.all(iterable) - all: function all(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var resolve = capability.resolve; - var reject = capability.reject; - var result = perform(function () { - var values = []; - var index = 0; - var remaining = 1; - forOf(iterable, false, function (promise) { - var $index = index++; - var alreadyCalled = false; - values.push(undefined); - remaining++; - C.resolve(promise).then(function (value) { - if (alreadyCalled) return; - alreadyCalled = true; - values[$index] = value; - --remaining || resolve(values); - }, reject); - }); - --remaining || resolve(values); - }); - if (result.e) reject(result.v); - return capability.promise; - }, - // 25.4.4.4 Promise.race(iterable) - race: function race(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var reject = capability.reject; - var result = perform(function () { - forOf(iterable, false, function (promise) { - C.resolve(promise).then(capability.resolve, reject); - }); - }); - if (result.e) reject(result.v); - return capability.promise; - } -}); +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} /***/ }), -/* 207 */ +/* 219 */, +/* 220 */, +/* 221 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var $at = __webpack_require__(199)(true); +module.exports = function (flag, argv) { + argv = argv || process.argv; -// 21.1.3.27 String.prototype[@@iterator]() -__webpack_require__(103)(String, 'String', function (iterated) { - this._t = String(iterated); // target - this._i = 0; // next index -// 21.1.5.2.1 %StringIteratorPrototype%.next() -}, function () { - var O = this._t; - var index = this._i; - var point; - if (index >= O.length) return { value: undefined, done: true }; - point = $at(O, index); - this._i += point.length; - return { value: point, done: false }; -}); + var terminatorPos = argv.indexOf('--'); + var prefix = /^--/.test(flag) ? '' : '--'; + var pos = argv.indexOf(prefix + flag); + + return pos !== -1 && (terminatorPos !== -1 ? pos < terminatorPos : true); +}; /***/ }), -/* 208 */ +/* 222 */, +/* 223 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; -// https://github.com/tc39/proposal-promise-finally +var wrappy = __webpack_require__(123) +var reqs = Object.create(null) +var once = __webpack_require__(61) -var $export = __webpack_require__(41); -var core = __webpack_require__(23); -var global = __webpack_require__(11); -var speciesConstructor = __webpack_require__(108); -var promiseResolve = __webpack_require__(105); +module.exports = wrappy(inflight) -$export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) { - var C = speciesConstructor(this, core.Promise || global.Promise); - var isFunction = typeof onFinally == 'function'; - return this.then( - isFunction ? function (x) { - return promiseResolve(C, onFinally()).then(function () { return x; }); - } : onFinally, - isFunction ? function (e) { - return promiseResolve(C, onFinally()).then(function () { throw e; }); - } : onFinally - ); -} }); +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null + } else { + reqs[key] = [cb] + return makeres(key) + } +} +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) -/***/ }), -/* 209 */ -/***/ (function(module, exports, __webpack_require__) { + // XXX It's somewhat ambiguous whether a new callback added in this + // pass should be queued for later execution if something in the + // list of callbacks throws, or if it should just be discarded. + // However, it's such an edge case that it hardly matters, and either + // choice is likely as surprising as the other. + // As it happens, we do go ahead and schedule it for later execution. + try { + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + } finally { + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + } + }) +} -"use strict"; +function slice (args) { + var length = args.length + var array = [] -// https://github.com/tc39/proposal-promise-try -var $export = __webpack_require__(41); -var newPromiseCapability = __webpack_require__(70); -var perform = __webpack_require__(104); + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} -$export($export.S, 'Promise', { 'try': function (callbackfn) { - var promiseCapability = newPromiseCapability.f(this); - var result = perform(callbackfn); - (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v); - return promiseCapability.promise; -} }); + +/***/ }), +/* 224 */ +/***/ (function(module, exports) { + +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} /***/ }), -/* 210 */ +/* 225 */, +/* 226 */, +/* 227 */ /***/ (function(module, exports, __webpack_require__) { -__webpack_require__(204); -var global = __webpack_require__(11); -var hide = __webpack_require__(31); -var Iterators = __webpack_require__(35); -var TO_STRING_TAG = __webpack_require__(13)('toStringTag'); +// @flow -var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + - 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + - 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + - 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + - 'TextTrackList,TouchList').split(','); +/*:: +declare var __webpack_require__: mixed; +*/ -for (var i = 0; i < DOMIterables.length; i++) { - var NAME = DOMIterables[i]; - var Collection = global[NAME]; - var proto = Collection && Collection.prototype; - if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); - Iterators[NAME] = Iterators.Array; -} +module.exports = typeof __webpack_require__ !== "undefined"; /***/ }), -/* 211 */ -/***/ (function(module, exports, __webpack_require__) { +/* 228 */, +/* 229 */ +/***/ (function(module, exports) { /** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. + * Helpers. */ -exports = module.exports = __webpack_require__(112); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; /** - * Colors. - */ - -exports.colors = [ - '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', - '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', - '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', - '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', - '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', - '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', - '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', - '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', - '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', - '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', - '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. + * Parse or format the given `val`. * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // Internet Explorer and Edge do not support colors. - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } -}; - - -/** - * Colorize log arguments if enabled. + * Options: * - * @api public - */ - -function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); -} - -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". + * - `long` verbose formatting [false] * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} * @api public */ -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; /** - * Save `namespaces`. + * Parse the given `str` and return milliseconds. * - * @param {String} namespaces + * @param {String} str + * @return {Number} * @api private */ -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } } /** - * Load `namespaces`. + * Short format for `ms`. * - * @return {String} returns the previously persisted debug modes + * @param {Number} ms + * @return {String} * @api private */ -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; } - - return r; + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; } /** - * Enable namespaces listed in `localStorage.debug` initially. - */ - -exports.enable(load()); - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. + * Long format for `ms`. * - * @return {LocalStorage} + * @param {Number} ms + * @return {String} * @api private */ -function localstorage() { - try { - return window.localStorage; - } catch (e) {} +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; } - -/***/ }), -/* 212 */ -/***/ (function(module, exports, __webpack_require__) { - /** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. + * Pluralization helper. */ -if (typeof process === 'undefined' || process.type === 'renderer') { - module.exports = __webpack_require__(211); -} else { - module.exports = __webpack_require__(213); +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; } /***/ }), -/* 213 */ +/* 230 */, +/* 231 */, +/* 232 */, +/* 233 */ /***/ (function(module, exports, __webpack_require__) { -/** - * Module dependencies. - */ +module.exports = rimraf +rimraf.sync = rimrafSync -var tty = __webpack_require__(79); -var util = __webpack_require__(2); +var assert = __webpack_require__(22) +var path = __webpack_require__(0) +var fs = __webpack_require__(3) +var glob = __webpack_require__(75) +var _0666 = parseInt('666', 8) -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. - */ +var defaultGlobOpts = { + nosort: true, + silent: true +} -exports = module.exports = __webpack_require__(112); -exports.init = init; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; +// for EMFILE handling +var timeout = 0 -/** - * Colors. - */ +var isWindows = (process.platform === "win32") -exports.colors = [ 6, 2, 3, 4, 5, 1 ]; +function defaults (options) { + var methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(function(m) { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) -try { - var supportsColor = __webpack_require__(239); - if (supportsColor && supportsColor.level >= 2) { - exports.colors = [ - 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, - 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, - 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, - 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, - 205, 206, 207, 208, 209, 214, 215, 220, 221 - ]; + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + if (options.glob === false) { + options.disableGlob = true } -} catch (err) { - // swallow - we only care if `supports-color` is available; it doesn't have to be. + options.disableGlob = options.disableGlob || false + options.glob = options.glob || defaultGlobOpts } -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ +function rimraf (p, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.equal(typeof options, 'object', 'rimraf: options should be object') - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); + defaults(options) - obj[prop] = val; - return obj; -}, {}); + var busyTries = 0 + var errState = null + var n = 0 -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(process.stderr.fd); -} + options.lstat(p, function (er, stat) { + if (!er) + return afterGlob(null, [p]) -/** - * Map %o to `util.inspect()`, all on a single line. - */ + glob(p, options.glob, afterGlob) + }) -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); -}; + function next (er) { + errState = errState || er + if (--n === 0) + cb(errState) + } -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ + function afterGlob (er, results) { + if (er) + return cb(er) -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; + n = results.length + if (n === 0) + return cb() -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ + results.forEach(function (p) { + rimraf_(p, options, function CB (er) { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + var time = busyTries * 100 + // try again, with the same exact callback as this one. + return setTimeout(function () { + rimraf_(p, options, CB) + }, time) + } -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(function () { + rimraf_(p, options, CB) + }, timeout ++) + } - if (useColors) { - var c = this.color; - var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); - var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; + // already gone + if (er.code === "ENOENT") er = null + } - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = getDate() + name + ' ' + args[0]; + timeout = 0 + next(er) + }) + }) } } -function getDate() { - if (exports.inspectOpts.hideDate) { - return ''; - } else { - return new Date().toISOString() + ' '; - } -} +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +function rimraf_ (p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') -/** - * Invokes `util.format()` with the specified arguments and writes to stderr. - */ + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, function (er, st) { + if (er && er.code === "ENOENT") + return cb(null) -function log() { - return process.stderr.write(util.format.apply(util, arguments) + '\n'); -} + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === "EPERM" && isWindows) + fixWinEPERM(p, options, er, cb) -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } -} + options.unlink(p, function (er) { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ +function fixWinEPERM (p, options, er, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + if (er) + assert(er instanceof Error) -function load() { - return process.env.DEBUG; + options.chmod(p, _0666, function (er2) { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, function(er3, stats) { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) } -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ +function fixWinEPERMSync (p, options, er) { + assert(p) + assert(options) + if (er) + assert(er instanceof Error) -function init (debug) { - debug.inspectOpts = {}; + try { + options.chmodSync(p, _0666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + try { + var stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er } + + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) } -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ +function rmdir (p, options, originalEr, cb) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + assert(typeof cb === 'function') -exports.enable(load()); + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, function (er) { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} +function rmkids(p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') -/***/ }), -/* 214 */, -/* 215 */, -/* 216 */, -/* 217 */ -/***/ (function(module, exports, __webpack_require__) { + options.readdir(p, function (er, files) { + if (er) + return cb(er) + var n = files.length + if (n === 0) + return options.rmdir(p, cb) + var errState + files.forEach(function (f) { + rimraf(path.join(p, f), options, function (er) { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) +} -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +function rimrafSync (p, options) { + options = options || {} + defaults(options) -var pathModule = __webpack_require__(0); -var isWindows = process.platform === 'win32'; -var fs = __webpack_require__(3); + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') -// JavaScript implementation of realpath, ported from node pre-v6 + var results -var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + options.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, options.glob) + } + } -function rethrow() { - // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and - // is fairly slow to generate. - var callback; - if (DEBUG) { - var backtrace = new Error; - callback = debugCallback; - } else - callback = missingCallback; + if (!results.length) + return - return callback; + for (var i = 0; i < results.length; i++) { + var p = results[i] - function debugCallback(err) { - if (err) { - backtrace.message = err.message; - err = backtrace; - missingCallback(err); + try { + var st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + + // Windows can EPERM on stat. Life is suffering. + if (er.code === "EPERM" && isWindows) + fixWinEPERMSync(p, options, er) } - } - function missingCallback(err) { - if (err) { - if (process.throwDeprecation) - throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs - else if (!process.noDeprecation) { - var msg = 'fs: missing callback ' + (err.stack || err.message); - if (process.traceDeprecation) - console.trace(msg); - else - console.error(msg); - } + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er + + rmdirSync(p, options, er) } } } -function maybeCallback(cb) { - return typeof cb === 'function' ? cb : rethrow(); +function rmdirSync (p, options, originalEr) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) + } } -var normalize = pathModule.normalize; +function rmkidsSync (p, options) { + assert(p) + assert(options) + options.readdirSync(p).forEach(function (f) { + rimrafSync(path.join(p, f), options) + }) -// Regexp that finds the next partion of a (partial) path -// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] -if (isWindows) { - var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; -} else { - var nextPartRe = /(.*?)(?:[\/]+|$)/g; + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + var retries = isWindows ? 100 : 1 + var i = 0 + do { + var threw = true + try { + var ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) + continue + } + } while (true) } -// Regex to find the device root, including trailing slash. E.g. 'c:\\'. -if (isWindows) { - var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; -} else { - var splitRootRe = /^[\/]*/; -} -exports.realpathSync = function realpathSync(p, cache) { - // make p is absolute - p = pathModule.resolve(p); +/***/ }), +/* 234 */, +/* 235 */, +/* 236 */, +/* 237 */, +/* 238 */, +/* 239 */ +/***/ (function(module, exports, __webpack_require__) { - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return cache[p]; - } +"use strict"; - var original = p, - seenLinks = {}, - knownHard = {}; +var hasFlag = __webpack_require__(221); - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; +var support = function (level) { + if (level === 0) { + return false; + } - start(); + return { + level: level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +}; - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; +var supportLevel = (function () { + if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + return 0; + } - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstatSync(base); - knownHard[base] = true; - } - } + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } - // walk down the path, swapping out linked pathparts for their real - // values - // NB: p.length changes. - while (pos < p.length) { - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; + if (hasFlag('color=256')) { + return 2; + } - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - continue; - } + if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + return 1; + } - var resolvedLink; - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // some known symbolic link. no need to stat again. - resolvedLink = cache[base]; - } else { - var stat = fs.lstatSync(base); - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - continue; - } + if (process.stdout && !process.stdout.isTTY) { + return 0; + } - // read the link if it wasn't read before - // dev/ino always return 0 on windows, so skip the check. - var linkTarget = null; - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - linkTarget = seenLinks[id]; - } - } - if (linkTarget === null) { - fs.statSync(base); - linkTarget = fs.readlinkSync(base); - } - resolvedLink = pathModule.resolve(previous, linkTarget); - // track this, if given a cache. - if (cache) cache[base] = resolvedLink; - if (!isWindows) seenLinks[id] = linkTarget; - } + if (process.platform === 'win32') { + return 1; + } - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); - } + if ('CI' in process.env) { + if ('TRAVIS' in process.env || process.env.CI === 'Travis') { + return 1; + } - if (cache) cache[original] = p; + return 0; + } - return p; -}; + if ('TEAMCITY_VERSION' in process.env) { + return process.env.TEAMCITY_VERSION.match(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/) === null ? 0 : 1; + } + if (/^(screen|xterm)-256(?:color)?/.test(process.env.TERM)) { + return 2; + } -exports.realpath = function realpath(p, cache, cb) { - if (typeof cb !== 'function') { - cb = maybeCallback(cache); - cache = null; - } + if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { + return 1; + } - // make p is absolute - p = pathModule.resolve(p); + if ('COLORTERM' in process.env) { + return 1; + } - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return process.nextTick(cb.bind(null, null, cache[p])); - } + if (process.env.TERM === 'dumb') { + return 0; + } - var original = p, - seenLinks = {}, - knownHard = {}; + return 0; +})(); - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; +if (supportLevel === 0 && 'FORCE_COLOR' in process.env) { + supportLevel = 1; +} - start(); +module.exports = process && support(supportLevel); - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstat(base, function(err) { - if (err) return cb(err); - knownHard[base] = true; - LOOP(); - }); - } else { - process.nextTick(LOOP); - } - } +/***/ }) +/******/ ]); - // walk down the path, swapping out linked pathparts for their real - // values - function LOOP() { - // stop if scanned past end of path - if (pos >= p.length) { - if (cache) cache[original] = p; - return cb(null, p); - } +/***/ }), +/* 281 */ +/***/ (function(module, exports) { - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; +module.exports = require("buffer"); - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - return process.nextTick(LOOP); - } +/***/ }), +/* 282 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // known symbolic link. no need to stat again. - return gotResolvedLink(cache[base]); - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BootstrapCacheFile", function() { return BootstrapCacheFile; }); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - return fs.lstat(base, gotStat); - } +/* + * 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. + */ - function gotStat(err, stat) { - if (err) return cb(err); - // if not a symlink, skip to the next path part - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - return process.nextTick(LOOP); +class BootstrapCacheFile { + constructor(kbn, project, checksums) { + _defineProperty(this, "path", void 0); + + _defineProperty(this, "expectedValue", void 0); + + this.path = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(project.targetLocation, '.bootstrap-cache'); + + if (!checksums) { + return; } - // stat & read the link if not read before - // call gotTarget as soon as the link target is known - // dev/ino always return 0 on windows, so skip the check. - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - return gotTarget(null, seenLinks[id], base); + const projectAndDepCacheKeys = Array.from(kbn.getProjectAndDeps(project.name).values()) // sort deps by name so that the key is stable + .sort((a, b) => a.name.localeCompare(b.name)) // get the cacheKey for each project, return undefined if the cache key couldn't be determined + .map(p => { + const cacheKey = checksums.get(p.name); + + if (cacheKey) { + return `${p.name}:${cacheKey}`; } - } - fs.stat(base, function(err) { - if (err) return cb(err); + }); // if any of the relevant cache keys are undefined then the projectCacheKey must be too - fs.readlink(base, function(err, target) { - if (!isWindows) seenLinks[id] = target; - gotTarget(err, target); - }); - }); + this.expectedValue = projectAndDepCacheKeys.some(k => !k) ? undefined : [`# this is only human readable for debugging, please don't try to parse this`, ...projectAndDepCacheKeys].join('\n'); } - function gotTarget(err, target, base) { - if (err) return cb(err); + isValid() { + if (!this.expectedValue) { + return false; + } - var resolvedLink = pathModule.resolve(previous, target); - if (cache) cache[base] = resolvedLink; - gotResolvedLink(resolvedLink); + try { + return fs__WEBPACK_IMPORTED_MODULE_0___default.a.readFileSync(this.path, 'utf8') === this.expectedValue; + } catch (error) { + if (error.code === 'ENOENT') { + return false; + } + + throw error; + } } - function gotResolvedLink(resolvedLink) { - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); + delete() { + try { + fs__WEBPACK_IMPORTED_MODULE_0___default.a.unlinkSync(this.path); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } + } + + write() { + if (!this.expectedValue) { + return; + } + + fs__WEBPACK_IMPORTED_MODULE_0___default.a.mkdirSync(path__WEBPACK_IMPORTED_MODULE_1___default.a.dirname(this.path), { + recursive: true + }); + fs__WEBPACK_IMPORTED_MODULE_0___default.a.writeFileSync(this.path, this.expectedValue); } -}; +} /***/ }), -/* 218 */ -/***/ (function(module, exports, __webpack_require__) { +/* 283 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -module.exports = globSync -globSync.GlobSync = GlobSync +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateYarnLock", function() { return validateYarnLock; }); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(130); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(143); +/* + * 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. + */ +// @ts-expect-error published types are useless -var fs = __webpack_require__(3) -var rp = __webpack_require__(114) -var minimatch = __webpack_require__(60) -var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(75).Glob -var util = __webpack_require__(2) -var path = __webpack_require__(0) -var assert = __webpack_require__(22) -var isAbsolute = __webpack_require__(76) -var common = __webpack_require__(115) -var alphasort = common.alphasort -var alphasorti = common.alphasorti -var setopts = common.setopts -var ownProp = common.ownProp -var childrenIgnored = common.childrenIgnored -var isIgnored = common.isIgnored -function globSync (pattern, options) { - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') - return new GlobSync(pattern, options).found -} -function GlobSync (pattern, options) { - if (!pattern) - throw new Error('must provide pattern') +async function validateYarnLock(kbn, yarnLock) { + // look through all of the packages in the yarn.lock file to see if + // we have accidentally installed multiple lodash v4 versions + const lodash4Versions = new Set(); + const lodash4Reqs = new Set(); - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') + for (const [req, dep] of Object.entries(yarnLock)) { + if (req.startsWith('lodash@') && dep.version.startsWith('4.')) { + lodash4Reqs.add(req); + lodash4Versions.add(dep.version); + } + } // if we find more than one lodash v4 version installed then delete + // lodash v4 requests from the yarn.lock file and prompt the user to + // retry bootstrap so that a single v4 version will be installed - if (!(this instanceof GlobSync)) - return new GlobSync(pattern, options) - setopts(this, pattern, options) + if (lodash4Versions.size > 1) { + for (const req of lodash4Reqs) { + delete yarnLock[req]; + } - if (this.noprocess) - return this + await Object(_fs__WEBPACK_IMPORTED_MODULE_2__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); + _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - var n = this.minimatch.set.length - this.matches = new Array(n) - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false) - } - this._finish() -} + Multiple version of lodash v4 were detected, so they have been removed + from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the + lodash versions installed. -GlobSync.prototype._finish = function () { - assert(this instanceof GlobSync) - if (this.realpath) { - var self = this - this.matches.forEach(function (matchset, index) { - var set = self.matches[index] = Object.create(null) - for (var p in matchset) { - try { - p = self._makeAbs(p) - var real = rp.realpathSync(p, self.realpathCache) - set[real] = true - } catch (er) { - if (er.syscall === 'stat') - set[self._makeAbs(p)] = true - else - throw er - } - } - }) - } - common.finish(this) -} + If you still see this error when you re-bootstrap then you might need + to force a new dependency to use the latest version of lodash via the + "resolutions" field in package.json. + If you have questions about this please reach out to the operations team. -GlobSync.prototype._process = function (pattern, index, inGlobStar) { - assert(this instanceof GlobSync) + `); + process.exit(1); + } // look through all the dependencies of production packages and production + // dependencies of those packages to determine if we're shipping any versions + // of lodash v3 in the distributable - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === 'string') { - n ++ - } - // now n is the index of the first one that is *not* a string. - // See if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - this._processSimple(pattern.join('/'), index) - return + const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_3__["log"]); + const lodash3Versions = new Set(); - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break + for (const dep of prodDependencies.values()) { + if (dep.name === 'lodash' && dep.version.startsWith('3.')) { + lodash3Versions.add(dep.version); + } + } // if any lodash v3 packages were found we abort and tell the user to fix things - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's 'absolute' like /foo/bar, - // or 'relative' like '../baz' - prefix = pattern.slice(0, n).join('/') - break - } - var remain = pattern.slice(n) + if (lodash3Versions.size) { + _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - // get the list of entries. - var read - if (prefix === null) - read = '.' - else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { - if (!prefix || !isAbsolute(prefix)) - prefix = '/' + prefix - read = prefix - } else - read = prefix + Due to changes in the yarn.lock file and/or package.json files a version of + lodash 3 is now included in the production dependencies. To reduce the size of + our distributable and especially our front-end bundles we have decided to + prevent adding any new instances of lodash 3. - var abs = this._makeAbs(read) + Please inspect the changes to yarn.lock or package.json files to identify where + the lodash 3 version is coming from and remove it. - //if ignored, skip processing - if (childrenIgnored(this, read)) - return + If you have questions about this please reack out to the operations team. - var isGlobStar = remain[0] === minimatch.GLOBSTAR - if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) - else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar) + `); + process.exit(1); + } + + _log__WEBPACK_IMPORTED_MODULE_3__["log"].success('yarn.lock analysis completed without any issues'); } +/***/ }), +/* 284 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { - var entries = this._readdir(abs, inGlobStar) +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(285); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(372); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(130); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); +/* + * 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. + */ - // if the abs isn't a dir, then nothing can match! - if (!entries) - return - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = remain[0] - var negate = !!this.minimatch.negate - var rawGlob = pn._glob - var dotOk = this.dot || rawGlob.charAt(0) === '.' - var matchedEntries = [] - for (var i = 0; i < entries.length; i++) { - var e = entries[i] - if (e.charAt(0) !== '.' || dotOk) { - var m - if (negate && !prefix) { - m = !e.match(pn) - } else { - m = e.match(pn) - } - if (m) - matchedEntries.push(e) - } - } - var len = matchedEntries.length - // If there are no matched entries, then nothing matches. - if (len === 0) - return - // if this is the last remaining pattern bit, then no need for - // an additional stat *unless* the user has specified mark or - // stat explicitly. We know they exist, since readdir returned - // them. +const CleanCommand = { + description: 'Remove the node_modules and target directories from all projects.', + name: 'clean', - if (remain.length === 1 && !this.mark && !this.stat) { - if (!this.matches[index]) - this.matches[index] = Object.create(null) + async run(projects) { + const toDelete = []; - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - if (prefix) { - if (prefix.slice(-1) !== '/') - e = prefix + '/' + e - else - e = prefix + e + for (const project of projects.values()) { + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_3__["isDirectory"])(project.nodeModulesLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(project.path, project.nodeModulesLocation) + }); } - if (e.charAt(0) === '/' && !this.nomount) { - e = path.join(this.root, e) + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_3__["isDirectory"])(project.targetLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(project.path, project.targetLocation) + }); } - this._emitMatch(index, e) - } - // This was the last one, and no stats were needed - return - } - - // now test all matched entries as stand-ins for that part - // of the pattern. - remain.shift() - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - var newPattern - if (prefix) - newPattern = [prefix, e] - else - newPattern = [e] - this._process(newPattern.concat(remain), index, inGlobStar) - } -} - -GlobSync.prototype._emitMatch = function (index, e) { - if (isIgnored(this, e)) - return + const { + extraPatterns + } = project.getCleanConfig(); - var abs = this._makeAbs(e) + if (extraPatterns) { + toDelete.push({ + cwd: project.path, + pattern: extraPatterns + }); + } + } - if (this.mark) - e = this._mark(e) + if (toDelete.length === 0) { + _utils_log__WEBPACK_IMPORTED_MODULE_4__["log"].success('Nothing to delete'); + } else { + /** + * In order to avoid patterns like `/build` in packages from accidentally + * impacting files outside the package we use `process.chdir()` to change + * the cwd to the package and execute `del()` without the `force` option + * so it will check that each file being deleted is within the package. + * + * `del()` does support a `cwd` option, but it's only for resolving the + * patterns and does not impact the cwd check. + */ + const originalCwd = process.cwd(); - if (this.absolute) { - e = abs - } + try { + for (const { + pattern, + cwd + } of toDelete) { + process.chdir(cwd); + const promise = del__WEBPACK_IMPORTED_MODULE_0___default()(pattern); - if (this.matches[index][e]) - return + if (_utils_log__WEBPACK_IMPORTED_MODULE_4__["log"].wouldLogLevel('info')) { + ora__WEBPACK_IMPORTED_MODULE_1___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(cwd, String(pattern)))); + } - if (this.nodir) { - var c = this.cache[abs] - if (c === 'DIR' || Array.isArray(c)) - return + await promise; + } + } finally { + process.chdir(originalCwd); + } + } } - this.matches[index][e] = true +}; - if (this.stat) - this._stat(e) -} +/***/ }), +/* 285 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; -GlobSync.prototype._readdirInGlobStar = function (abs) { - // follow all symlinked directories forever - // just proceed as if this is a non-globstar situation - if (this.follow) - return this._readdir(abs, false) +const {promisify} = __webpack_require__(111); +const path = __webpack_require__(4); +const globby = __webpack_require__(286); +const isGlob = __webpack_require__(364); +const slash = __webpack_require__(362); +const gracefulFs = __webpack_require__(132); +const isPathCwd = __webpack_require__(365); +const isPathInside = __webpack_require__(366); +const rimraf = __webpack_require__(367); +const pMap = __webpack_require__(368); - var entries - var lstat - var stat - try { - lstat = fs.lstatSync(abs) - } catch (er) { - if (er.code === 'ENOENT') { - // lstat failed, doesn't exist - return null - } - } +const rimrafP = promisify(rimraf); - var isSym = lstat && lstat.isSymbolicLink() - this.symlinks[abs] = isSym +const rimrafOptions = { + glob: false, + unlink: gracefulFs.unlink, + unlinkSync: gracefulFs.unlinkSync, + chmod: gracefulFs.chmod, + chmodSync: gracefulFs.chmodSync, + stat: gracefulFs.stat, + statSync: gracefulFs.statSync, + lstat: gracefulFs.lstat, + lstatSync: gracefulFs.lstatSync, + rmdir: gracefulFs.rmdir, + rmdirSync: gracefulFs.rmdirSync, + readdir: gracefulFs.readdir, + readdirSync: gracefulFs.readdirSync +}; - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) - this.cache[abs] = 'FILE' - else - entries = this._readdir(abs, false) +function safeCheck(file, cwd) { + if (isPathCwd(file)) { + throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.'); + } - return entries + if (!isPathInside(file, cwd)) { + throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'); + } } -GlobSync.prototype._readdir = function (abs, inGlobStar) { - var entries - - if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs) +function normalizePatterns(patterns) { + patterns = Array.isArray(patterns) ? patterns : [patterns]; - if (ownProp(this.cache, abs)) { - var c = this.cache[abs] - if (!c || c === 'FILE') - return null + patterns = patterns.map(pattern => { + if (process.platform === 'win32' && isGlob(pattern) === false) { + return slash(pattern); + } - if (Array.isArray(c)) - return c - } + return pattern; + }); - try { - return this._readdirEntries(abs, fs.readdirSync(abs)) - } catch (er) { - this._readdirError(abs, er) - return null - } + return patterns; } -GlobSync.prototype._readdirEntries = function (abs, entries) { - // if we haven't asked to stat everything, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. - if (!this.mark && !this.stat) { - for (var i = 0; i < entries.length; i ++) { - var e = entries[i] - if (abs === '/') - e = abs + e - else - e = abs + '/' + e - this.cache[e] = true - } - } +module.exports = async (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => { + options = { + expandDirectories: false, + onlyFiles: false, + followSymbolicLinks: false, + cwd, + ...options + }; - this.cache[abs] = entries + patterns = normalizePatterns(patterns); - // mark and cache dir-ness - return entries -} + const files = (await globby(patterns, options)) + .sort((a, b) => b.localeCompare(a)); -GlobSync.prototype._readdirError = function (f, er) { - // handle errors, and cache the information - switch (er.code) { - case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 - case 'ENOTDIR': // totally normal. means it *does* exist. - var abs = this._makeAbs(f) - this.cache[abs] = 'FILE' - if (abs === this.cwdAbs) { - var error = new Error(er.code + ' invalid cwd ' + this.cwd) - error.path = this.cwd - error.code = er.code - throw error - } - break + const mapper = async file => { + file = path.resolve(cwd, file); - case 'ENOENT': // not terribly unusual - case 'ELOOP': - case 'ENAMETOOLONG': - case 'UNKNOWN': - this.cache[this._makeAbs(f)] = false - break + if (!force) { + safeCheck(file, cwd); + } - default: // some unusual error. Treat as failure. - this.cache[this._makeAbs(f)] = false - if (this.strict) - throw er - if (!this.silent) - console.error('glob error', er) - break - } -} + if (!dryRun) { + await rimrafP(file, rimrafOptions); + } -GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { + return file; + }; - var entries = this._readdir(abs, inGlobStar) + const removedFiles = await pMap(files, mapper, options); - // no entries means not a dir, so it can never have matches - // foo.txt/** doesn't match foo.txt - if (!entries) - return + removedFiles.sort((a, b) => a.localeCompare(b)); - // test without the globstar, and with every child both below - // and replacing the globstar. - var remainWithoutGlobStar = remain.slice(1) - var gspref = prefix ? [ prefix ] : [] - var noGlobStar = gspref.concat(remainWithoutGlobStar) + return removedFiles; +}; - // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false) +module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => { + options = { + expandDirectories: false, + onlyFiles: false, + followSymbolicLinks: false, + cwd, + ...options + }; - var len = entries.length - var isSym = this.symlinks[abs] + patterns = normalizePatterns(patterns); - // If it's a symlink, and we're in a globstar, then stop - if (isSym && inGlobStar) - return + const files = globby.sync(patterns, options) + .sort((a, b) => b.localeCompare(a)); - for (var i = 0; i < len; i++) { - var e = entries[i] - if (e.charAt(0) === '.' && !this.dot) - continue + const removedFiles = files.map(file => { + file = path.resolve(cwd, file); - // these two cases enter the inGlobStar state - var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true) + if (!force) { + safeCheck(file, cwd); + } - var below = gspref.concat(entries[i], remain) - this._process(below, index, true) - } -} + if (!dryRun) { + rimraf.sync(file, rimrafOptions); + } -GlobSync.prototype._processSimple = function (prefix, index) { - // XXX review this. Shouldn't it be doing the mounting etc - // before doing stat? kinda weird? - var exists = this._stat(prefix) + return file; + }); - if (!this.matches[index]) - this.matches[index] = Object.create(null) + removedFiles.sort((a, b) => a.localeCompare(b)); - // If it doesn't exist, then just mark the lack of results - if (!exists) - return + return removedFiles; +}; - if (prefix && isAbsolute(prefix) && !this.nomount) { - var trail = /[\/\\]$/.test(prefix) - if (prefix.charAt(0) === '/') { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - if (trail) - prefix += '/' - } - } - if (process.platform === 'win32') - prefix = prefix.replace(/\\/g, '/') +/***/ }), +/* 286 */ +/***/ (function(module, exports, __webpack_require__) { - // Mark this as a match - this._emitMatch(index, prefix) -} +"use strict"; -// Returns either 'DIR', 'FILE', or false -GlobSync.prototype._stat = function (f) { - var abs = this._makeAbs(f) - var needDir = f.slice(-1) === '/' +const fs = __webpack_require__(133); +const arrayUnion = __webpack_require__(287); +const merge2 = __webpack_require__(288); +const glob = __webpack_require__(146); +const fastGlob = __webpack_require__(289); +const dirGlob = __webpack_require__(358); +const gitignore = __webpack_require__(360); +const {FilterStream, UniqueStream} = __webpack_require__(363); - if (f.length > this.maxLength) - return false +const DEFAULT_FILTER = () => false; - if (!this.stat && ownProp(this.cache, abs)) { - var c = this.cache[abs] +const isNegative = pattern => pattern[0] === '!'; - if (Array.isArray(c)) - c = 'DIR' +const assertPatternsInput = patterns => { + if (!patterns.every(pattern => typeof pattern === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); + } +}; - // It exists, but maybe not how we need it - if (!needDir || c === 'DIR') - return c +const checkCwdOption = (options = {}) => { + if (!options.cwd) { + return; + } - if (needDir && c === 'FILE') - return false + let stat; + try { + stat = fs.statSync(options.cwd); + } catch (_) { + return; + } - // otherwise we have to stat, because maybe c=true - // if we know it exists, but not what it is. - } + if (!stat.isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } +}; - var exists - var stat = this.statCache[abs] - if (!stat) { - var lstat - try { - lstat = fs.lstatSync(abs) - } catch (er) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return false - } - } +const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; - if (lstat && lstat.isSymbolicLink()) { - try { - stat = fs.statSync(abs) - } catch (er) { - stat = lstat - } - } else { - stat = lstat - } - } +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); - this.statCache[abs] = stat + const globTasks = []; - var c = true - if (stat) - c = stat.isDirectory() ? 'DIR' : 'FILE' + taskOptions = { + ignore: [], + expandDirectories: true, + ...taskOptions + }; - this.cache[abs] = this.cache[abs] || c + for (const [index, pattern] of patterns.entries()) { + if (isNegative(pattern)) { + continue; + } - if (needDir && c === 'FILE') - return false + const ignore = patterns + .slice(index) + .filter(isNegative) + .map(pattern => pattern.slice(1)); - return c -} + const options = { + ...taskOptions, + ignore: taskOptions.ignore.concat(ignore) + }; -GlobSync.prototype._mark = function (p) { - return common.mark(this, p) -} + globTasks.push({pattern, options}); + } -GlobSync.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) -} + return globTasks; +}; + +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } + if (Array.isArray(task.options.expandDirectories)) { + options = { + ...options, + files: task.options.expandDirectories + }; + } else if (typeof task.options.expandDirectories === 'object') { + options = { + ...options, + ...task.options.expandDirectories + }; + } -/***/ }), -/* 219 */, -/* 220 */, -/* 221 */ -/***/ (function(module, exports, __webpack_require__) { + return fn(task.pattern, options); +}; -"use strict"; +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -module.exports = function (flag, argv) { - argv = argv || process.argv; +const getFilterSync = options => { + return options && options.gitignore ? + gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; +}; - var terminatorPos = argv.indexOf('--'); - var prefix = /^--/.test(flag) ? '' : '--'; - var pos = argv.indexOf(prefix + flag); +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } - return pos !== -1 && (terminatorPos !== -1 ? pos < terminatorPos : true); + return { + pattern: glob, + options + }; }; +module.exports = async (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -/***/ }), -/* 222 */, -/* 223 */ -/***/ (function(module, exports, __webpack_require__) { + const getFilter = async () => { + return options && options.gitignore ? + gitignore({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; + }; -var wrappy = __webpack_require__(123) -var reqs = Object.create(null) -var once = __webpack_require__(61) + const getTasks = async () => { + const tasks = await Promise.all(globTasks.map(async task => { + const globs = await getPattern(task, dirGlob); + return Promise.all(globs.map(globToTask(task))); + })); -module.exports = wrappy(inflight) + return arrayUnion(...tasks); + }; -function inflight (key, cb) { - if (reqs[key]) { - reqs[key].push(cb) - return null - } else { - reqs[key] = [cb] - return makeres(key) - } -} + const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); + const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); -function makeres (key) { - return once(function RES () { - var cbs = reqs[key] - var len = cbs.length - var args = slice(arguments) + return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); +}; - // XXX It's somewhat ambiguous whether a new callback added in this - // pass should be queued for later execution if something in the - // list of callbacks throws, or if it should just be discarded. - // However, it's such an edge case that it hardly matters, and either - // choice is likely as surprising as the other. - // As it happens, we do go ahead and schedule it for later execution. - try { - for (var i = 0; i < len; i++) { - cbs[i].apply(null, args) - } - } finally { - if (cbs.length > len) { - // added more in the interim. - // de-zalgo, just in case, but don't call again. - cbs.splice(0, len) - process.nextTick(function () { - RES.apply(null, args) - }) - } else { - delete reqs[key] - } - } - }) -} +module.exports.sync = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -function slice (args) { - var length = args.length - var array = [] + const tasks = globTasks.reduce((tasks, task) => { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + return tasks.concat(newTask); + }, []); - for (var i = 0; i < length; i++) array[i] = args[i] - return array -} + const filter = getFilterSync(options); + return tasks.reduce( + (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), + [] + ).filter(path_ => !filter(path_)); +}; -/***/ }), -/* 224 */ -/***/ (function(module, exports) { +module.exports.stream = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} + const tasks = globTasks.reduce((tasks, task) => { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + return tasks.concat(newTask); + }, []); + + const filter = getFilterSync(options); + const filterStream = new FilterStream(p => !filter(p)); + const uniqueStream = new UniqueStream(); + + return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) + .pipe(filterStream) + .pipe(uniqueStream); +}; + +module.exports.generateGlobTasks = generateGlobTasks; + +module.exports.hasMagic = (patterns, options) => [] + .concat(patterns) + .some(pattern => glob.hasMagic(pattern, options)); + +module.exports.gitignore = gitignore; /***/ }), -/* 225 */, -/* 226 */, -/* 227 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { -// @flow +"use strict"; -/*:: -declare var __webpack_require__: mixed; -*/ -module.exports = typeof __webpack_require__ !== "undefined"; +module.exports = (...arguments_) => { + return [...new Set([].concat(...arguments_))]; +}; /***/ }), -/* 228 */, -/* 229 */ -/***/ (function(module, exports) { - -/** - * Helpers. - */ +/* 288 */ +/***/ (function(module, exports, __webpack_require__) { -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; +"use strict"; -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] +/* + * merge2 + * https://github.com/teambition/merge2 * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public + * Copyright (c) 2014-2020 Teambition + * Licensed under the MIT license. */ +const Stream = __webpack_require__(137) +const PassThrough = Stream.PassThrough +const slice = Array.prototype.slice -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; +module.exports = merge2 -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ +function merge2 () { + const streamsQueue = [] + const args = slice.call(arguments) + let merging = false + let options = args[args.length - 1] -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; + if (options && !Array.isArray(options) && options.pipe == null) { + args.pop() + } else { + options = {} } -} -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; + const doEnd = options.end !== false + const doPipeError = options.pipeError === true + if (options.objectMode == null) { + options.objectMode = true } - if (ms >= m) { - return Math.round(ms / m) + 'm'; + if (options.highWaterMark == null) { + options.highWaterMark = 64 * 1024 } - if (ms >= s) { - return Math.round(ms / s) + 's'; + const mergedStream = PassThrough(options) + + function addStream () { + for (let i = 0, len = arguments.length; i < len; i++) { + streamsQueue.push(pauseStreams(arguments[i], options)) + } + mergeStream() + return this } - return ms + 'ms'; -} -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ + function mergeStream () { + if (merging) { + return + } + merging = true -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} + let streams = streamsQueue.shift() + if (!streams) { + process.nextTick(endStream) + return + } + if (!Array.isArray(streams)) { + streams = [streams] + } -/** - * Pluralization helper. - */ + let pipesCount = streams.length + 1 -function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; - } - return Math.ceil(ms / n) + ' ' + name + 's'; -} + function next () { + if (--pipesCount > 0) { + return + } + merging = false + mergeStream() + } + function pipe (stream) { + function onend () { + stream.removeListener('merge2UnpipeEnd', onend) + stream.removeListener('end', onend) + if (doPipeError) { + stream.removeListener('error', onerror) + } + next() + } + function onerror (err) { + mergedStream.emit('error', err) + } + // skip ended stream + if (stream._readableState.endEmitted) { + return next() + } -/***/ }), -/* 230 */, -/* 231 */, -/* 232 */, -/* 233 */ -/***/ (function(module, exports, __webpack_require__) { + stream.on('merge2UnpipeEnd', onend) + stream.on('end', onend) -module.exports = rimraf -rimraf.sync = rimrafSync + if (doPipeError) { + stream.on('error', onerror) + } -var assert = __webpack_require__(22) -var path = __webpack_require__(0) -var fs = __webpack_require__(3) -var glob = __webpack_require__(75) -var _0666 = parseInt('666', 8) + stream.pipe(mergedStream, { end: false }) + // compatible for old stream + stream.resume() + } -var defaultGlobOpts = { - nosort: true, - silent: true -} + for (let i = 0; i < streams.length; i++) { + pipe(streams[i]) + } -// for EMFILE handling -var timeout = 0 + next() + } -var isWindows = (process.platform === "win32") + function endStream () { + merging = false + // emit 'queueDrain' when all streams merged. + mergedStream.emit('queueDrain') + if (doEnd) { + mergedStream.end() + } + } -function defaults (options) { - var methods = [ - 'unlink', - 'chmod', - 'stat', - 'lstat', - 'rmdir', - 'readdir' - ] - methods.forEach(function(m) { - options[m] = options[m] || fs[m] - m = m + 'Sync' - options[m] = options[m] || fs[m] + mergedStream.setMaxListeners(0) + mergedStream.add = addStream + mergedStream.on('unpipe', function (stream) { + stream.emit('merge2UnpipeEnd') }) - options.maxBusyTries = options.maxBusyTries || 3 - options.emfileWait = options.emfileWait || 1000 - if (options.glob === false) { - options.disableGlob = true + if (args.length) { + addStream.apply(null, args) } - options.disableGlob = options.disableGlob || false - options.glob = options.glob || defaultGlobOpts + return mergedStream } -function rimraf (p, options, cb) { - if (typeof options === 'function') { - cb = options - options = {} +// check and pause streams for pipe. +function pauseStreams (streams, options) { + if (!Array.isArray(streams)) { + // Backwards-compat with old-style streams + if (!streams._readableState && streams.pipe) { + streams = streams.pipe(PassThrough(options)) + } + if (!streams._readableState || !streams.pause || !streams.pipe) { + throw new Error('Only readable stream can be merged.') + } + streams.pause() + } else { + for (let i = 0, len = streams.length; i < len; i++) { + streams[i] = pauseStreams(streams[i], options) + } } + return streams +} - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert.equal(typeof cb, 'function', 'rimraf: callback function required') - assert(options, 'rimraf: invalid options argument provided') - assert.equal(typeof options, 'object', 'rimraf: options should be object') - - defaults(options) - var busyTries = 0 - var errState = null - var n = 0 +/***/ }), +/* 289 */ +/***/ (function(module, exports, __webpack_require__) { - if (options.disableGlob || !glob.hasMagic(p)) - return afterGlob(null, [p]) +"use strict"; + +const taskManager = __webpack_require__(290); +const async_1 = __webpack_require__(319); +const stream_1 = __webpack_require__(354); +const sync_1 = __webpack_require__(355); +const settings_1 = __webpack_require__(357); +const utils = __webpack_require__(291); +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = [].concat(source); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = [].concat(source); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} +module.exports = FastGlob; - options.lstat(p, function (er, stat) { - if (!er) - return afterGlob(null, [p]) - glob(p, options.glob, afterGlob) - }) +/***/ }), +/* 290 */ +/***/ (function(module, exports, __webpack_require__) { - function next (er) { - errState = errState || er - if (--n === 0) - cb(errState) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +function convertPatternsToTasks(positive, negative, dynamic) { + const positivePatternsGroup = groupPatternsByBaseDirectory(positive); + // When we have a global group – there is no reason to divide the patterns into independent tasks. + // In this case, the global task covers the rest. + if ('.' in positivePatternsGroup) { + const task = convertPatternGroupToTask('.', positive, negative, dynamic); + return [task]; + } + return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); +} +exports.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils.pattern.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils.pattern.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; - function afterGlob (er, results) { - if (er) - return cb(er) - n = results.length - if (n === 0) - return cb() +/***/ }), +/* 291 */ +/***/ (function(module, exports, __webpack_require__) { - results.forEach(function (p) { - rimraf_(p, options, function CB (er) { - if (er) { - if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && - busyTries < options.maxBusyTries) { - busyTries ++ - var time = busyTries * 100 - // try again, with the same exact callback as this one. - return setTimeout(function () { - rimraf_(p, options, CB) - }, time) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const array = __webpack_require__(292); +exports.array = array; +const errno = __webpack_require__(293); +exports.errno = errno; +const fs = __webpack_require__(294); +exports.fs = fs; +const path = __webpack_require__(295); +exports.path = path; +const pattern = __webpack_require__(296); +exports.pattern = pattern; +const stream = __webpack_require__(317); +exports.stream = stream; +const string = __webpack_require__(318); +exports.string = string; - // this one won't happen if graceful-fs is used. - if (er.code === "EMFILE" && timeout < options.emfileWait) { - return setTimeout(function () { - rimraf_(p, options, CB) - }, timeout ++) - } - // already gone - if (er.code === "ENOENT") er = null - } +/***/ }), +/* 292 */ +/***/ (function(module, exports, __webpack_require__) { - timeout = 0 - next(er) - }) - }) - } -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function flatten(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +exports.flatten = flatten; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} +exports.splitWhen = splitWhen; -// Two possible strategies. -// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR -// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR -// -// Both result in an extra syscall when you guess wrong. However, there -// are likely far more normal files in the world than directories. This -// is based on the assumption that a the average number of files per -// directory is >= 1. -// -// If anyone ever complains about this, then I guess the strategy could -// be made configurable somehow. But until then, YAGNI. -function rimraf_ (p, options, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') - // sunos lets the root user unlink directories, which is... weird. - // so we have to lstat here and make sure it's not a dir. - options.lstat(p, function (er, st) { - if (er && er.code === "ENOENT") - return cb(null) +/***/ }), +/* 293 */ +/***/ (function(module, exports, __webpack_require__) { - // Windows can EPERM on stat. Life is suffering. - if (er && er.code === "EPERM" && isWindows) - fixWinEPERM(p, options, er, cb) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} +exports.isEnoentCodeError = isEnoentCodeError; - if (st && st.isDirectory()) - return rmdir(p, options, er, cb) - options.unlink(p, function (er) { - if (er) { - if (er.code === "ENOENT") - return cb(null) - if (er.code === "EPERM") - return (isWindows) - ? fixWinEPERM(p, options, er, cb) - : rmdir(p, options, er, cb) - if (er.code === "EISDIR") - return rmdir(p, options, er, cb) - } - return cb(er) - }) - }) -} +/***/ }), +/* 294 */ +/***/ (function(module, exports, __webpack_require__) { -function fixWinEPERM (p, options, er, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') - if (er) - assert(er instanceof Error) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; - options.chmod(p, _0666, function (er2) { - if (er2) - cb(er2.code === "ENOENT" ? null : er) - else - options.stat(p, function(er3, stats) { - if (er3) - cb(er3.code === "ENOENT" ? null : er) - else if (stats.isDirectory()) - rmdir(p, options, er, cb) - else - options.unlink(p, cb) - }) - }) -} -function fixWinEPERMSync (p, options, er) { - assert(p) - assert(options) - if (er) - assert(er instanceof Error) +/***/ }), +/* 295 */ +/***/ (function(module, exports, __webpack_require__) { - try { - options.chmodSync(p, _0666) - } catch (er2) { - if (er2.code === "ENOENT") - return - else - throw er - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path.resolve(cwd, filepath); +} +exports.makeAbsolute = makeAbsolute; +function escape(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +exports.escape = escape; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } + } + return entry; +} +exports.removeLeadingDotSegment = removeLeadingDotSegment; - try { - var stats = options.statSync(p) - } catch (er3) { - if (er3.code === "ENOENT") - return - else - throw er - } - if (stats.isDirectory()) - rmdirSync(p, options, er) - else - options.unlinkSync(p) -} +/***/ }), +/* 296 */ +/***/ (function(module, exports, __webpack_require__) { -function rmdir (p, options, originalEr, cb) { - assert(p) - assert(options) - if (originalEr) - assert(originalEr instanceof Error) - assert(typeof cb === 'function') +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const globParent = __webpack_require__(297); +const micromatch = __webpack_require__(300); +const picomatch = __webpack_require__(311); +const GLOBSTAR = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; +const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +exports.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { + return true; + } + return false; +} +exports.isDynamicPattern = isDynamicPattern; +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +function getBaseDirectory(pattern) { + return globParent(pattern, { flipBackslashes: false }); +} +exports.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR); +} +exports.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +exports.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + const info = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + // See micromatch/picomatch#58 for more details + if (info.parts.length === 0) { + return [pattern]; + } + return info.parts; +} +exports.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +exports.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} +exports.matchAny = matchAny; - // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) - // if we guessed wrong, and it's not a directory, then - // raise the original error. - options.rmdir(p, function (er) { - if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) - rmkids(p, options, cb) - else if (er && er.code === "ENOTDIR") - cb(originalEr) - else - cb(er) - }) -} -function rmkids(p, options, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') +/***/ }), +/* 297 */ +/***/ (function(module, exports, __webpack_require__) { - options.readdir(p, function (er, files) { - if (er) - return cb(er) - var n = files.length - if (n === 0) - return options.rmdir(p, cb) - var errState - files.forEach(function (f) { - rimraf(path.join(p, f), options, function (er) { - if (errState) - return - if (er) - return cb(errState = er) - if (--n === 0) - options.rmdir(p, cb) - }) - }) - }) -} +"use strict"; -// this looks simpler, and is strictly *faster*, but will -// tie up the JavaScript thread and fail on excessively -// deep directory trees. -function rimrafSync (p, options) { - options = options || {} - defaults(options) - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert(options, 'rimraf: missing options') - assert.equal(typeof options, 'object', 'rimraf: options should be object') +var isGlob = __webpack_require__(298); +var pathPosixDirname = __webpack_require__(4).posix.dirname; +var isWin32 = __webpack_require__(120).platform() === 'win32'; - var results +var slash = '/'; +var backslash = /\\/g; +var enclosure = /[\{\[].*[\/]*.*[\}\]]$/; +var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; +var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; - if (options.disableGlob || !glob.hasMagic(p)) { - results = [p] - } else { - try { - options.lstatSync(p) - results = [p] - } catch (er) { - results = glob.sync(p, options.glob) - } - } +/** + * @param {string} str + * @param {Object} opts + * @param {boolean} [opts.flipBackslashes=true] + */ +module.exports = function globParent(str, opts) { + var options = Object.assign({ flipBackslashes: true }, opts); - if (!results.length) - return + // flip windows path separators + if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { + str = str.replace(backslash, slash); + } - for (var i = 0; i < results.length; i++) { - var p = results[i] + // special case for strings ending in enclosure containing path separator + if (enclosure.test(str)) { + str += slash; + } - try { - var st = options.lstatSync(p) - } catch (er) { - if (er.code === "ENOENT") - return + // preserves full path in case of trailing path separator + str += 'a'; - // Windows can EPERM on stat. Life is suffering. - if (er.code === "EPERM" && isWindows) - fixWinEPERMSync(p, options, er) - } + // remove path parts that are globby + do { + str = pathPosixDirname(str); + } while (isGlob(str) || globby.test(str)); - try { - // sunos lets the root user unlink directories, which is... weird. - if (st && st.isDirectory()) - rmdirSync(p, options, null) - else - options.unlinkSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "EPERM") - return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) - if (er.code !== "EISDIR") - throw er + // remove escape chars and return result + return str.replace(escaped, '$1'); +}; - rmdirSync(p, options, er) - } - } -} -function rmdirSync (p, options, originalEr) { - assert(p) - assert(options) - if (originalEr) - assert(originalEr instanceof Error) +/***/ }), +/* 298 */ +/***/ (function(module, exports, __webpack_require__) { - try { - options.rmdirSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "ENOTDIR") - throw originalEr - if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") - rmkidsSync(p, options) - } -} +/*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ -function rmkidsSync (p, options) { - assert(p) - assert(options) - options.readdirSync(p).forEach(function (f) { - rimrafSync(path.join(p, f), options) - }) +var isExtglob = __webpack_require__(299); +var chars = { '{': '}', '(': ')', '[': ']'}; +var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; +var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - // We only end up here once we got ENOTEMPTY at least once, and - // at this point, we are guaranteed to have removed all the kids. - // So, we know that it won't be ENOENT or ENOTDIR or anything else. - // try really hard to delete stuff on windows, because it has a - // PROFOUNDLY annoying habit of not closing handles promptly when - // files are deleted, resulting in spurious ENOTEMPTY errors. - var retries = isWindows ? 100 : 1 - var i = 0 - do { - var threw = true - try { - var ret = options.rmdirSync(p, options) - threw = false - return ret - } finally { - if (++i < retries && threw) - continue - } - } while (true) -} +module.exports = function isGlob(str, options) { + if (typeof str !== 'string' || str === '') { + return false; + } + if (isExtglob(str)) { + return true; + } -/***/ }), -/* 234 */, -/* 235 */, -/* 236 */, -/* 237 */, -/* 238 */, -/* 239 */ -/***/ (function(module, exports, __webpack_require__) { + var regex = strictRegex; + var match; -"use strict"; + // optionally relax regex + if (options && options.strict === false) { + regex = relaxedRegex; + } -var hasFlag = __webpack_require__(221); + while ((match = regex.exec(str))) { + if (match[2]) return true; + var idx = match.index + match[0].length; -var support = function (level) { - if (level === 0) { - return false; - } + // if an open bracket/brace/paren is escaped, + // set the index to the next closing character + var open = match[1]; + var close = open ? chars[open] : null; + if (open && close) { + var n = str.indexOf(close, idx); + if (n !== -1) { + idx = n + 1; + } + } - return { - level: level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; + str = str.slice(idx); + } + return false; }; -var supportLevel = (function () { - if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false')) { - return 0; - } - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } +/***/ }), +/* 299 */ +/***/ (function(module, exports) { - if (hasFlag('color=256')) { - return 2; - } +/*! + * is-extglob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + */ - if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - return 1; - } +module.exports = function isExtglob(str) { + if (typeof str !== 'string' || str === '') { + return false; + } - if (process.stdout && !process.stdout.isTTY) { - return 0; - } + var match; + while ((match = /(\\).|([@?!+*]\(.*\))/g.exec(str))) { + if (match[2]) return true; + str = str.slice(match.index + match[0].length); + } - if (process.platform === 'win32') { - return 1; - } + return false; +}; - if ('CI' in process.env) { - if ('TRAVIS' in process.env || process.env.CI === 'Travis') { - return 1; - } - return 0; - } +/***/ }), +/* 300 */ +/***/ (function(module, exports, __webpack_require__) { - if ('TEAMCITY_VERSION' in process.env) { - return process.env.TEAMCITY_VERSION.match(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/) === null ? 0 : 1; - } +"use strict"; - if (/^(screen|xterm)-256(?:color)?/.test(process.env.TERM)) { - return 2; - } - if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { - return 1; - } +const util = __webpack_require__(111); +const braces = __webpack_require__(301); +const picomatch = __webpack_require__(311); +const utils = __webpack_require__(314); +const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); - if ('COLORTERM' in process.env) { - return 1; - } - - if (process.env.TERM === 'dumb') { - return 0; - } - - return 0; -})(); - -if (supportLevel === 0 && 'FORCE_COLOR' in process.env) { - supportLevel = 1; -} - -module.exports = process && support(supportLevel); - - -/***/ }) -/******/ ]); - -/***/ }), -/* 285 */ -/***/ (function(module, exports) { - -module.exports = require("buffer"); - -/***/ }), -/* 286 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BootstrapCacheFile", function() { return BootstrapCacheFile; }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/* - * 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 +/** + * Returns an array of strings that match one or more glob patterns. * - * http://www.apache.org/licenses/LICENSE-2.0 + * ```js + * const mm = require('micromatch'); + * // mm(list, patterns[, options]); * - * 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. + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {String|Array} list List of strings to match. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} options See available [options](#options) + * @return {Array} Returns an array of matches + * @summary false + * @api public */ +const micromatch = (list, patterns, options) => { + patterns = [].concat(patterns); + list = [].concat(list); -class BootstrapCacheFile { - constructor(kbn, project, checksums) { - _defineProperty(this, "path", void 0); - - _defineProperty(this, "expectedValue", void 0); - - this.path = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(project.targetLocation, '.bootstrap-cache'); + let omit = new Set(); + let keep = new Set(); + let items = new Set(); + let negatives = 0; - if (!checksums) { - return; + let onResult = state => { + items.add(state.output); + if (options && options.onResult) { + options.onResult(state); } + }; - const projectAndDepCacheKeys = Array.from(kbn.getProjectAndDeps(project.name).values()) // sort deps by name so that the key is stable - .sort((a, b) => a.name.localeCompare(b.name)) // get the cacheKey for each project, return undefined if the cache key couldn't be determined - .map(p => { - const cacheKey = checksums.get(p.name); - - if (cacheKey) { - return `${p.name}:${cacheKey}`; - } - }); // if any of the relevant cache keys are undefined then the projectCacheKey must be too + for (let i = 0; i < patterns.length; i++) { + let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); + let negated = isMatch.state.negated || isMatch.state.negatedExtglob; + if (negated) negatives++; - this.expectedValue = projectAndDepCacheKeys.some(k => !k) ? undefined : [`# this is only human readable for debugging, please don't try to parse this`, ...projectAndDepCacheKeys].join('\n'); - } + for (let item of list) { + let matched = isMatch(item, true); - isValid() { - if (!this.expectedValue) { - return false; - } + let match = negated ? !matched.isMatch : matched.isMatch; + if (!match) continue; - try { - return fs__WEBPACK_IMPORTED_MODULE_0___default.a.readFileSync(this.path, 'utf8') === this.expectedValue; - } catch (error) { - if (error.code === 'ENOENT') { - return false; + if (negated) { + omit.add(matched.output); + } else { + omit.delete(matched.output); + keep.add(matched.output); } - - throw error; } } - delete() { - try { - fs__WEBPACK_IMPORTED_MODULE_0___default.a.unlinkSync(this.path); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } - } - } + let result = negatives === patterns.length ? [...items] : [...keep]; + let matches = result.filter(item => !omit.has(item)); - write() { - if (!this.expectedValue) { - return; + if (options && matches.length === 0) { + if (options.failglob === true) { + throw new Error(`No matches found for "${patterns.join(', ')}"`); } - fs__WEBPACK_IMPORTED_MODULE_0___default.a.mkdirSync(path__WEBPACK_IMPORTED_MODULE_1___default.a.dirname(this.path), { - recursive: true - }); - fs__WEBPACK_IMPORTED_MODULE_0___default.a.writeFileSync(this.path, this.expectedValue); + if (options.nonull === true || options.nullglob === true) { + return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; + } } -} + return matches; +}; -/***/ }), -/* 287 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/** + * Backwards compatibility + */ -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(288); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(375); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(130); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); -/* - * 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 +micromatch.match = micromatch; + +/** + * Returns a matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. * - * http://www.apache.org/licenses/LICENSE-2.0 + * ```js + * const mm = require('micromatch'); + * // mm.matcher(pattern[, options]); * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * const isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` + * @return {Function} Returns a matcher function. + * @api public */ +micromatch.matcher = (pattern, options) => picomatch(pattern, options); +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const mm = require('micromatch'); + * // mm.isMatch(string, patterns[, options]); + * + * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(mm.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ +micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); +/** + * Backwards compatibility + */ -const CleanCommand = { - description: 'Remove the node_modules and target directories from all projects.', - name: 'clean', +micromatch.any = micromatch.isMatch; - async run(projects) { - const toDelete = []; +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ - for (const project of projects.values()) { - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_3__["isDirectory"])(project.nodeModulesLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(project.path, project.nodeModulesLocation) - }); - } +micromatch.not = (list, patterns, options = {}) => { + patterns = [].concat(patterns).map(String); + let result = new Set(); + let items = []; - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_3__["isDirectory"])(project.targetLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(project.path, project.targetLocation) - }); - } + let onResult = state => { + if (options.onResult) options.onResult(state); + items.push(state.output); + }; - const { - extraPatterns - } = project.getCleanConfig(); + let matches = micromatch(list, patterns, { ...options, onResult }); - if (extraPatterns) { - toDelete.push({ - cwd: project.path, - pattern: extraPatterns - }); - } + for (let item of items) { + if (!matches.includes(item)) { + result.add(item); } + } + return [...result]; +}; - if (toDelete.length === 0) { - _utils_log__WEBPACK_IMPORTED_MODULE_4__["log"].success('Nothing to delete'); - } else { - /** - * In order to avoid patterns like `/build` in packages from accidentally - * impacting files outside the package we use `process.chdir()` to change - * the cwd to the package and execute `del()` without the `force` option - * so it will check that each file being deleted is within the package. - * - * `del()` does support a `cwd` option, but it's only for resolving the - * patterns and does not impact the cwd check. - */ - const originalCwd = process.cwd(); +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * // mm.contains(string, pattern[, options]); + * + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the patter matches any part of `str`. + * @api public + */ - try { - for (const { - pattern, - cwd - } of toDelete) { - process.chdir(cwd); - const promise = del__WEBPACK_IMPORTED_MODULE_0___default()(pattern); +micromatch.contains = (str, pattern, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } - if (_utils_log__WEBPACK_IMPORTED_MODULE_4__["log"].wouldLogLevel('info')) { - ora__WEBPACK_IMPORTED_MODULE_1___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(cwd, String(pattern)))); - } + if (Array.isArray(pattern)) { + return pattern.some(p => micromatch.contains(str, p, options)); + } - await promise; - } - } finally { - process.chdir(originalCwd); - } + if (typeof pattern === 'string') { + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; + } + + if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { + return true; } } + return micromatch.isMatch(str, pattern, { ...options, contains: true }); }; -/***/ }), -/* 288 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * const mm = require('micromatch'); + * // mm.matchKeys(object, patterns[, options]); + * + * const obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ -"use strict"; +micromatch.matchKeys = (obj, patterns, options) => { + if (!utils.isObject(obj)) { + throw new TypeError('Expected the first argument to be an object'); + } + let keys = micromatch(Object.keys(obj), patterns, options); + let res = {}; + for (let key of keys) res[key] = obj[key]; + return res; +}; -const {promisify} = __webpack_require__(111); -const path = __webpack_require__(4); -const globby = __webpack_require__(289); -const isGlob = __webpack_require__(367); -const slash = __webpack_require__(365); -const gracefulFs = __webpack_require__(132); -const isPathCwd = __webpack_require__(368); -const isPathInside = __webpack_require__(369); -const rimraf = __webpack_require__(370); -const pMap = __webpack_require__(371); +/** + * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -const rimrafP = promisify(rimraf); +micromatch.some = (list, patterns, options) => { + let items = [].concat(list); -const rimrafOptions = { - glob: false, - unlink: gracefulFs.unlink, - unlinkSync: gracefulFs.unlinkSync, - chmod: gracefulFs.chmod, - chmodSync: gracefulFs.chmodSync, - stat: gracefulFs.stat, - statSync: gracefulFs.statSync, - lstat: gracefulFs.lstat, - lstatSync: gracefulFs.lstatSync, - rmdir: gracefulFs.rmdir, - rmdirSync: gracefulFs.rmdirSync, - readdir: gracefulFs.readdir, - readdirSync: gracefulFs.readdirSync + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (items.some(item => isMatch(item))) { + return true; + } + } + return false; }; -function safeCheck(file, cwd) { - if (isPathCwd(file)) { - throw new Error('Cannot delete the current working directory. Can be overridden with the `force` option.'); - } - - if (!isPathInside(file, cwd)) { - throw new Error('Cannot delete files/directories outside the current working directory. Can be overridden with the `force` option.'); - } -} - -function normalizePatterns(patterns) { - patterns = Array.isArray(patterns) ? patterns : [patterns]; +/** + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - patterns = patterns.map(pattern => { - if (process.platform === 'win32' && isGlob(pattern) === false) { - return slash(pattern); - } +micromatch.every = (list, patterns, options) => { + let items = [].concat(list); - return pattern; - }); + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (!items.every(item => isMatch(item))) { + return false; + } + } + return true; +}; - return patterns; -} +/** + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * const mm = require('micromatch'); + * // mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -module.exports = async (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => { - options = { - expandDirectories: false, - onlyFiles: false, - followSymbolicLinks: false, - cwd, - ...options - }; +micromatch.all = (str, patterns, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } - patterns = normalizePatterns(patterns); + return [].concat(patterns).every(p => picomatch(p, options)(str)); +}; - const files = (await globby(patterns, options)) - .sort((a, b) => b.localeCompare(a)); - - const mapper = async file => { - file = path.resolve(cwd, file); - - if (!force) { - safeCheck(file, cwd); - } +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * const mm = require('micromatch'); + * // mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `glob` Glob pattern to use for matching. + * @param {String} `input` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the input matches the glob pattern, otherwise `null`. + * @api public + */ - if (!dryRun) { - await rimrafP(file, rimrafOptions); - } +micromatch.capture = (glob, input, options) => { + let posix = utils.isWindows(options); + let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); + let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); - return file; - }; + if (match) { + return match.slice(1).map(v => v === void 0 ? '' : v); + } +}; - const removedFiles = await pMap(files, mapper, options); +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * const mm = require('micromatch'); + * // mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ - removedFiles.sort((a, b) => a.localeCompare(b)); +micromatch.makeRe = (...args) => picomatch.makeRe(...args); - return removedFiles; -}; +/** + * Scan a glob pattern to separate the pattern into segments. Used + * by the [split](#split) method. + * + * ```js + * const mm = require('micromatch'); + * const state = mm.scan(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ -module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options} = {}) => { - options = { - expandDirectories: false, - onlyFiles: false, - followSymbolicLinks: false, - cwd, - ...options - }; +micromatch.scan = (...args) => picomatch.scan(...args); - patterns = normalizePatterns(patterns); +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const mm = require('micromatch'); + * const state = mm(pattern[, options]); + * ``` + * @param {String} `glob` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as regex source string. + * @api public + */ - const files = globby.sync(patterns, options) - .sort((a, b) => b.localeCompare(a)); +micromatch.parse = (patterns, options) => { + let res = []; + for (let pattern of [].concat(patterns || [])) { + for (let str of braces(String(pattern), options)) { + res.push(picomatch.parse(str, options)); + } + } + return res; +}; - const removedFiles = files.map(file => { - file = path.resolve(cwd, file); +/** + * Process the given brace `pattern`. + * + * ```js + * const { braces } = require('micromatch'); + * console.log(braces('foo/{a,b,c}/bar')); + * //=> [ 'foo/(a|b|c)/bar' ] + * + * console.log(braces('foo/{a,b,c}/bar', { expand: true })); + * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] + * ``` + * @param {String} `pattern` String with brace pattern to process. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public + */ - if (!force) { - safeCheck(file, cwd); - } +micromatch.braces = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { + return [pattern]; + } + return braces(pattern, options); +}; - if (!dryRun) { - rimraf.sync(file, rimrafOptions); - } +/** + * Expand braces + */ - return file; - }); +micromatch.braceExpand = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + return micromatch.braces(pattern, { ...options, expand: true }); +}; - removedFiles.sort((a, b) => a.localeCompare(b)); +/** + * Expose micromatch + */ - return removedFiles; -}; +module.exports = micromatch; /***/ }), -/* 289 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(290); -const merge2 = __webpack_require__(291); -const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(292); -const dirGlob = __webpack_require__(361); -const gitignore = __webpack_require__(363); -const {FilterStream, UniqueStream} = __webpack_require__(366); - -const DEFAULT_FILTER = () => false; -const isNegative = pattern => pattern[0] === '!'; +const stringify = __webpack_require__(302); +const compile = __webpack_require__(304); +const expand = __webpack_require__(308); +const parse = __webpack_require__(309); -const assertPatternsInput = patterns => { - if (!patterns.every(pattern => typeof pattern === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } -}; +/** + * Expand the given pattern or create a regex-compatible string. + * + * ```js + * const braces = require('braces'); + * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)'] + * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c'] + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {String} + * @api public + */ -const checkCwdOption = (options = {}) => { - if (!options.cwd) { - return; - } +const braces = (input, options = {}) => { + let output = []; - let stat; - try { - stat = fs.statSync(options.cwd); - } catch (_) { - return; - } + if (Array.isArray(input)) { + for (let pattern of input) { + let result = braces.create(pattern, options); + if (Array.isArray(result)) { + output.push(...result); + } else { + output.push(result); + } + } + } else { + output = [].concat(braces.create(input, options)); + } - if (!stat.isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } + if (options && options.expand === true && options.nodupes === true) { + output = [...new Set(output)]; + } + return output; }; -const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; +/** + * Parse the given `str` with the given `options`. + * + * ```js + * // braces.parse(pattern, [, options]); + * const ast = braces.parse('a/{b,c}/d'); + * console.log(ast); + * ``` + * @param {String} pattern Brace pattern to parse + * @param {Object} options + * @return {Object} Returns an AST + * @api public + */ -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); +braces.parse = (input, options = {}) => parse(input, options); - const globTasks = []; +/** + * Creates a braces string from an AST, or an AST node. + * + * ```js + * const braces = require('braces'); + * let ast = braces.parse('foo/{a,b}/bar'); + * console.log(stringify(ast.nodes[2])); //=> '{a,b}' + * ``` + * @param {String} `input` Brace pattern or AST. + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ - taskOptions = { - ignore: [], - expandDirectories: true, - ...taskOptions - }; +braces.stringify = (input, options = {}) => { + if (typeof input === 'string') { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); +}; - for (const [index, pattern] of patterns.entries()) { - if (isNegative(pattern)) { - continue; - } +/** + * Compiles a brace pattern into a regex-compatible, optimized string. + * This method is called by the main [braces](#braces) function by default. + * + * ```js + * const braces = require('braces'); + * console.log(braces.compile('a/{b,c}/d')); + * //=> ['a/(b|c)/d'] + * ``` + * @param {String} `input` Brace pattern or AST. + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ - const ignore = patterns - .slice(index) - .filter(isNegative) - .map(pattern => pattern.slice(1)); +braces.compile = (input, options = {}) => { + if (typeof input === 'string') { + input = braces.parse(input, options); + } + return compile(input, options); +}; - const options = { - ...taskOptions, - ignore: taskOptions.ignore.concat(ignore) - }; +/** + * Expands a brace pattern into an array. This method is called by the + * main [braces](#braces) function when `options.expand` is true. Before + * using this method it's recommended that you read the [performance notes](#performance)) + * and advantages of using [.compile](#compile) instead. + * + * ```js + * const braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/b/d', 'a/c/d']; + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ - globTasks.push({pattern, options}); - } +braces.expand = (input, options = {}) => { + if (typeof input === 'string') { + input = braces.parse(input, options); + } - return globTasks; -}; + let result = expand(input, options); -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } + // filter out empty strings if specified + if (options.noempty === true) { + result = result.filter(Boolean); + } - if (Array.isArray(task.options.expandDirectories)) { - options = { - ...options, - files: task.options.expandDirectories - }; - } else if (typeof task.options.expandDirectories === 'object') { - options = { - ...options, - ...task.options.expandDirectories - }; - } + // filter out duplicates if specified + if (options.nodupes === true) { + result = [...new Set(result)]; + } - return fn(task.pattern, options); + return result; }; -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; - -const getFilterSync = options => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; -}; +/** + * Processes a brace pattern and returns either an expanded array + * (if `options.expand` is true), a highly optimized regex-compatible string. + * This method is called by the main [braces](#braces) function. + * + * ```js + * const braces = require('braces'); + * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) + * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); - } +braces.create = (input, options = {}) => { + if (input === '' || input.length < 3) { + return [input]; + } - return { - pattern: glob, - options - }; + return options.expand !== true + ? braces.compile(input, options) + : braces.expand(input, options); }; -module.exports = async (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); - - const getFilter = async () => { - return options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; - - const getTasks = async () => { - const tasks = await Promise.all(globTasks.map(async task => { - const globs = await getPattern(task, dirGlob); - return Promise.all(globs.map(globToTask(task))); - })); +/** + * Expose "braces" + */ - return arrayUnion(...tasks); - }; +module.exports = braces; - const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); - const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); - return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); -}; +/***/ }), +/* 302 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports.sync = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); +"use strict"; - const tasks = globTasks.reduce((tasks, task) => { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); - const filter = getFilterSync(options); +const utils = __webpack_require__(303); - return tasks.reduce( - (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), - [] - ).filter(path_ => !filter(path_)); -}; +module.exports = (ast, options = {}) => { + let stringify = (node, parent = {}) => { + let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ''; -module.exports.stream = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return '\\' + node.value; + } + return node.value; + } - const tasks = globTasks.reduce((tasks, task) => { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); + if (node.value) { + return node.value; + } - const filter = getFilterSync(options); - const filterStream = new FilterStream(p => !filter(p)); - const uniqueStream = new UniqueStream(); + if (node.nodes) { + for (let child of node.nodes) { + output += stringify(child); + } + } + return output; + }; - return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) - .pipe(filterStream) - .pipe(uniqueStream); + return stringify(ast); }; -module.exports.generateGlobTasks = generateGlobTasks; - -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => glob.hasMagic(pattern, options)); - -module.exports.gitignore = gitignore; /***/ }), -/* 290 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = (...arguments_) => { - return [...new Set([].concat(...arguments_))]; +exports.isInteger = num => { + if (typeof num === 'number') { + return Number.isInteger(num); + } + if (typeof num === 'string' && num.trim() !== '') { + return Number.isInteger(Number(num)); + } + return false; }; +/** + * Find a node of the given type + */ -/***/ }), -/* 291 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; +exports.find = (node, type) => node.nodes.find(node => node.type === type); -/* - * merge2 - * https://github.com/teambition/merge2 - * - * Copyright (c) 2014-2020 Teambition - * Licensed under the MIT license. +/** + * Find a node of the given type */ -const Stream = __webpack_require__(137) -const PassThrough = Stream.PassThrough -const slice = Array.prototype.slice -module.exports = merge2 +exports.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) return false; + if (!exports.isInteger(min) || !exports.isInteger(max)) return false; + return ((Number(max) - Number(min)) / Number(step)) >= limit; +}; -function merge2 () { - const streamsQueue = [] - const args = slice.call(arguments) - let merging = false - let options = args[args.length - 1] +/** + * Escape the given node with '\\' before node.value + */ - if (options && !Array.isArray(options) && options.pipe == null) { - args.pop() - } else { - options = {} - } +exports.escapeNode = (block, n = 0, type) => { + let node = block.nodes[n]; + if (!node) return; - const doEnd = options.end !== false - const doPipeError = options.pipeError === true - if (options.objectMode == null) { - options.objectMode = true + if ((type && node.type === type) || node.type === 'open' || node.type === 'close') { + if (node.escaped !== true) { + node.value = '\\' + node.value; + node.escaped = true; + } } - if (options.highWaterMark == null) { - options.highWaterMark = 64 * 1024 +}; + +/** + * Returns true if the given brace node should be enclosed in literal braces + */ + +exports.encloseBrace = node => { + if (node.type !== 'brace') return false; + if ((node.commas >> 0 + node.ranges >> 0) === 0) { + node.invalid = true; + return true; } - const mergedStream = PassThrough(options) + return false; +}; - function addStream () { - for (let i = 0, len = arguments.length; i < len; i++) { - streamsQueue.push(pauseStreams(arguments[i], options)) - } - mergeStream() - return this +/** + * Returns true if a brace node is invalid. + */ + +exports.isInvalidBrace = block => { + if (block.type !== 'brace') return false; + if (block.invalid === true || block.dollar) return true; + if ((block.commas >> 0 + block.ranges >> 0) === 0) { + block.invalid = true; + return true; + } + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; } + return false; +}; - function mergeStream () { - if (merging) { - return - } - merging = true +/** + * Returns true if a node is an open or close node + */ - let streams = streamsQueue.shift() - if (!streams) { - process.nextTick(endStream) - return - } - if (!Array.isArray(streams)) { - streams = [streams] - } +exports.isOpenOrClose = node => { + if (node.type === 'open' || node.type === 'close') { + return true; + } + return node.open === true || node.close === true; +}; - let pipesCount = streams.length + 1 +/** + * Reduce an array of text nodes. + */ - function next () { - if (--pipesCount > 0) { - return - } - merging = false - mergeStream() +exports.reduce = nodes => nodes.reduce((acc, node) => { + if (node.type === 'text') acc.push(node.value); + if (node.type === 'range') node.type = 'text'; + return acc; +}, []); + +/** + * Flatten an array + */ + +exports.flatten = (...args) => { + const result = []; + const flat = arr => { + for (let i = 0; i < arr.length; i++) { + let ele = arr[i]; + Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele); } + return result; + }; + flat(args); + return result; +}; - function pipe (stream) { - function onend () { - stream.removeListener('merge2UnpipeEnd', onend) - stream.removeListener('end', onend) - if (doPipeError) { - stream.removeListener('error', onerror) - } - next() - } - function onerror (err) { - mergedStream.emit('error', err) - } - // skip ended stream - if (stream._readableState.endEmitted) { - return next() - } - stream.on('merge2UnpipeEnd', onend) - stream.on('end', onend) +/***/ }), +/* 304 */ +/***/ (function(module, exports, __webpack_require__) { - if (doPipeError) { - stream.on('error', onerror) - } +"use strict"; - stream.pipe(mergedStream, { end: false }) - // compatible for old stream - stream.resume() - } - for (let i = 0; i < streams.length; i++) { - pipe(streams[i]) - } +const fill = __webpack_require__(305); +const utils = __webpack_require__(303); - next() - } +const compile = (ast, options = {}) => { + let walk = (node, parent = {}) => { + let invalidBlock = utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let invalid = invalidBlock === true || invalidNode === true; + let prefix = options.escapeInvalid === true ? '\\' : ''; + let output = ''; - function endStream () { - merging = false - // emit 'queueDrain' when all streams merged. - mergedStream.emit('queueDrain') - if (doEnd) { - mergedStream.end() + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + return prefix + node.value; } - } - mergedStream.setMaxListeners(0) - mergedStream.add = addStream - mergedStream.on('unpipe', function (stream) { - stream.emit('merge2UnpipeEnd') - }) + if (node.type === 'open') { + return invalid ? (prefix + node.value) : '('; + } - if (args.length) { - addStream.apply(null, args) - } - return mergedStream -} + if (node.type === 'close') { + return invalid ? (prefix + node.value) : ')'; + } -// check and pause streams for pipe. -function pauseStreams (streams, options) { - if (!Array.isArray(streams)) { - // Backwards-compat with old-style streams - if (!streams._readableState && streams.pipe) { - streams = streams.pipe(PassThrough(options)) + if (node.type === 'comma') { + return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|'); } - if (!streams._readableState || !streams.pause || !streams.pipe) { - throw new Error('Only readable stream can be merged.') + + if (node.value) { + return node.value; } - streams.pause() - } else { - for (let i = 0, len = streams.length; i < len; i++) { - streams[i] = pauseStreams(streams[i], options) + + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + let range = fill(...args, { ...options, wrap: false, toRegex: true }); + + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } } - } - return streams -} + if (node.nodes) { + for (let child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; -/***/ }), -/* 292 */ -/***/ (function(module, exports, __webpack_require__) { + return walk(ast); +}; -"use strict"; - -const taskManager = __webpack_require__(293); -const async_1 = __webpack_require__(322); -const stream_1 = __webpack_require__(357); -const sync_1 = __webpack_require__(358); -const settings_1 = __webpack_require__(360); -const utils = __webpack_require__(294); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; +module.exports = compile; /***/ }), -/* 293 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -function convertPatternsToTasks(positive, negative, dynamic) { - const positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - const task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; - +/*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ -/***/ }), -/* 294 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(295); -exports.array = array; -const errno = __webpack_require__(296); -exports.errno = errno; -const fs = __webpack_require__(297); -exports.fs = fs; -const path = __webpack_require__(298); -exports.path = path; -const pattern = __webpack_require__(299); -exports.pattern = pattern; -const stream = __webpack_require__(320); -exports.stream = stream; -const string = __webpack_require__(321); -exports.string = string; +const util = __webpack_require__(111); +const toRegexRange = __webpack_require__(306); -/***/ }), -/* 295 */ -/***/ (function(module, exports, __webpack_require__) { +const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} -exports.splitWhen = splitWhen; +const transform = toNumber => { + return value => toNumber === true ? Number(value) : String(value); +}; +const isValidValue = value => { + return typeof value === 'number' || (typeof value === 'string' && value !== ''); +}; -/***/ }), -/* 296 */ -/***/ (function(module, exports, __webpack_require__) { +const isNumber = num => Number.isInteger(+num); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; +const zeros = input => { + let value = `${input}`; + let index = -1; + if (value[0] === '-') value = value.slice(1); + if (value === '0') return false; + while (value[++index] === '0'); + return index > 0; +}; +const stringify = (start, end, options) => { + if (typeof start === 'string' || typeof end === 'string') { + return true; + } + return options.stringify === true; +}; -/***/ }), -/* 297 */ -/***/ (function(module, exports, __webpack_require__) { +const pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === '-' ? '-' : ''; + if (dash) input = input.slice(1); + input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); + } + if (toNumber === false) { + return String(input); + } + return input; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; +const toMaxLen = (input, maxLength) => { + let negative = input[0] === '-' ? '-' : ''; + if (negative) { + input = input.slice(1); + maxLength--; + } + while (input.length < maxLength) input = '0' + input; + return negative ? ('-' + input) : input; +}; +const toSequence = (parts, options) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); -/***/ }), -/* 298 */ -/***/ (function(module, exports, __webpack_require__) { + let prefix = options.capture ? '' : '?:'; + let positives = ''; + let negatives = ''; + let result; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; + if (parts.positives.length) { + positives = parts.positives.join('|'); + } + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.join('|')})`; + } -/***/ }), -/* 299 */ -/***/ (function(module, exports, __webpack_require__) { + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const globParent = __webpack_require__(300); -const micromatch = __webpack_require__(303); -const picomatch = __webpack_require__(314); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - const info = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - // See micromatch/picomatch#58 for more details - if (info.parts.length === 0) { - return [pattern]; - } - return info.parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} -exports.matchAny = matchAny; + if (options.wrap) { + return `(${prefix}${result})`; + } + return result; +}; -/***/ }), -/* 300 */ -/***/ (function(module, exports, __webpack_require__) { +const toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, { wrap: false, ...options }); + } -"use strict"; + let start = String.fromCharCode(a); + if (a === b) return start; + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; +}; -var isGlob = __webpack_require__(301); -var pathPosixDirname = __webpack_require__(4).posix.dirname; -var isWin32 = __webpack_require__(120).platform() === 'win32'; +const toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? '' : '?:'; + return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); + } + return toRegexRange(start, end, options); +}; -var slash = '/'; -var backslash = /\\/g; -var enclosure = /[\{\[].*[\/]*.*[\}\]]$/; -var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; -var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; +const rangeError = (...args) => { + return new RangeError('Invalid range arguments: ' + util.inspect(...args)); +}; -/** - * @param {string} str - * @param {Object} opts - * @param {boolean} [opts.flipBackslashes=true] - */ -module.exports = function globParent(str, opts) { - var options = Object.assign({ flipBackslashes: true }, opts); +const invalidRange = (start, end, options) => { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; +}; - // flip windows path separators - if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { - str = str.replace(backslash, slash); +const invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); } + return []; +}; - // special case for strings ending in enclosure containing path separator - if (enclosure.test(str)) { - str += slash; - } +const fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); - // preserves full path in case of trailing path separator - str += 'a'; + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + } - // remove path parts that are globby - do { - str = pathPosixDirname(str); - } while (isGlob(str) || globby.test(str)); + // fix negative zero + if (a === 0) a = 0; + if (b === 0) b = 0; - // remove escape chars and return result - return str.replace(escaped, '$1'); -}; + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); -/***/ }), -/* 301 */ -/***/ (function(module, exports, __webpack_require__) { + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); + } -/*! - * is-glob - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ + let parts = { negatives: [], positives: [] }; + let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); + let range = []; + let index = 0; -var isExtglob = __webpack_require__(302); -var chars = { '{': '}', '(': ')', '[': ']'}; -var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; -var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); + } + a = descending ? a - step : a + step; + index++; + } -module.exports = function isGlob(str, options) { - if (typeof str !== 'string' || str === '') { - return false; + if (options.toRegex === true) { + return step > 1 + ? toSequence(parts, options) + : toRegex(range, null, { wrap: false, ...options }); } - if (isExtglob(str)) { - return true; + return range; +}; + +const fillLetters = (start, end, step = 1, options = {}) => { + if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { + return invalidRange(start, end, options); } - var regex = strictRegex; - var match; - // optionally relax regex - if (options && options.strict === false) { - regex = relaxedRegex; + let format = options.transform || (val => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); + + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); + + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); } - while ((match = regex.exec(str))) { - if (match[2]) return true; - var idx = match.index + match[0].length; + let range = []; + let index = 0; - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - var open = match[1]; - var close = open ? chars[open] : null; - if (open && close) { - var n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } - } + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } - str = str.slice(idx); + if (options.toRegex === true) { + return toRegex(range, null, { wrap: false, options }); } - return false; + + return range; }; +const fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; + } -/***/ }), -/* 302 */ -/***/ (function(module, exports) { + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); + } -/*! - * is-extglob - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. - */ + if (typeof step === 'function') { + return fill(start, end, 1, { transform: step }); + } -module.exports = function isExtglob(str) { - if (typeof str !== 'string' || str === '') { - return false; + if (isObject(step)) { + return fill(start, end, 0, step); } - var match; - while ((match = /(\\).|([@?!+*]\(.*\))/g.exec(str))) { - if (match[2]) return true; - str = str.slice(match.index + match[0].length); + let opts = { ...options }; + if (opts.capture === true) opts.wrap = true; + step = step || opts.step || 1; + + if (!isNumber(step)) { + if (step != null && !isObject(step)) return invalidStep(step, opts); + return fill(start, end, 1, step); } - return false; + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } + + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); }; +module.exports = fill; + /***/ }), -/* 303 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + */ -const util = __webpack_require__(111); -const braces = __webpack_require__(304); -const picomatch = __webpack_require__(314); -const utils = __webpack_require__(317); -const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); -/** - * Returns an array of strings that match one or more glob patterns. - * - * ```js - * const mm = require('micromatch'); - * // mm(list, patterns[, options]); - * - * console.log(mm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {String|Array} list List of strings to match. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} options See available [options](#options) - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ +const isNumber = __webpack_require__(307); -const micromatch = (list, patterns, options) => { - patterns = [].concat(patterns); - list = [].concat(list); +const toRegexRange = (min, max, options) => { + if (isNumber(min) === false) { + throw new TypeError('toRegexRange: expected the first argument to be a number'); + } - let omit = new Set(); - let keep = new Set(); - let items = new Set(); - let negatives = 0; + if (max === void 0 || min === max) { + return String(min); + } - let onResult = state => { - items.add(state.output); - if (options && options.onResult) { - options.onResult(state); - } - }; + if (isNumber(max) === false) { + throw new TypeError('toRegexRange: expected the second argument to be a number.'); + } - for (let i = 0; i < patterns.length; i++) { - let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); - let negated = isMatch.state.negated || isMatch.state.negatedExtglob; - if (negated) negatives++; + let opts = { relaxZeros: true, ...options }; + if (typeof opts.strictZeros === 'boolean') { + opts.relaxZeros = opts.strictZeros === false; + } - for (let item of list) { - let matched = isMatch(item, true); + let relax = String(opts.relaxZeros); + let shorthand = String(opts.shorthand); + let capture = String(opts.capture); + let wrap = String(opts.wrap); + let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap; - let match = negated ? !matched.isMatch : matched.isMatch; - if (!match) continue; + if (toRegexRange.cache.hasOwnProperty(cacheKey)) { + return toRegexRange.cache[cacheKey].result; + } - if (negated) { - omit.add(matched.output); - } else { - omit.delete(matched.output); - keep.add(matched.output); - } + let a = Math.min(min, max); + let b = Math.max(min, max); + + if (Math.abs(a - b) === 1) { + let result = min + '|' + max; + if (opts.capture) { + return `(${result})`; + } + if (opts.wrap === false) { + return result; } + return `(?:${result})`; } - let result = negatives === patterns.length ? [...items] : [...keep]; - let matches = result.filter(item => !omit.has(item)); + let isPadded = hasPadding(min) || hasPadding(max); + let state = { min, max, a, b }; + let positives = []; + let negatives = []; - if (options && matches.length === 0) { - if (options.failglob === true) { - throw new Error(`No matches found for "${patterns.join(', ')}"`); - } + if (isPadded) { + state.isPadded = isPadded; + state.maxLen = String(state.max).length; + } - if (options.nonull === true || options.nullglob === true) { - return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; - } + if (a < 0) { + let newMin = b < 0 ? Math.abs(b) : 1; + negatives = splitToPatterns(newMin, Math.abs(a), state, opts); + a = state.a = 0; } - return matches; -}; + if (b >= 0) { + positives = splitToPatterns(a, b, state, opts); + } -/** - * Backwards compatibility - */ + state.negatives = negatives; + state.positives = positives; + state.result = collatePatterns(negatives, positives, opts); -micromatch.match = micromatch; + if (opts.capture === true) { + state.result = `(${state.result})`; + } else if (opts.wrap !== false && (positives.length + negatives.length) > 1) { + state.result = `(?:${state.result})`; + } -/** - * Returns a matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * const mm = require('micromatch'); - * // mm.matcher(pattern[, options]); - * - * const isMatch = mm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` - * @return {Function} Returns a matcher function. - * @api public - */ + toRegexRange.cache[cacheKey] = state; + return state.result; +}; -micromatch.matcher = (pattern, options) => picomatch(pattern, options); +function collatePatterns(neg, pos, options) { + let onlyNegative = filterPatterns(neg, pos, '-', false, options) || []; + let onlyPositive = filterPatterns(pos, neg, '', false, options) || []; + let intersected = filterPatterns(neg, pos, '-?', true, options) || []; + let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join('|'); +} -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const mm = require('micromatch'); - * // mm.isMatch(string, patterns[, options]); - * - * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(mm.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ +function splitToRanges(min, max) { + let nines = 1; + let zeros = 1; -micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + let stop = countNines(min, nines); + let stops = new Set([max]); -/** - * Backwards compatibility - */ + while (min <= stop && stop <= max) { + stops.add(stop); + nines += 1; + stop = countNines(min, nines); + } -micromatch.any = micromatch.isMatch; + stop = countZeros(max + 1, zeros) - 1; -/** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.not(list, patterns[, options]); - * - * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] - * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. - * @api public + while (min < stop && stop <= max) { + stops.add(stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + + stops = [...stops]; + stops.sort(compare); + return stops; +} + +/** + * Convert a range to a regex pattern + * @param {Number} `start` + * @param {Number} `stop` + * @return {String} */ -micromatch.not = (list, patterns, options = {}) => { - patterns = [].concat(patterns).map(String); - let result = new Set(); - let items = []; +function rangeToPattern(start, stop, options) { + if (start === stop) { + return { pattern: start, count: [], digits: 0 }; + } - let onResult = state => { - if (options.onResult) options.onResult(state); - items.push(state.output); - }; + let zipped = zip(start, stop); + let digits = zipped.length; + let pattern = ''; + let count = 0; - let matches = micromatch(list, patterns, { ...options, onResult }); + for (let i = 0; i < digits; i++) { + let [startDigit, stopDigit] = zipped[i]; - for (let item of items) { - if (!matches.includes(item)) { - result.add(item); - } - } - return [...result]; -}; + if (startDigit === stopDigit) { + pattern += startDigit; -/** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var mm = require('micromatch'); - * // mm.contains(string, pattern[, options]); - * - * console.log(mm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(mm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the patter matches any part of `str`. - * @api public - */ + } else if (startDigit !== '0' || stopDigit !== '9') { + pattern += toCharacterClass(startDigit, stopDigit, options); -micromatch.contains = (str, pattern, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } else { + count++; + } } - if (Array.isArray(pattern)) { - return pattern.some(p => micromatch.contains(str, p, options)); + if (count) { + pattern += options.shorthand === true ? '\\d' : '[0-9]'; } - if (typeof pattern === 'string') { - if (isEmptyString(str) || isEmptyString(pattern)) { - return false; + return { pattern, count: [count], digits }; +} + +function splitToPatterns(min, max, tok, options) { + let ranges = splitToRanges(min, max); + let tokens = []; + let start = min; + let prev; + + for (let i = 0; i < ranges.length; i++) { + let max = ranges[i]; + let obj = rangeToPattern(String(start), String(max), options); + let zeros = ''; + + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.count.length > 1) { + prev.count.pop(); + } + + prev.count.push(obj.count[0]); + prev.string = prev.pattern + toQuantifier(prev.count); + start = max + 1; + continue; } - if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { - return true; + if (tok.isPadded) { + zeros = padZeros(max, tok, options); } - } - return micromatch.isMatch(str, pattern, { ...options, contains: true }); -}; + obj.string = zeros + obj.pattern + toQuantifier(obj.count); + tokens.push(obj); + start = max + 1; + prev = obj; + } -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * const mm = require('micromatch'); - * // mm.matchKeys(object, patterns[, options]); - * - * const obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(mm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public - */ + return tokens; +} -micromatch.matchKeys = (obj, patterns, options) => { - if (!utils.isObject(obj)) { - throw new TypeError('Expected the first argument to be an object'); - } - let keys = micromatch(Object.keys(obj), patterns, options); - let res = {}; - for (let key of keys) res[key] = obj[key]; - return res; -}; +function filterPatterns(arr, comparison, prefix, intersection, options) { + let result = []; -/** - * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.some(list, patterns[, options]); - * - * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + for (let ele of arr) { + let { string } = ele; -micromatch.some = (list, patterns, options) => { - let items = [].concat(list); + // only push if _both_ are negative... + if (!intersection && !contains(comparison, 'string', string)) { + result.push(prefix + string); + } - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (items.some(item => isMatch(item))) { - return true; + // or _both_ are positive + if (intersection && contains(comparison, 'string', string)) { + result.push(prefix + string); } } - return false; -}; + return result; +} /** - * Returns true if every string in the given `list` matches - * any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.every(list, patterns[, options]); - * - * console.log(mm.every('foo.js', ['foo.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public + * Zip strings */ -micromatch.every = (list, patterns, options) => { - let items = [].concat(list); +function zip(a, b) { + let arr = []; + for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]); + return arr; +} - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (!items.every(item => isMatch(item))) { - return false; - } - } - return true; -}; +function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; +} -/** - * Returns true if **all** of the given `patterns` match - * the specified string. - * - * ```js - * const mm = require('micromatch'); - * // mm.all(string, patterns[, options]); - * - * console.log(mm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ +function contains(arr, key, val) { + return arr.some(ele => ele[key] === val); +} -micromatch.all = (str, patterns, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); +function countNines(min, len) { + return Number(String(min).slice(0, -len) + '9'.repeat(len)); +} + +function countZeros(integer, zeros) { + return integer - (integer % Math.pow(10, zeros)); +} + +function toQuantifier(digits) { + let [start = 0, stop = ''] = digits; + if (stop || start > 1) { + return `{${start + (stop ? ',' + stop : '')}}`; } + return ''; +} - return [].concat(patterns).every(p => picomatch(p, options)(str)); -}; +function toCharacterClass(a, b, options) { + return `[${a}${(b - a === 1) ? '' : '-'}${b}]`; +} -/** - * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. - * - * ```js - * const mm = require('micromatch'); - * // mm.capture(pattern, string[, options]); - * - * console.log(mm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(mm.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `glob` Glob pattern to use for matching. - * @param {String} `input` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the input matches the glob pattern, otherwise `null`. - * @api public - */ +function hasPadding(str) { + return /^-?(0+)\d/.test(str); +} -micromatch.capture = (glob, input, options) => { - let posix = utils.isWindows(options); - let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); - let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); +function padZeros(value, tok, options) { + if (!tok.isPadded) { + return value; + } - if (match) { - return match.slice(1).map(v => v === void 0 ? '' : v); + let diff = Math.abs(tok.maxLen - String(value).length); + let relax = options.relaxZeros !== false; + + switch (diff) { + case 0: + return ''; + case 1: + return relax ? '0?' : '0'; + case 2: + return relax ? '0{0,2}' : '00'; + default: { + return relax ? `0{0,${diff}}` : `0{${diff}}`; + } } -}; +} /** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * const mm = require('micromatch'); - * // mm.makeRe(pattern[, options]); - * - * console.log(mm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ - * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public + * Cache */ -micromatch.makeRe = (...args) => picomatch.makeRe(...args); +toRegexRange.cache = {}; +toRegexRange.clearCache = () => (toRegexRange.cache = {}); /** - * Scan a glob pattern to separate the pattern into segments. Used - * by the [split](#split) method. - * - * ```js - * const mm = require('micromatch'); - * const state = mm.scan(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public + * Expose `toRegexRange` */ -micromatch.scan = (...args) => picomatch.scan(...args); +module.exports = toRegexRange; -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const mm = require('micromatch'); - * const state = mm(pattern[, options]); - * ``` - * @param {String} `glob` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as regex source string. - * @api public - */ -micromatch.parse = (patterns, options) => { - let res = []; - for (let pattern of [].concat(patterns || [])) { - for (let str of braces(String(pattern), options)) { - res.push(picomatch.parse(str, options)); - } - } - return res; -}; +/***/ }), +/* 307 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Process the given brace `pattern`. - * - * ```js - * const { braces } = require('micromatch'); - * console.log(braces('foo/{a,b,c}/bar')); - * //=> [ 'foo/(a|b|c)/bar' ] +"use strict"; +/*! + * is-number * - * console.log(braces('foo/{a,b,c}/bar', { expand: true })); - * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] - * ``` - * @param {String} `pattern` String with brace pattern to process. - * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. - * @return {Array} - * @api public + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.braces = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { - return [pattern]; - } - return braces(pattern, options); -}; -/** - * Expand braces - */ -micromatch.braceExpand = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - return micromatch.braces(pattern, { ...options, expand: true }); +module.exports = function(num) { + if (typeof num === 'number') { + return num - num === 0; + } + if (typeof num === 'string' && num.trim() !== '') { + return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); + } + return false; }; -/** - * Expose micromatch - */ - -module.exports = micromatch; - /***/ }), -/* 304 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(305); -const compile = __webpack_require__(307); -const expand = __webpack_require__(311); -const parse = __webpack_require__(312); +const fill = __webpack_require__(305); +const stringify = __webpack_require__(302); +const utils = __webpack_require__(303); -/** - * Expand the given pattern or create a regex-compatible string. - * - * ```js - * const braces = require('braces'); - * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)'] - * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c'] - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {String} - * @api public - */ +const append = (queue = '', stash = '', enclose = false) => { + let result = []; -const braces = (input, options = {}) => { - let output = []; + queue = [].concat(queue); + stash = [].concat(stash); - if (Array.isArray(input)) { - for (let pattern of input) { - let result = braces.create(pattern, options); - if (Array.isArray(result)) { - output.push(...result); - } else { - output.push(result); - } - } - } else { - output = [].concat(braces.create(input, options)); + if (!stash.length) return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash; } - if (options && options.expand === true && options.nodupes === true) { - output = [...new Set(output)]; + for (let item of queue) { + if (Array.isArray(item)) { + for (let value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === 'string') ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele)); + } + } } - return output; + return utils.flatten(result); }; -/** - * Parse the given `str` with the given `options`. - * - * ```js - * // braces.parse(pattern, [, options]); - * const ast = braces.parse('a/{b,c}/d'); - * console.log(ast); - * ``` - * @param {String} pattern Brace pattern to parse - * @param {Object} options - * @return {Object} Returns an AST - * @api public - */ +const expand = (ast, options = {}) => { + let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit; -braces.parse = (input, options = {}) => parse(input, options); + let walk = (node, parent = {}) => { + node.queue = []; -/** - * Creates a braces string from an AST, or an AST node. - * - * ```js - * const braces = require('braces'); - * let ast = braces.parse('foo/{a,b}/bar'); - * console.log(stringify(ast.nodes[2])); //=> '{a,b}' - * ``` - * @param {String} `input` Brace pattern or AST. - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ - -braces.stringify = (input, options = {}) => { - if (typeof input === 'string') { - return stringify(braces.parse(input, options), options); - } - return stringify(input, options); -}; - -/** - * Compiles a brace pattern into a regex-compatible, optimized string. - * This method is called by the main [braces](#braces) function by default. - * - * ```js - * const braces = require('braces'); - * console.log(braces.compile('a/{b,c}/d')); - * //=> ['a/(b|c)/d'] - * ``` - * @param {String} `input` Brace pattern or AST. - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ - -braces.compile = (input, options = {}) => { - if (typeof input === 'string') { - input = braces.parse(input, options); - } - return compile(input, options); -}; - -/** - * Expands a brace pattern into an array. This method is called by the - * main [braces](#braces) function when `options.expand` is true. Before - * using this method it's recommended that you read the [performance notes](#performance)) - * and advantages of using [.compile](#compile) instead. - * - * ```js - * const braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/b/d', 'a/c/d']; - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ - -braces.expand = (input, options = {}) => { - if (typeof input === 'string') { - input = braces.parse(input, options); - } - - let result = expand(input, options); - - // filter out empty strings if specified - if (options.noempty === true) { - result = result.filter(Boolean); - } - - // filter out duplicates if specified - if (options.nodupes === true) { - result = [...new Set(result)]; - } - - return result; -}; + let p = parent; + let q = parent.queue; -/** - * Processes a brace pattern and returns either an expanded array - * (if `options.expand` is true), a highly optimized regex-compatible string. - * This method is called by the main [braces](#braces) function. - * - * ```js - * const braces = require('braces'); - * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) - * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ + while (p.type !== 'brace' && p.type !== 'root' && p.parent) { + p = p.parent; + q = p.queue; + } -braces.create = (input, options = {}) => { - if (input === '' || input.length < 3) { - return [input]; - } + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; + } - return options.expand !== true - ? braces.compile(input, options) - : braces.expand(input, options); -}; + if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ['{}'])); + return; + } -/** - * Expose "braces" - */ + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); -module.exports = braces; + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); + } + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); + } -/***/ }), -/* 305 */ -/***/ (function(module, exports, __webpack_require__) { + q.push(append(q.pop(), range)); + node.nodes = []; + return; + } -"use strict"; + let enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; + while (block.type !== 'brace' && block.type !== 'root' && block.parent) { + block = block.parent; + queue = block.queue; + } -const utils = __webpack_require__(306); + for (let i = 0; i < node.nodes.length; i++) { + let child = node.nodes[i]; -module.exports = (ast, options = {}) => { - let stringify = (node, parent = {}) => { - let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); - let invalidNode = node.invalid === true && options.escapeInvalid === true; - let output = ''; + if (child.type === 'comma' && node.type === 'brace') { + if (i === 1) queue.push(''); + queue.push(''); + continue; + } - if (node.value) { - if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { - return '\\' + node.value; + if (child.type === 'close') { + q.push(append(q.pop(), queue, enclose)); + continue; } - return node.value; - } - if (node.value) { - return node.value; - } + if (child.value && child.type !== 'open') { + queue.push(append(queue.pop(), child.value)); + continue; + } - if (node.nodes) { - for (let child of node.nodes) { - output += stringify(child); + if (child.nodes) { + walk(child, node); } } - return output; + + return queue; }; - return stringify(ast); + return utils.flatten(walk(ast)); }; +module.exports = expand; /***/ }), -/* 306 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -exports.isInteger = num => { - if (typeof num === 'number') { - return Number.isInteger(num); - } - if (typeof num === 'string' && num.trim() !== '') { - return Number.isInteger(Number(num)); - } - return false; -}; +const stringify = __webpack_require__(302); /** - * Find a node of the given type + * Constants */ -exports.find = (node, type) => node.nodes.find(node => node.type === type); +const { + MAX_LENGTH, + CHAR_BACKSLASH, /* \ */ + CHAR_BACKTICK, /* ` */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, /* ] */ + CHAR_DOUBLE_QUOTE, /* " */ + CHAR_SINGLE_QUOTE, /* ' */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE +} = __webpack_require__(310); /** - * Find a node of the given type + * parse */ -exports.exceedsLimit = (min, max, step = 1, limit) => { - if (limit === false) return false; - if (!exports.isInteger(min) || !exports.isInteger(max)) return false; - return ((Number(max) - Number(min)) / Number(step)) >= limit; -}; +const parse = (input, options = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } -/** - * Escape the given node with '\\' before node.value - */ + let opts = options || {}; + let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } -exports.escapeNode = (block, n = 0, type) => { - let node = block.nodes[n]; - if (!node) return; + let ast = { type: 'root', input, nodes: [] }; + let stack = [ast]; + let block = ast; + let prev = ast; + let brackets = 0; + let length = input.length; + let index = 0; + let depth = 0; + let value; + let memo = {}; - if ((type && node.type === type) || node.type === 'open' || node.type === 'close') { - if (node.escaped !== true) { - node.value = '\\' + node.value; - node.escaped = true; + /** + * Helpers + */ + + const advance = () => input[index++]; + const push = node => { + if (node.type === 'text' && prev.type === 'dot') { + prev.type = 'text'; } - } -}; -/** - * Returns true if the given brace node should be enclosed in literal braces - */ + if (prev && prev.type === 'text' && node.type === 'text') { + prev.value += node.value; + return; + } -exports.encloseBrace = node => { - if (node.type !== 'brace') return false; - if ((node.commas >> 0 + node.ranges >> 0) === 0) { - node.invalid = true; - return true; - } - return false; -}; + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; -/** - * Returns true if a brace node is invalid. - */ + push({ type: 'bos' }); -exports.isInvalidBrace = block => { - if (block.type !== 'brace') return false; - if (block.invalid === true || block.dollar) return true; - if ((block.commas >> 0 + block.ranges >> 0) === 0) { - block.invalid = true; - return true; - } - if (block.open !== true || block.close !== true) { - block.invalid = true; - return true; - } - return false; -}; + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); -/** - * Returns true if a node is an open or close node - */ + /** + * Invalid chars + */ -exports.isOpenOrClose = node => { - if (node.type === 'open' || node.type === 'close') { - return true; - } - return node.open === true || node.close === true; -}; + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } -/** - * Reduce an array of text nodes. - */ + /** + * Escaped chars + */ -exports.reduce = nodes => nodes.reduce((acc, node) => { - if (node.type === 'text') acc.push(node.value); - if (node.type === 'range') node.type = 'text'; - return acc; -}, []); + if (value === CHAR_BACKSLASH) { + push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() }); + continue; + } -/** - * Flatten an array - */ + /** + * Right square bracket (literal): ']' + */ -exports.flatten = (...args) => { - const result = []; - const flat = arr => { - for (let i = 0; i < arr.length; i++) { - let ele = arr[i]; - Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele); + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: 'text', value: '\\' + value }); + continue; } - return result; - }; - flat(args); - return result; -}; + /** + * Left square bracket: '[' + */ -/***/ }), -/* 307 */ -/***/ (function(module, exports, __webpack_require__) { + if (value === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; -"use strict"; + let closed = true; + let next; + while (index < length && (next = advance())) { + value += next; -const fill = __webpack_require__(308); -const utils = __webpack_require__(306); + if (next === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + continue; + } -const compile = (ast, options = {}) => { - let walk = (node, parent = {}) => { - let invalidBlock = utils.isInvalidBrace(parent); - let invalidNode = node.invalid === true && options.escapeInvalid === true; - let invalid = invalidBlock === true || invalidNode === true; - let prefix = options.escapeInvalid === true ? '\\' : ''; - let output = ''; + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } - if (node.isOpen === true) { - return prefix + node.value; - } - if (node.isClose === true) { - return prefix + node.value; - } + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + brackets--; - if (node.type === 'open') { - return invalid ? (prefix + node.value) : '('; - } + if (brackets === 0) { + break; + } + } + } - if (node.type === 'close') { - return invalid ? (prefix + node.value) : ')'; + push({ type: 'text', value }); + continue; } - if (node.type === 'comma') { - return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|'); - } + /** + * Parentheses + */ - if (node.value) { - return node.value; + if (value === CHAR_LEFT_PARENTHESES) { + block = push({ type: 'paren', nodes: [] }); + stack.push(block); + push({ type: 'text', value }); + continue; } - if (node.nodes && node.ranges > 0) { - let args = utils.reduce(node.nodes); - let range = fill(...args, { ...options, wrap: false, toRegex: true }); - - if (range.length !== 0) { - return args.length > 1 && range.length > 1 ? `(${range})` : range; + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== 'paren') { + push({ type: 'text', value }); + continue; } + block = stack.pop(); + push({ type: 'text', value }); + block = stack[stack.length - 1]; + continue; } - if (node.nodes) { - for (let child of node.nodes) { - output += walk(child, node); + /** + * Quotes: '|"|` + */ + + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + let open = value; + let next; + + if (options.keepQuotes !== true) { + value = ''; } - } - return output; - }; - return walk(ast); -}; + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } -module.exports = compile; + if (next === open) { + if (options.keepQuotes === true) value += next; + break; + } + value += next; + } -/***/ }), -/* 308 */ -/***/ (function(module, exports, __webpack_require__) { + push({ type: 'text', value }); + continue; + } -"use strict"; -/*! - * fill-range - * - * Copyright (c) 2014-present, Jon Schlinkert. - * Licensed under the MIT License. - */ + /** + * Left curly brace: '{' + */ + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true; + let brace = { + type: 'brace', + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; -const util = __webpack_require__(111); -const toRegexRange = __webpack_require__(309); + block = push(brace); + stack.push(block); + push({ type: 'open', value }); + continue; + } -const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); + /** + * Right curly brace: '}' + */ -const transform = toNumber => { - return value => toNumber === true ? Number(value) : String(value); -}; + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== 'brace') { + push({ type: 'text', value }); + continue; + } -const isValidValue = value => { - return typeof value === 'number' || (typeof value === 'string' && value !== ''); -}; + let type = 'close'; + block = stack.pop(); + block.close = true; -const isNumber = num => Number.isInteger(+num); + push({ type, value }); + depth--; -const zeros = input => { - let value = `${input}`; - let index = -1; - if (value[0] === '-') value = value.slice(1); - if (value === '0') return false; - while (value[++index] === '0'); - return index > 0; -}; + block = stack[stack.length - 1]; + continue; + } -const stringify = (start, end, options) => { - if (typeof start === 'string' || typeof end === 'string') { - return true; - } - return options.stringify === true; -}; - -const pad = (input, maxLength, toNumber) => { - if (maxLength > 0) { - let dash = input[0] === '-' ? '-' : ''; - if (dash) input = input.slice(1); - input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); - } - if (toNumber === false) { - return String(input); - } - return input; -}; - -const toMaxLen = (input, maxLength) => { - let negative = input[0] === '-' ? '-' : ''; - if (negative) { - input = input.slice(1); - maxLength--; - } - while (input.length < maxLength) input = '0' + input; - return negative ? ('-' + input) : input; -}; - -const toSequence = (parts, options) => { - parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); - parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); - - let prefix = options.capture ? '' : '?:'; - let positives = ''; - let negatives = ''; - let result; - - if (parts.positives.length) { - positives = parts.positives.join('|'); - } + /** + * Comma: ',' + */ - if (parts.negatives.length) { - negatives = `-(${prefix}${parts.negatives.join('|')})`; - } + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + let open = block.nodes.shift(); + block.nodes = [open, { type: 'text', value: stringify(block) }]; + } - if (positives && negatives) { - result = `${positives}|${negatives}`; - } else { - result = positives || negatives; - } + push({ type: 'comma', value }); + block.commas++; + continue; + } - if (options.wrap) { - return `(${prefix}${result})`; - } + /** + * Dot: '.' + */ - return result; -}; + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + let siblings = block.nodes; -const toRange = (a, b, isNumbers, options) => { - if (isNumbers) { - return toRegexRange(a, b, { wrap: false, ...options }); - } + if (depth === 0 || siblings.length === 0) { + push({ type: 'text', value }); + continue; + } - let start = String.fromCharCode(a); - if (a === b) return start; + if (prev.type === 'dot') { + block.range = []; + prev.value += value; + prev.type = 'range'; - let stop = String.fromCharCode(b); - return `[${start}-${stop}]`; -}; + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = 'text'; + continue; + } -const toRegex = (start, end, options) => { - if (Array.isArray(start)) { - let wrap = options.wrap === true; - let prefix = options.capture ? '' : '?:'; - return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); - } - return toRegexRange(start, end, options); -}; + block.ranges++; + block.args = []; + continue; + } -const rangeError = (...args) => { - return new RangeError('Invalid range arguments: ' + util.inspect(...args)); -}; + if (prev.type === 'range') { + siblings.pop(); -const invalidRange = (start, end, options) => { - if (options.strictRanges === true) throw rangeError([start, end]); - return []; -}; + let before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } -const invalidStep = (step, options) => { - if (options.strictRanges === true) { - throw new TypeError(`Expected step "${step}" to be a number`); - } - return []; -}; + push({ type: 'dot', value }); + continue; + } -const fillNumbers = (start, end, step = 1, options = {}) => { - let a = Number(start); - let b = Number(end); + /** + * Text + */ - if (!Number.isInteger(a) || !Number.isInteger(b)) { - if (options.strictRanges === true) throw rangeError([start, end]); - return []; + push({ type: 'text', value }); } - // fix negative zero - if (a === 0) a = 0; - if (b === 0) b = 0; - - let descending = a > b; - let startString = String(start); - let endString = String(end); - let stepString = String(step); - step = Math.max(Math.abs(step), 1); - - let padded = zeros(startString) || zeros(endString) || zeros(stepString); - let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; - let toNumber = padded === false && stringify(start, end, options) === false; - let format = options.transform || transform(toNumber); - - if (options.toRegex && step === 1) { - return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); - } + // Mark imbalanced braces and brackets as invalid + do { + block = stack.pop(); - let parts = { negatives: [], positives: [] }; - let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); - let range = []; - let index = 0; + if (block.type !== 'root') { + block.nodes.forEach(node => { + if (!node.nodes) { + if (node.type === 'open') node.isOpen = true; + if (node.type === 'close') node.isClose = true; + if (!node.nodes) node.type = 'text'; + node.invalid = true; + } + }); - while (descending ? a >= b : a <= b) { - if (options.toRegex === true && step > 1) { - push(a); - } else { - range.push(pad(format(a, index), maxLen, toNumber)); + // get the location of the block on parent.nodes (block's siblings) + let parent = stack[stack.length - 1]; + let index = parent.nodes.indexOf(block); + // replace the (invalid) block with it's nodes + parent.nodes.splice(index, 1, ...block.nodes); } - a = descending ? a - step : a + step; - index++; - } - - if (options.toRegex === true) { - return step > 1 - ? toSequence(parts, options) - : toRegex(range, null, { wrap: false, ...options }); - } + } while (stack.length > 0); - return range; + push({ type: 'eos' }); + return ast; }; -const fillLetters = (start, end, step = 1, options = {}) => { - if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { - return invalidRange(start, end, options); - } - - - let format = options.transform || (val => String.fromCharCode(val)); - let a = `${start}`.charCodeAt(0); - let b = `${end}`.charCodeAt(0); +module.exports = parse; - let descending = a > b; - let min = Math.min(a, b); - let max = Math.max(a, b); - if (options.toRegex && step === 1) { - return toRange(min, max, false, options); - } +/***/ }), +/* 310 */ +/***/ (function(module, exports, __webpack_require__) { - let range = []; - let index = 0; +"use strict"; - while (descending ? a >= b : a <= b) { - range.push(format(a, index)); - a = descending ? a - step : a + step; - index++; - } - if (options.toRegex === true) { - return toRegex(range, null, { wrap: false, options }); - } +module.exports = { + MAX_LENGTH: 1024 * 64, - return range; -}; + // Digits + CHAR_0: '0', /* 0 */ + CHAR_9: '9', /* 9 */ -const fill = (start, end, step, options = {}) => { - if (end == null && isValidValue(start)) { - return [start]; - } + // Alphabet chars. + CHAR_UPPERCASE_A: 'A', /* A */ + CHAR_LOWERCASE_A: 'a', /* a */ + CHAR_UPPERCASE_Z: 'Z', /* Z */ + CHAR_LOWERCASE_Z: 'z', /* z */ - if (!isValidValue(start) || !isValidValue(end)) { - return invalidRange(start, end, options); - } + CHAR_LEFT_PARENTHESES: '(', /* ( */ + CHAR_RIGHT_PARENTHESES: ')', /* ) */ - if (typeof step === 'function') { - return fill(start, end, 1, { transform: step }); - } + CHAR_ASTERISK: '*', /* * */ - if (isObject(step)) { - return fill(start, end, 0, step); - } + // Non-alphabetic chars. + CHAR_AMPERSAND: '&', /* & */ + CHAR_AT: '@', /* @ */ + CHAR_BACKSLASH: '\\', /* \ */ + CHAR_BACKTICK: '`', /* ` */ + CHAR_CARRIAGE_RETURN: '\r', /* \r */ + CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */ + CHAR_COLON: ':', /* : */ + CHAR_COMMA: ',', /* , */ + CHAR_DOLLAR: '$', /* . */ + CHAR_DOT: '.', /* . */ + CHAR_DOUBLE_QUOTE: '"', /* " */ + CHAR_EQUAL: '=', /* = */ + CHAR_EXCLAMATION_MARK: '!', /* ! */ + CHAR_FORM_FEED: '\f', /* \f */ + CHAR_FORWARD_SLASH: '/', /* / */ + CHAR_HASH: '#', /* # */ + CHAR_HYPHEN_MINUS: '-', /* - */ + CHAR_LEFT_ANGLE_BRACKET: '<', /* < */ + CHAR_LEFT_CURLY_BRACE: '{', /* { */ + CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */ + CHAR_LINE_FEED: '\n', /* \n */ + CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */ + CHAR_PERCENT: '%', /* % */ + CHAR_PLUS: '+', /* + */ + CHAR_QUESTION_MARK: '?', /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */ + CHAR_RIGHT_CURLY_BRACE: '}', /* } */ + CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */ + CHAR_SEMICOLON: ';', /* ; */ + CHAR_SINGLE_QUOTE: '\'', /* ' */ + CHAR_SPACE: ' ', /* */ + CHAR_TAB: '\t', /* \t */ + CHAR_UNDERSCORE: '_', /* _ */ + CHAR_VERTICAL_LINE: '|', /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */ +}; - let opts = { ...options }; - if (opts.capture === true) opts.wrap = true; - step = step || opts.step || 1; - if (!isNumber(step)) { - if (step != null && !isObject(step)) return invalidStep(step, opts); - return fill(start, end, 1, step); - } +/***/ }), +/* 311 */ +/***/ (function(module, exports, __webpack_require__) { - if (isNumber(start) && isNumber(end)) { - return fillNumbers(start, end, step, opts); - } +"use strict"; - return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); -}; -module.exports = fill; +module.exports = __webpack_require__(312); /***/ }), -/* 309 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * to-regex-range - * - * Copyright (c) 2015-present, Jon Schlinkert. - * Released under the MIT License. - */ +const path = __webpack_require__(4); +const scan = __webpack_require__(313); +const parse = __webpack_require__(316); +const utils = __webpack_require__(314); +const constants = __webpack_require__(315); +const isObject = val => val && typeof val === 'object' && !Array.isArray(val); -const isNumber = __webpack_require__(310); +/** + * Creates a matcher function from one or more glob patterns. The + * returned function takes a string to match as its first argument, + * and returns true if the string is a match. The returned matcher + * function also takes a boolean as the second argument that, when true, + * returns an object with additional information. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch(glob[, options]); + * + * const isMatch = picomatch('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @name picomatch + * @param {String|Array} `globs` One or more glob patterns. + * @param {Object=} `options` + * @return {Function=} Returns a matcher function. + * @api public + */ -const toRegexRange = (min, max, options) => { - if (isNumber(min) === false) { - throw new TypeError('toRegexRange: expected the first argument to be a number'); +const picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map(input => picomatch(input, options, returnState)); + const arrayMatcher = str => { + for (const isMatch of fns) { + const state = isMatch(str); + if (state) return state; + } + return false; + }; + return arrayMatcher; } - if (max === void 0 || min === max) { - return String(min); - } + const isState = isObject(glob) && glob.tokens && glob.input; - if (isNumber(max) === false) { - throw new TypeError('toRegexRange: expected the second argument to be a number.'); + if (glob === '' || (typeof glob !== 'string' && !isState)) { + throw new TypeError('Expected pattern to be a non-empty string'); } - let opts = { relaxZeros: true, ...options }; - if (typeof opts.strictZeros === 'boolean') { - opts.relaxZeros = opts.strictZeros === false; - } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState + ? picomatch.compileRe(glob, options) + : picomatch.makeRe(glob, options, false, true); - let relax = String(opts.relaxZeros); - let shorthand = String(opts.shorthand); - let capture = String(opts.capture); - let wrap = String(opts.wrap); - let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap; + const state = regex.state; + delete regex.state; - if (toRegexRange.cache.hasOwnProperty(cacheKey)) { - return toRegexRange.cache[cacheKey].result; + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); } - let a = Math.min(min, max); - let b = Math.max(min, max); + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; - if (Math.abs(a - b) === 1) { - let result = min + '|' + max; - if (opts.capture) { - return `(${result})`; + if (typeof opts.onResult === 'function') { + opts.onResult(result); } - if (opts.wrap === false) { - return result; + + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; } - return `(?:${result})`; - } - let isPadded = hasPadding(min) || hasPadding(max); - let state = { min, max, a, b }; - let positives = []; - let negatives = []; + if (isIgnored(input)) { + if (typeof opts.onIgnore === 'function') { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } - if (isPadded) { - state.isPadded = isPadded; - state.maxLen = String(state.max).length; - } + if (typeof opts.onMatch === 'function') { + opts.onMatch(result); + } + return returnObject ? result : true; + }; - if (a < 0) { - let newMin = b < 0 ? Math.abs(b) : 1; - negatives = splitToPatterns(newMin, Math.abs(a), state, opts); - a = state.a = 0; + if (returnState) { + matcher.state = state; } - if (b >= 0) { - positives = splitToPatterns(a, b, state, opts); - } + return matcher; +}; - state.negatives = negatives; - state.positives = positives; - state.result = collatePatterns(negatives, positives, opts); +/** + * Test `input` with the given `regex`. This is used by the main + * `picomatch()` function to test the input string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.test(input, regex[, options]); + * + * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); + * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } + * ``` + * @param {String} `input` String to test. + * @param {RegExp} `regex` + * @return {Object} Returns an object with matching info. + * @api public + */ - if (opts.capture === true) { - state.result = `(${state.result})`; - } else if (opts.wrap !== false && (positives.length + negatives.length) > 1) { - state.result = `(?:${state.result})`; +picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected input to be a string'); } - toRegexRange.cache[cacheKey] = state; - return state.result; -}; - -function collatePatterns(neg, pos, options) { - let onlyNegative = filterPatterns(neg, pos, '-', false, options) || []; - let onlyPositive = filterPatterns(pos, neg, '', false, options) || []; - let intersected = filterPatterns(neg, pos, '-?', true, options) || []; - let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); - return subpatterns.join('|'); -} - -function splitToRanges(min, max) { - let nines = 1; - let zeros = 1; + if (input === '') { + return { isMatch: false, output: '' }; + } - let stop = countNines(min, nines); - let stops = new Set([max]); + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = (match && format) ? format(input) : input; - while (min <= stop && stop <= max) { - stops.add(stop); - nines += 1; - stop = countNines(min, nines); + if (match === false) { + output = format ? format(input) : input; + match = output === glob; } - stop = countZeros(max + 1, zeros) - 1; - - while (min < stop && stop <= max) { - stops.add(stop); - zeros += 1; - stop = countZeros(max + 1, zeros) - 1; + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } } - stops = [...stops]; - stops.sort(compare); - return stops; -} + return { isMatch: Boolean(match), match, output }; +}; /** - * Convert a range to a regex pattern - * @param {Number} `start` - * @param {Number} `stop` - * @return {String} + * Match the basename of a filepath. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.matchBase(input, glob[, options]); + * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true + * ``` + * @param {String} `input` String to test. + * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). + * @return {Boolean} + * @api public */ -function rangeToPattern(start, stop, options) { - if (start === stop) { - return { pattern: start, count: [], digits: 0 }; - } - - let zipped = zip(start, stop); - let digits = zipped.length; - let pattern = ''; - let count = 0; - - for (let i = 0; i < digits; i++) { - let [startDigit, stopDigit] = zipped[i]; - - if (startDigit === stopDigit) { - pattern += startDigit; - - } else if (startDigit !== '0' || stopDigit !== '9') { - pattern += toCharacterClass(startDigit, stopDigit, options); - - } else { - count++; - } - } +picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path.basename(input)); +}; - if (count) { - pattern += options.shorthand === true ? '\\d' : '[0-9]'; - } +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.isMatch(string, patterns[, options]); + * + * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String|Array} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - return { pattern, count: [count], digits }; -} +picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); -function splitToPatterns(min, max, tok, options) { - let ranges = splitToRanges(min, max); - let tokens = []; - let start = min; - let prev; +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const picomatch = require('picomatch'); + * const result = picomatch.parse(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as a regex source string. + * @api public + */ - for (let i = 0; i < ranges.length; i++) { - let max = ranges[i]; - let obj = rangeToPattern(String(start), String(max), options); - let zeros = ''; +picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); +}; - if (!tok.isPadded && prev && prev.pattern === obj.pattern) { - if (prev.count.length > 1) { - prev.count.pop(); - } +/** + * Scan a glob pattern to separate the pattern into segments. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.scan(input[, options]); + * + * const result = picomatch.scan('!./foo/*.js'); + * console.log(result); + * { prefix: '!./', + * input: '!./foo/*.js', + * start: 3, + * base: 'foo', + * glob: '*.js', + * isBrace: false, + * isBracket: false, + * isGlob: true, + * isExtglob: false, + * isGlobstar: false, + * negated: true } + * ``` + * @param {String} `input` Glob pattern to scan. + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ - prev.count.push(obj.count[0]); - prev.string = prev.pattern + toQuantifier(prev.count); - start = max + 1; - continue; - } +picomatch.scan = (input, options) => scan(input, options); - if (tok.isPadded) { - zeros = padZeros(max, tok, options); - } +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ - obj.string = zeros + obj.pattern + toQuantifier(obj.count); - tokens.push(obj); - start = max + 1; - prev = obj; +picomatch.compileRe = (parsed, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return parsed.output; } - return tokens; -} - -function filterPatterns(arr, comparison, prefix, intersection, options) { - let result = []; - - for (let ele of arr) { - let { string } = ele; - - // only push if _both_ are negative... - if (!intersection && !contains(comparison, 'string', string)) { - result.push(prefix + string); - } + const opts = options || {}; + const prepend = opts.contains ? '' : '^'; + const append = opts.contains ? '' : '$'; - // or _both_ are positive - if (intersection && contains(comparison, 'string', string)) { - result.push(prefix + string); - } + let source = `${prepend}(?:${parsed.output})${append}`; + if (parsed && parsed.negated === true) { + source = `^(?!${source}).*$`; } - return result; -} - -/** - * Zip strings - */ - -function zip(a, b) { - let arr = []; - for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]); - return arr; -} -function compare(a, b) { - return a > b ? 1 : b > a ? -1 : 0; -} + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = parsed; + } -function contains(arr, key, val) { - return arr.some(ele => ele[key] === val); -} + return regex; +}; -function countNines(min, len) { - return Number(String(min).slice(0, -len) + '9'.repeat(len)); -} +picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => { + if (!input || typeof input !== 'string') { + throw new TypeError('Expected a non-empty string'); + } -function countZeros(integer, zeros) { - return integer - (integer % Math.pow(10, zeros)); -} + const opts = options || {}; + let parsed = { negated: false, fastpaths: true }; + let prefix = ''; + let output; -function toQuantifier(digits) { - let [start = 0, stop = ''] = digits; - if (stop || start > 1) { - return `{${start + (stop ? ',' + stop : '')}}`; + if (input.startsWith('./')) { + input = input.slice(2); + prefix = parsed.prefix = './'; } - return ''; -} - -function toCharacterClass(a, b, options) { - return `[${a}${(b - a === 1) ? '' : '-'}${b}]`; -} -function hasPadding(str) { - return /^-?(0+)\d/.test(str); -} + if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { + output = parse.fastpaths(input, options); + } -function padZeros(value, tok, options) { - if (!tok.isPadded) { - return value; + if (output === undefined) { + parsed = parse(input, options); + parsed.prefix = prefix + (parsed.prefix || ''); + } else { + parsed.output = output; } - let diff = Math.abs(tok.maxLen - String(value).length); - let relax = options.relaxZeros !== false; + return picomatch.compileRe(parsed, options, returnOutput, returnState); +}; - switch (diff) { - case 0: - return ''; - case 1: - return relax ? '0?' : '0'; - case 2: - return relax ? '0{0,2}' : '00'; - default: { - return relax ? `0{0,${diff}}` : `0{${diff}}`; - } +/** + * Create a regular expression from the given regex source string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.toRegex(source[, options]); + * + * const { output } = picomatch.parse('*.js'); + * console.log(picomatch.toRegex(output)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `source` Regular expression source string. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ + +picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; } -} +}; /** - * Cache + * Picomatch constants. + * @return {Object} */ -toRegexRange.cache = {}; -toRegexRange.clearCache = () => (toRegexRange.cache = {}); +picomatch.constants = constants; /** - * Expose `toRegexRange` + * Expose "picomatch" */ -module.exports = toRegexRange; +module.exports = picomatch; /***/ }), -/* 310 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * is-number - * - * Copyright (c) 2014-present, Jon Schlinkert. - * Released under the MIT License. - */ +const utils = __webpack_require__(314); +const { + CHAR_ASTERISK, /* * */ + CHAR_AT, /* @ */ + CHAR_BACKWARD_SLASH, /* \ */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_EXCLAMATION_MARK, /* ! */ + CHAR_FORWARD_SLASH, /* / */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_PLUS, /* + */ + CHAR_QUESTION_MARK, /* ? */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_RIGHT_SQUARE_BRACKET /* ] */ +} = __webpack_require__(315); -module.exports = function(num) { - if (typeof num === 'number') { - return num - num === 0; - } - if (typeof num === 'string' && num.trim() !== '') { - return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); - } - return false; +const isPathSeparator = code => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; }; +const depth = token => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } +}; -/***/ }), -/* 311 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Quickly scans a glob pattern and returns an object with a handful of + * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), + * `glob` (the actual pattern), and `negated` (true if the path starts with `!`). + * + * ```js + * const pm = require('picomatch'); + * console.log(pm.scan('foo/bar/*.js')); + * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an object with tokens and regex source string. + * @api public + */ -"use strict"; +const scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; -const fill = __webpack_require__(308); -const stringify = __webpack_require__(305); -const utils = __webpack_require__(306); + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: '', depth: 0, isGlob: false }; -const append = (queue = '', stash = '', enclose = false) => { - let result = []; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; - queue = [].concat(queue); - stash = [].concat(stash); + while (index < length) { + code = advance(); + let next; - if (!stash.length) return queue; - if (!queue.length) { - return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash; - } + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); - for (let item of queue) { - if (Array.isArray(item)) { - for (let value of item) { - result.push(append(value, stash, enclose)); - } - } else { - for (let ele of stash) { - if (enclose === true && typeof ele === 'string') ele = `{${ele}}`; - result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele)); + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; } + continue; } - } - return utils.flatten(result); -}; - -const expand = (ast, options = {}) => { - let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit; - - let walk = (node, parent = {}) => { - node.queue = []; - - let p = parent; - let q = parent.queue; - while (p.type !== 'brace' && p.type !== 'root' && p.parent) { - p = p.parent; - q = p.queue; - } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; - if (node.invalid || node.dollar) { - q.push(append(q.pop(), stringify(node, options))); - return; - } + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } - if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) { - q.push(append(q.pop(), ['{}'])); - return; - } + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } - if (node.nodes && node.ranges > 0) { - let args = utils.reduce(node.nodes); + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; - if (utils.exceedsLimit(...args, options.step, rangeLimit)) { - throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); - } + if (scanToEnd === true) { + continue; + } - let range = fill(...args, options); - if (range.length === 0) { - range = stringify(node, options); - } + break; + } - q.push(append(q.pop(), range)); - node.nodes = []; - return; - } + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; - let enclose = utils.encloseBrace(node); - let queue = node.queue; - let block = node; + if (scanToEnd === true) { + continue; + } - while (block.type !== 'brace' && block.type !== 'root' && block.parent) { - block = block.parent; - queue = block.queue; - } + break; + } - for (let i = 0; i < node.nodes.length; i++) { - let child = node.nodes[i]; + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; - if (child.type === 'comma' && node.type === 'brace') { - if (i === 1) queue.push(''); - queue.push(''); - continue; + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } } - if (child.type === 'close') { - q.push(append(q.pop(), queue, enclose)); + if (scanToEnd === true) { continue; } - if (child.value && child.type !== 'open') { - queue.push(append(queue.pop(), child.value)); + break; + } + + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: '', depth: 0, isGlob: false }; + + if (finished === true) continue; + if (prev === CHAR_DOT && index === (start + 1)) { + start += 2; continue; } - if (child.nodes) { - walk(child, node); - } + lastIndex = index + 1; + continue; } - return queue; - }; - - return utils.flatten(walk(ast)); -}; - -module.exports = expand; - - -/***/ }), -/* 312 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const stringify = __webpack_require__(305); - -/** - * Constants - */ - -const { - MAX_LENGTH, - CHAR_BACKSLASH, /* \ */ - CHAR_BACKTICK, /* ` */ - CHAR_COMMA, /* , */ - CHAR_DOT, /* . */ - CHAR_LEFT_PARENTHESES, /* ( */ - CHAR_RIGHT_PARENTHESES, /* ) */ - CHAR_LEFT_CURLY_BRACE, /* { */ - CHAR_RIGHT_CURLY_BRACE, /* } */ - CHAR_LEFT_SQUARE_BRACKET, /* [ */ - CHAR_RIGHT_SQUARE_BRACKET, /* ] */ - CHAR_DOUBLE_QUOTE, /* " */ - CHAR_SINGLE_QUOTE, /* ' */ - CHAR_NO_BREAK_SPACE, - CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(313); - -/** - * parse - */ - -const parse = (input, options = {}) => { - if (typeof input !== 'string') { - throw new TypeError('Expected a string'); - } - - let opts = options || {}; - let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - if (input.length > max) { - throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); - } - - let ast = { type: 'root', input, nodes: [] }; - let stack = [ast]; - let block = ast; - let prev = ast; - let brackets = 0; - let length = input.length; - let index = 0; - let depth = 0; - let value; - let memo = {}; - - /** - * Helpers - */ - - const advance = () => input[index++]; - const push = node => { - if (node.type === 'text' && prev.type === 'dot') { - prev.type = 'text'; - } - - if (prev && prev.type === 'text' && node.type === 'text') { - prev.value += node.value; - return; - } - - block.nodes.push(node); - node.parent = block; - node.prev = prev; - prev = node; - return node; - }; - - push({ type: 'bos' }); + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS + || code === CHAR_AT + || code === CHAR_ASTERISK + || code === CHAR_QUESTION_MARK + || code === CHAR_EXCLAMATION_MARK; - while (index < length) { - block = stack[stack.length - 1]; - value = advance(); + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; - /** - * Invalid chars - */ + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } - if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { - continue; + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } } - /** - * Escaped chars - */ + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; - if (value === CHAR_BACKSLASH) { - push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() }); - continue; + if (scanToEnd === true) { + continue; + } + break; } - /** - * Right square bracket (literal): ']' - */ + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; - if (value === CHAR_RIGHT_SQUARE_BRACKET) { - push({ type: 'text', value: '\\' + value }); - continue; + if (scanToEnd === true) { + continue; + } + break; } - /** - * Left square bracket: '[' - */ - - if (value === CHAR_LEFT_SQUARE_BRACKET) { - brackets++; - - let closed = true; - let next; - - while (index < length && (next = advance())) { - value += next; - - if (next === CHAR_LEFT_SQUARE_BRACKET) { - brackets++; - continue; - } - - if (next === CHAR_BACKSLASH) { - value += advance(); + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); continue; } if (next === CHAR_RIGHT_SQUARE_BRACKET) { - brackets--; + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; - if (brackets === 0) { - break; + if (scanToEnd === true) { + continue; } + break; } } + } - push({ type: 'text', value }); + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; continue; } - /** - * Parentheses - */ + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; - if (value === CHAR_LEFT_PARENTHESES) { - block = push({ type: 'paren', nodes: [] }); - stack.push(block); - push({ type: 'text', value }); - continue; - } + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } - if (value === CHAR_RIGHT_PARENTHESES) { - if (block.type !== 'paren') { - push({ type: 'text', value }); + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } continue; } - block = stack.pop(); - push({ type: 'text', value }); - block = stack[stack.length - 1]; - continue; + break; } - /** - * Quotes: '|"|` - */ - - if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { - let open = value; - let next; + if (isGlob === true) { + finished = true; - if (options.keepQuotes !== true) { - value = ''; + if (scanToEnd === true) { + continue; } - while (index < length && (next = advance())) { - if (next === CHAR_BACKSLASH) { - value += next + advance(); - continue; - } + break; + } + } - if (next === open) { - if (options.keepQuotes === true) value += next; - break; - } + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } - value += next; - } + let base = str; + let prefix = ''; + let glob = ''; - push({ type: 'text', value }); - continue; - } + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } - /** - * Left curly brace: '{' - */ + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ''; + glob = str; + } else { + base = str; + } - if (value === CHAR_LEFT_CURLY_BRACE) { - depth++; + if (base && base !== '' && base !== '/' && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); + } + } - let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true; - let brace = { - type: 'brace', - open: true, - close: false, - dollar, - depth, - commas: 0, - ranges: 0, - nodes: [] - }; + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); - block = push(brace); - stack.push(block); - push({ type: 'open', value }); - continue; + if (base && backslashes === true) { + base = utils.removeBackslashes(base); } + } - /** - * Right curly brace: '}' - */ - - if (value === CHAR_RIGHT_CURLY_BRACE) { - if (block.type !== 'brace') { - push({ type: 'text', value }); - continue; - } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated + }; - let type = 'close'; - block = stack.pop(); - block.close = true; + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; + } - push({ type, value }); - depth--; + if (opts.parts === true || opts.tokens === true) { + let prevIndex; - block = stack[stack.length - 1]; - continue; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== '') { + parts.push(value); + } + prevIndex = i; } - /** - * Comma: ',' - */ + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); - if (value === CHAR_COMMA && depth > 0) { - if (block.ranges > 0) { - block.ranges = 0; - let open = block.nodes.shift(); - block.nodes = [open, { type: 'text', value: stringify(block) }]; + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; } - - push({ type: 'comma', value }); - block.commas++; - continue; } - /** - * Dot: '.' - */ + state.slashes = slashes; + state.parts = parts; + } - if (value === CHAR_DOT && depth > 0 && block.commas === 0) { - let siblings = block.nodes; + return state; +}; - if (depth === 0 || siblings.length === 0) { - push({ type: 'text', value }); - continue; - } +module.exports = scan; - if (prev.type === 'dot') { - block.range = []; - prev.value += value; - prev.type = 'range'; - if (block.nodes.length !== 3 && block.nodes.length !== 5) { - block.invalid = true; - block.ranges = 0; - prev.type = 'text'; - continue; - } +/***/ }), +/* 314 */ +/***/ (function(module, exports, __webpack_require__) { - block.ranges++; - block.args = []; - continue; - } +"use strict"; - if (prev.type === 'range') { - siblings.pop(); - let before = siblings[siblings.length - 1]; - before.value += prev.value + value; - prev = before; - block.ranges--; - continue; - } +const path = __webpack_require__(4); +const win32 = process.platform === 'win32'; +const { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL +} = __webpack_require__(315); - push({ type: 'dot', value }); - continue; - } +exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); +exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); +exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); +exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); +exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); - /** - * Text - */ +exports.removeBackslashes = str => { + return str.replace(REGEX_REMOVE_BACKSLASH, match => { + return match === '\\' ? '' : match; + }); +}; - push({ type: 'text', value }); +exports.supportsLookbehinds = () => { + const segs = process.version.slice(1).split('.').map(Number); + if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { + return true; } + return false; +}; - // Mark imbalanced braces and brackets as invalid - do { - block = stack.pop(); - - if (block.type !== 'root') { - block.nodes.forEach(node => { - if (!node.nodes) { - if (node.type === 'open') node.isOpen = true; - if (node.type === 'close') node.isClose = true; - if (!node.nodes) node.type = 'text'; - node.invalid = true; - } - }); +exports.isWindows = options => { + if (options && typeof options.windows === 'boolean') { + return options.windows; + } + return win32 === true || path.sep === '\\'; +}; - // get the location of the block on parent.nodes (block's siblings) - let parent = stack[stack.length - 1]; - let index = parent.nodes.indexOf(block); - // replace the (invalid) block with it's nodes - parent.nodes.splice(index, 1, ...block.nodes); - } - } while (stack.length > 0); +exports.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; +}; - push({ type: 'eos' }); - return ast; +exports.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith('./')) { + output = output.slice(2); + state.prefix = './'; + } + return output; }; -module.exports = parse; +exports.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? '' : '^'; + const append = options.contains ? '' : '$'; + + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; +}; /***/ }), -/* 313 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = { - MAX_LENGTH: 1024 * 64, +const path = __webpack_require__(4); +const WIN_SLASH = '\\\\/'; +const WIN_NO_SLASH = `[^${WIN_SLASH}]`; - // Digits - CHAR_0: '0', /* 0 */ - CHAR_9: '9', /* 9 */ +/** + * Posix glob regex + */ - // Alphabet chars. - CHAR_UPPERCASE_A: 'A', /* A */ - CHAR_LOWERCASE_A: 'a', /* a */ - CHAR_UPPERCASE_Z: 'Z', /* Z */ - CHAR_LOWERCASE_Z: 'z', /* z */ - - CHAR_LEFT_PARENTHESES: '(', /* ( */ - CHAR_RIGHT_PARENTHESES: ')', /* ) */ - - CHAR_ASTERISK: '*', /* * */ +const DOT_LITERAL = '\\.'; +const PLUS_LITERAL = '\\+'; +const QMARK_LITERAL = '\\?'; +const SLASH_LITERAL = '\\/'; +const ONE_CHAR = '(?=.)'; +const QMARK = '[^/]'; +const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; +const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; +const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; +const NO_DOT = `(?!${DOT_LITERAL})`; +const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; +const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; +const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; +const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; +const STAR = `${QMARK}*?`; - // Non-alphabetic chars. - CHAR_AMPERSAND: '&', /* & */ - CHAR_AT: '@', /* @ */ - CHAR_BACKSLASH: '\\', /* \ */ - CHAR_BACKTICK: '`', /* ` */ - CHAR_CARRIAGE_RETURN: '\r', /* \r */ - CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */ - CHAR_COLON: ':', /* : */ - CHAR_COMMA: ',', /* , */ - CHAR_DOLLAR: '$', /* . */ - CHAR_DOT: '.', /* . */ - CHAR_DOUBLE_QUOTE: '"', /* " */ - CHAR_EQUAL: '=', /* = */ - CHAR_EXCLAMATION_MARK: '!', /* ! */ - CHAR_FORM_FEED: '\f', /* \f */ - CHAR_FORWARD_SLASH: '/', /* / */ - CHAR_HASH: '#', /* # */ - CHAR_HYPHEN_MINUS: '-', /* - */ - CHAR_LEFT_ANGLE_BRACKET: '<', /* < */ - CHAR_LEFT_CURLY_BRACE: '{', /* { */ - CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */ - CHAR_LINE_FEED: '\n', /* \n */ - CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */ - CHAR_PERCENT: '%', /* % */ - CHAR_PLUS: '+', /* + */ - CHAR_QUESTION_MARK: '?', /* ? */ - CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */ - CHAR_RIGHT_CURLY_BRACE: '}', /* } */ - CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */ - CHAR_SEMICOLON: ';', /* ; */ - CHAR_SINGLE_QUOTE: '\'', /* ' */ - CHAR_SPACE: ' ', /* */ - CHAR_TAB: '\t', /* \t */ - CHAR_UNDERSCORE: '_', /* _ */ - CHAR_VERTICAL_LINE: '|', /* | */ - CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */ +const POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR }; +/** + * Windows glob regex + */ -/***/ }), -/* 314 */ -/***/ (function(module, exports, __webpack_require__) { +const WINDOWS_CHARS = { + ...POSIX_CHARS, -"use strict"; + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` +}; +/** + * POSIX Bracket Regex + */ -module.exports = __webpack_require__(315); +const POSIX_REGEX_SOURCE = { + alnum: 'a-zA-Z0-9', + alpha: 'a-zA-Z', + ascii: '\\x00-\\x7F', + blank: ' \\t', + cntrl: '\\x00-\\x1F\\x7F', + digit: '0-9', + graph: '\\x21-\\x7E', + lower: 'a-z', + print: '\\x20-\\x7E ', + punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', + space: ' \\t\\r\\n\\v\\f', + upper: 'A-Z', + word: 'A-Za-z0-9_', + xdigit: 'A-Fa-f0-9' +}; +module.exports = { + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, -/***/ }), -/* 315 */ -/***/ (function(module, exports, __webpack_require__) { + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, -"use strict"; + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + '***': '*', + '**/**': '**', + '**/**/**': '**' + }, + // Digits + CHAR_0: 48, /* 0 */ + CHAR_9: 57, /* 9 */ -const path = __webpack_require__(4); -const scan = __webpack_require__(316); -const parse = __webpack_require__(319); -const utils = __webpack_require__(317); -const constants = __webpack_require__(318); -const isObject = val => val && typeof val === 'object' && !Array.isArray(val); + // Alphabet chars. + CHAR_UPPERCASE_A: 65, /* A */ + CHAR_LOWERCASE_A: 97, /* a */ + CHAR_UPPERCASE_Z: 90, /* Z */ + CHAR_LOWERCASE_Z: 122, /* z */ -/** - * Creates a matcher function from one or more glob patterns. The - * returned function takes a string to match as its first argument, - * and returns true if the string is a match. The returned matcher - * function also takes a boolean as the second argument that, when true, - * returns an object with additional information. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch(glob[, options]); - * - * const isMatch = picomatch('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @name picomatch - * @param {String|Array} `globs` One or more glob patterns. - * @param {Object=} `options` - * @return {Function=} Returns a matcher function. - * @api public - */ + CHAR_LEFT_PARENTHESES: 40, /* ( */ + CHAR_RIGHT_PARENTHESES: 41, /* ) */ -const picomatch = (glob, options, returnState = false) => { - if (Array.isArray(glob)) { - const fns = glob.map(input => picomatch(input, options, returnState)); - const arrayMatcher = str => { - for (const isMatch of fns) { - const state = isMatch(str); - if (state) return state; - } - return false; - }; - return arrayMatcher; - } + CHAR_ASTERISK: 42, /* * */ - const isState = isObject(glob) && glob.tokens && glob.input; + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, /* & */ + CHAR_AT: 64, /* @ */ + CHAR_BACKWARD_SLASH: 92, /* \ */ + CHAR_CARRIAGE_RETURN: 13, /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ + CHAR_COLON: 58, /* : */ + CHAR_COMMA: 44, /* , */ + CHAR_DOT: 46, /* . */ + CHAR_DOUBLE_QUOTE: 34, /* " */ + CHAR_EQUAL: 61, /* = */ + CHAR_EXCLAMATION_MARK: 33, /* ! */ + CHAR_FORM_FEED: 12, /* \f */ + CHAR_FORWARD_SLASH: 47, /* / */ + CHAR_GRAVE_ACCENT: 96, /* ` */ + CHAR_HASH: 35, /* # */ + CHAR_HYPHEN_MINUS: 45, /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ + CHAR_LEFT_CURLY_BRACE: 123, /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ + CHAR_LINE_FEED: 10, /* \n */ + CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ + CHAR_PERCENT: 37, /* % */ + CHAR_PLUS: 43, /* + */ + CHAR_QUESTION_MARK: 63, /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ + CHAR_SEMICOLON: 59, /* ; */ + CHAR_SINGLE_QUOTE: 39, /* ' */ + CHAR_SPACE: 32, /* */ + CHAR_TAB: 9, /* \t */ + CHAR_UNDERSCORE: 95, /* _ */ + CHAR_VERTICAL_LINE: 124, /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ - if (glob === '' || (typeof glob !== 'string' && !isState)) { - throw new TypeError('Expected pattern to be a non-empty string'); - } + SEP: path.sep, - const opts = options || {}; - const posix = utils.isWindows(options); - const regex = isState - ? picomatch.compileRe(glob, options) - : picomatch.makeRe(glob, options, false, true); + /** + * Create EXTGLOB_CHARS + */ - const state = regex.state; - delete regex.state; + extglobChars(chars) { + return { + '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, + '?': { type: 'qmark', open: '(?:', close: ')?' }, + '+': { type: 'plus', open: '(?:', close: ')+' }, + '*': { type: 'star', open: '(?:', close: ')*' }, + '@': { type: 'at', open: '(?:', close: ')' } + }; + }, - let isIgnored = () => false; - if (opts.ignore) { - const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; - isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + /** + * Create GLOB_CHARS + */ + + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; } +}; - const matcher = (input, returnObject = false) => { - const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); - const result = { glob, state, regex, posix, input, output, match, isMatch }; - if (typeof opts.onResult === 'function') { - opts.onResult(result); - } +/***/ }), +/* 316 */ +/***/ (function(module, exports, __webpack_require__) { - if (isMatch === false) { - result.isMatch = false; - return returnObject ? result : false; - } +"use strict"; - if (isIgnored(input)) { - if (typeof opts.onIgnore === 'function') { - opts.onIgnore(result); - } - result.isMatch = false; - return returnObject ? result : false; - } - if (typeof opts.onMatch === 'function') { - opts.onMatch(result); - } - return returnObject ? result : true; - }; +const constants = __webpack_require__(315); +const utils = __webpack_require__(314); - if (returnState) { - matcher.state = state; - } +/** + * Constants + */ - return matcher; -}; +const { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS +} = constants; /** - * Test `input` with the given `regex`. This is used by the main - * `picomatch()` function to test the input string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.test(input, regex[, options]); - * - * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); - * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } - * ``` - * @param {String} `input` String to test. - * @param {RegExp} `regex` - * @return {Object} Returns an object with matching info. - * @api public + * Helpers */ -picomatch.test = (input, regex, options, { glob, posix } = {}) => { - if (typeof input !== 'string') { - throw new TypeError('Expected input to be a string'); - } - - if (input === '') { - return { isMatch: false, output: '' }; +const expandRange = (args, options) => { + if (typeof options.expandRange === 'function') { + return options.expandRange(...args, options); } - const opts = options || {}; - const format = opts.format || (posix ? utils.toPosixSlashes : null); - let match = input === glob; - let output = (match && format) ? format(input) : input; - - if (match === false) { - output = format ? format(input) : input; - match = output === glob; - } + args.sort(); + const value = `[${args.join('-')}]`; - if (match === false || opts.capture === true) { - if (opts.matchBase === true || opts.basename === true) { - match = picomatch.matchBase(input, regex, options, posix); - } else { - match = regex.exec(output); - } + try { + /* eslint-disable-next-line no-new */ + new RegExp(value); + } catch (ex) { + return args.map(v => utils.escapeRegex(v)).join('..'); } - return { isMatch: Boolean(match), match, output }; + return value; }; /** - * Match the basename of a filepath. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.matchBase(input, glob[, options]); - * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true - * ``` - * @param {String} `input` String to test. - * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). - * @return {Boolean} - * @api public + * Create the message for a syntax error */ -picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { - const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); - return regex.test(path.basename(input)); +const syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; }; /** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.isMatch(string, patterns[, options]); - * - * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String|Array} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ - -picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); - -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const picomatch = require('picomatch'); - * const result = picomatch.parse(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as a regex source string. - * @api public + * Parse the given input string. + * @param {String} input + * @param {Object} options + * @return {Object} */ -picomatch.parse = (pattern, options) => { - if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); - return parse(pattern, { ...options, fastpaths: false }); -}; - -/** - * Scan a glob pattern to separate the pattern into segments. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.scan(input[, options]); - * - * const result = picomatch.scan('!./foo/*.js'); - * console.log(result); - * { prefix: '!./', - * input: '!./foo/*.js', - * start: 3, - * base: 'foo', - * glob: '*.js', - * isBrace: false, - * isBracket: false, - * isGlob: true, - * isExtglob: false, - * isGlobstar: false, - * negated: true } - * ``` - * @param {String} `input` Glob pattern to scan. - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ +const parse = (input, options) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } -picomatch.scan = (input, options) => scan(input, options); + input = REPLACEMENTS[input] || input; -/** - * Create a regular expression from a parsed glob pattern. - * - * ```js - * const picomatch = require('picomatch'); - * const state = picomatch.parse('*.js'); - * // picomatch.compileRe(state[, options]); - * - * console.log(picomatch.compileRe(state)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `state` The object returned from the `.parse` method. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; -picomatch.compileRe = (parsed, options, returnOutput = false, returnState = false) => { - if (returnOutput === true) { - return parsed.output; + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); } - const opts = options || {}; - const prepend = opts.contains ? '' : '^'; - const append = opts.contains ? '' : '$'; + const bos = { type: 'bos', value: '', output: opts.prepend || '' }; + const tokens = [bos]; - let source = `${prepend}(?:${parsed.output})${append}`; - if (parsed && parsed.negated === true) { - source = `^(?!${source}).*$`; - } + const capture = opts.capture ? '' : '?:'; + const win32 = utils.isWindows(options); - const regex = picomatch.toRegex(source, options); - if (returnState === true) { - regex.state = parsed; - } + // create constants based on platform, for windows or posix + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); - return regex; -}; + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; -picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => { - if (!input || typeof input !== 'string') { - throw new TypeError('Expected a non-empty string'); - } + const globstar = (opts) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; - const opts = options || {}; - let parsed = { negated: false, fastpaths: true }; - let prefix = ''; - let output; + const nodot = opts.dot ? '' : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; - if (input.startsWith('./')) { - input = input.slice(2); - prefix = parsed.prefix = './'; + if (opts.capture) { + star = `(${star})`; } - if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { - output = parse.fastpaths(input, options); + // minimatch options support + if (typeof opts.noext === 'boolean') { + opts.noextglob = opts.noext; } - if (output === undefined) { - parsed = parse(input, options); - parsed.prefix = prefix + (parsed.prefix || ''); - } else { - parsed.output = output; - } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: '', + output: '', + prefix: '', + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; - return picomatch.compileRe(parsed, options, returnOutput, returnState); -}; + input = utils.removePrefix(input, state); + len = input.length; -/** - * Create a regular expression from the given regex source string. - * - * ```js - * const picomatch = require('picomatch'); - * // picomatch.toRegex(source[, options]); - * - * const { output } = picomatch.parse('*.js'); - * console.log(picomatch.toRegex(output)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `source` Regular expression source string. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; -picomatch.toRegex = (source, options) => { - try { - const opts = options || {}; - return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); - } catch (err) { - if (options && options.debug === true) throw err; - return /$^/; - } -}; + /** + * Tokenizing helpers + */ -/** - * Picomatch constants. - * @return {Object} - */ + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index]; + const remaining = () => input.slice(state.index + 1); + const consume = (value = '', num = 0) => { + state.consumed += value; + state.index += num; + }; + const append = token => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; -picomatch.constants = constants; + const negate = () => { + let count = 1; -/** - * Expose "picomatch" - */ + while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { + advance(); + state.start++; + count++; + } -module.exports = picomatch; + if (count % 2 === 0) { + return false; + } + state.negated = true; + state.start++; + return true; + }; -/***/ }), -/* 316 */ -/***/ (function(module, exports, __webpack_require__) { + const increment = type => { + state[type]++; + stack.push(type); + }; -"use strict"; + const decrement = type => { + state[type]--; + stack.pop(); + }; + /** + * Push tokens onto the tokens array. This helper speeds up + * tokenizing by 1) helping us avoid backtracking as much as possible, + * and 2) helping us avoid creating extra tokens when consecutive + * characters are plain text. This improves performance and simplifies + * lookbehinds. + */ -const utils = __webpack_require__(317); -const { - CHAR_ASTERISK, /* * */ - CHAR_AT, /* @ */ - CHAR_BACKWARD_SLASH, /* \ */ - CHAR_COMMA, /* , */ - CHAR_DOT, /* . */ - CHAR_EXCLAMATION_MARK, /* ! */ - CHAR_FORWARD_SLASH, /* / */ - CHAR_LEFT_CURLY_BRACE, /* { */ - CHAR_LEFT_PARENTHESES, /* ( */ - CHAR_LEFT_SQUARE_BRACKET, /* [ */ - CHAR_PLUS, /* + */ - CHAR_QUESTION_MARK, /* ? */ - CHAR_RIGHT_CURLY_BRACE, /* } */ - CHAR_RIGHT_PARENTHESES, /* ) */ - CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(318); + const push = tok => { + if (prev.type === 'globstar') { + const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); + const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); -const isPathSeparator = code => { - return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; -}; + if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = 'star'; + prev.value = '*'; + prev.output = star; + state.output += prev.output; + } + } -const depth = token => { - if (token.isPrefix !== true) { - token.depth = token.isGlobstar ? Infinity : 1; - } -}; + if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { + extglobs[extglobs.length - 1].inner += tok.value; + } -/** - * Quickly scans a glob pattern and returns an object with a handful of - * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), - * `glob` (the actual pattern), and `negated` (true if the path starts with `!`). - * - * ```js - * const pm = require('picomatch'); - * console.log(pm.scan('foo/bar/*.js')); - * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {Object} Returns an object with tokens and regex source string. - * @api public - */ + if (tok.value || tok.output) append(tok); + if (prev && prev.type === 'text' && tok.type === 'text') { + prev.value += tok.value; + prev.output = (prev.output || '') + tok.value; + return; + } -const scan = (input, options) => { - const opts = options || {}; + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; - const length = input.length - 1; - const scanToEnd = opts.parts === true || opts.scanToEnd === true; - const slashes = []; - const tokens = []; - const parts = []; + const extglobOpen = (type, value) => { + const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; - let str = input; - let index = -1; - let start = 0; - let lastIndex = 0; - let isBrace = false; - let isBracket = false; - let isGlob = false; - let isExtglob = false; - let isGlobstar = false; - let braceEscaped = false; - let backslashes = false; - let negated = false; - let finished = false; - let braces = 0; - let prev; - let code; - let token = { value: '', depth: 0, isGlob: false }; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + const output = (opts.capture ? '(' : '') + token.open; - const eos = () => index >= length; - const peek = () => str.charCodeAt(index + 1); - const advance = () => { - prev = code; - return str.charCodeAt(++index); + increment('parens'); + push({ type, value, output: state.output ? '' : ONE_CHAR }); + push({ type: 'paren', extglob: true, value: advance(), output }); + extglobs.push(token); }; - while (index < length) { - code = advance(); - let next; + const extglobClose = token => { + let output = token.close + (opts.capture ? ')' : ''); - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); + if (token.type === 'negate') { + let extglobStar = star; - if (code === CHAR_LEFT_CURLY_BRACE) { - braceEscaped = true; + if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { + extglobStar = globstar(opts); } - continue; - } - - if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { - braces++; - - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; - } - if (code === CHAR_LEFT_CURLY_BRACE) { - braces++; - continue; - } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } - if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; + if (token.prev.type === 'bos' && eos()) { + state.negatedExtglob = true; + } + } - if (scanToEnd === true) { - continue; - } + push({ type: 'paren', extglob: true, value, output }); + decrement('parens'); + }; - break; - } + /** + * Fast paths + */ - if (braceEscaped !== true && code === CHAR_COMMA) { - isBrace = token.isBrace = true; - isGlob = token.isGlob = true; - finished = true; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; - if (scanToEnd === true) { - continue; - } + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === '\\') { + backslashes = true; + return m; + } - break; + if (first === '?') { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ''); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); } + return QMARK.repeat(chars.length); + } - if (code === CHAR_RIGHT_CURLY_BRACE) { - braces--; + if (first === '.') { + return DOT_LITERAL.repeat(chars.length); + } - if (braces === 0) { - braceEscaped = false; - isBrace = token.isBrace = true; - finished = true; - break; - } + if (first === '*') { + if (esc) { + return esc + first + (rest ? star : ''); } + return star; } + return esc ? m : `\\${m}`; + }); - if (scanToEnd === true) { - continue; + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ''); + } else { + output = output.replace(/\\+/g, m => { + return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); + }); } + } - break; + if (output === input && opts.contains === true) { + state.output = input; + return state; } - if (code === CHAR_FORWARD_SLASH) { - slashes.push(index); - tokens.push(token); - token = { value: '', depth: 0, isGlob: false }; + state.output = utils.wrapOutput(output, state, options); + return state; + } - if (finished === true) continue; - if (prev === CHAR_DOT && index === (start + 1)) { - start += 2; - continue; - } + /** + * Tokenize input until we reach end-of-string + */ - lastIndex = index + 1; + while (!eos()) { + value = advance(); + + if (value === '\u0000') { continue; } - if (opts.noext !== true) { - const isExtglobChar = code === CHAR_PLUS - || code === CHAR_AT - || code === CHAR_ASTERISK - || code === CHAR_QUESTION_MARK - || code === CHAR_EXCLAMATION_MARK; - - if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; - isExtglob = token.isExtglob = true; - finished = true; + /** + * Escaped characters + */ - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } + if (value === '\\') { + const next = peek(); - if (code === CHAR_RIGHT_PARENTHESES) { - isGlob = token.isGlob = true; - finished = true; - break; - } - } - continue; - } - break; + if (next === '/' && opts.bash !== true) { + continue; } - } - if (code === CHAR_ASTERISK) { - if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; - isGlob = token.isGlob = true; - finished = true; - - if (scanToEnd === true) { + if (next === '.' || next === ';') { continue; } - break; - } - - if (code === CHAR_QUESTION_MARK) { - isGlob = token.isGlob = true; - finished = true; - if (scanToEnd === true) { + if (!next) { + value += '\\'; + push({ type: 'text', value }); continue; } - break; - } - if (code === CHAR_LEFT_SQUARE_BRACKET) { - while (eos() !== true && (next = advance())) { - if (next === CHAR_BACKWARD_SLASH) { - backslashes = token.backslashes = true; - advance(); - continue; + // collapse slashes to reduce potential for exploits + const match = /^\\+/.exec(remaining()); + let slashes = 0; + + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += '\\'; } + } - if (next === CHAR_RIGHT_SQUARE_BRACKET) { - isBracket = token.isBracket = true; - isGlob = token.isGlob = true; - finished = true; + if (opts.unescape === true) { + value = advance() || ''; + } else { + value += advance() || ''; + } - if (scanToEnd === true) { - continue; - } - break; - } + if (state.brackets === 0) { + push({ type: 'text', value }); + continue; } } - if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { - negated = token.negated = true; - start++; - continue; - } + /** + * If we're inside a regex character class, continue + * until we reach the closing bracket. + */ - if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { - isGlob = token.isGlob = true; + if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { + if (opts.posix !== false && value === ':') { + const inner = prev.value.slice(1); + if (inner.includes('[')) { + prev.posix = true; - if (scanToEnd === true) { - while (eos() !== true && (code = advance())) { - if (code === CHAR_LEFT_PARENTHESES) { - backslashes = token.backslashes = true; - code = advance(); - continue; - } + if (inner.includes(':')) { + const idx = prev.value.lastIndexOf('['); + const pre = prev.value.slice(0, idx); + const rest = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); - if (code === CHAR_RIGHT_PARENTHESES) { - finished = true; - break; + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } } } - continue; } - break; - } - if (isGlob === true) { - finished = true; + if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { + value = `\\${value}`; + } - if (scanToEnd === true) { - continue; + if (value === ']' && (prev.value === '[' || prev.value === '[^')) { + value = `\\${value}`; } - break; - } - } + if (opts.posix === true && value === '!' && prev.value === '[') { + value = '^'; + } - if (opts.noext === true) { - isExtglob = false; - isGlob = false; - } + prev.value += value; + append({ value }); + continue; + } - let base = str; - let prefix = ''; - let glob = ''; + /** + * If we're inside a quoted string, continue + * until we reach the closing double quote. + */ - if (start > 0) { - prefix = str.slice(0, start); - str = str.slice(start); - lastIndex -= start; - } + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } - if (base && isGlob === true && lastIndex > 0) { - base = str.slice(0, lastIndex); - glob = str.slice(lastIndex); - } else if (isGlob === true) { - base = ''; - glob = str; - } else { - base = str; - } + /** + * Double quotes + */ - if (base && base !== '' && base !== '/' && base !== str) { - if (isPathSeparator(base.charCodeAt(base.length - 1))) { - base = base.slice(0, -1); + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: 'text', value }); + } + continue; } - } - if (opts.unescape === true) { - if (glob) glob = utils.removeBackslashes(glob); + /** + * Parentheses + */ - if (base && backslashes === true) { - base = utils.removeBackslashes(base); + if (value === '(') { + increment('parens'); + push({ type: 'paren', value }); + continue; } - } - const state = { - prefix, - input, - start, - base, - glob, - isBrace, - isBracket, - isGlob, - isExtglob, - isGlobstar, - negated - }; + if (value === ')') { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '(')); + } - if (opts.tokens === true) { - state.maxDepth = 0; - if (!isPathSeparator(code)) { - tokens.push(token); + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + + push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); + decrement('parens'); + continue; } - state.tokens = tokens; - } - if (opts.parts === true || opts.tokens === true) { - let prevIndex; + /** + * Square brackets + */ - for (let idx = 0; idx < slashes.length; idx++) { - const n = prevIndex ? prevIndex + 1 : start; - const i = slashes[idx]; - const value = input.slice(n, i); - if (opts.tokens) { - if (idx === 0 && start !== 0) { - tokens[idx].isPrefix = true; - tokens[idx].value = prefix; - } else { - tokens[idx].value = value; + if (value === '[') { + if (opts.nobracket === true || !remaining().includes(']')) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('closing', ']')); } - depth(tokens[idx]); - state.maxDepth += tokens[idx].depth; - } - if (idx !== 0 || value !== '') { - parts.push(value); - } - prevIndex = i; - } - - if (prevIndex && prevIndex + 1 < input.length) { - const value = input.slice(prevIndex + 1); - parts.push(value); - if (opts.tokens) { - tokens[tokens.length - 1].value = value; - depth(tokens[tokens.length - 1]); - state.maxDepth += tokens[tokens.length - 1].depth; + value = `\\${value}`; + } else { + increment('brackets'); } + + push({ type: 'bracket', value }); + continue; } - state.slashes = slashes; - state.parts = parts; - } + if (value === ']') { + if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { + push({ type: 'text', value, output: `\\${value}` }); + continue; + } - return state; -}; + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '[')); + } -module.exports = scan; + push({ type: 'text', value, output: `\\${value}` }); + continue; + } + decrement('brackets'); -/***/ }), -/* 317 */ -/***/ (function(module, exports, __webpack_require__) { + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { + value = `/${value}`; + } -"use strict"; + prev.value += value; + append({ value }); + // when literal brackets are explicitly disabled + // assume we should match with a regex character class + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } -const path = __webpack_require__(4); -const win32 = process.platform === 'win32'; -const { - REGEX_BACKSLASH, - REGEX_REMOVE_BACKSLASH, - REGEX_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(318); + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); -exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); -exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); -exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); -exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); -exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); + // when literal brackets are explicitly enabled + // assume we should escape the brackets to match literal characters + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } -exports.removeBackslashes = str => { - return str.replace(REGEX_REMOVE_BACKSLASH, match => { - return match === '\\' ? '' : match; - }); -}; + // when the user specifies nothing, try to match both + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } -exports.supportsLookbehinds = () => { - const segs = process.version.slice(1).split('.').map(Number); - if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { - return true; - } - return false; -}; + /** + * Braces + */ -exports.isWindows = options => { - if (options && typeof options.windows === 'boolean') { - return options.windows; - } - return win32 === true || path.sep === '\\'; -}; + if (value === '{' && opts.nobrace !== true) { + increment('braces'); -exports.escapeLast = (input, char, lastIdx) => { - const idx = input.lastIndexOf(char, lastIdx); - if (idx === -1) return input; - if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); - return `${input.slice(0, idx)}\\${input.slice(idx)}`; -}; + const open = { + type: 'brace', + value, + output: '(', + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; -exports.removePrefix = (input, state = {}) => { - let output = input; - if (output.startsWith('./')) { - output = output.slice(2); - state.prefix = './'; - } - return output; -}; + braces.push(open); + push(open); + continue; + } -exports.wrapOutput = (input, state = {}, options = {}) => { - const prepend = options.contains ? '' : '^'; - const append = options.contains ? '' : '$'; + if (value === '}') { + const brace = braces[braces.length - 1]; - let output = `${prepend}(?:${input})${append}`; - if (state.negated === true) { - output = `(?:^(?!${output}).*$)`; - } - return output; -}; + if (opts.nobrace === true || !brace) { + push({ type: 'text', value, output: value }); + continue; + } + let output = ')'; -/***/ }), -/* 318 */ -/***/ (function(module, exports, __webpack_require__) { + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; -"use strict"; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === 'brace') { + break; + } + if (arr[i].type !== 'dots') { + range.unshift(arr[i].value); + } + } + output = expandRange(range, opts); + state.backtrack = true; + } -const path = __webpack_require__(4); -const WIN_SLASH = '\\\\/'; -const WIN_NO_SLASH = `[^${WIN_SLASH}]`; + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = '\\{'; + value = output = '\\}'; + state.output = out; + for (const t of toks) { + state.output += (t.output || t.value); + } + } -/** - * Posix glob regex - */ + push({ type: 'brace', value, output }); + decrement('braces'); + braces.pop(); + continue; + } -const DOT_LITERAL = '\\.'; -const PLUS_LITERAL = '\\+'; -const QMARK_LITERAL = '\\?'; -const SLASH_LITERAL = '\\/'; -const ONE_CHAR = '(?=.)'; -const QMARK = '[^/]'; -const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; -const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; -const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; -const NO_DOT = `(?!${DOT_LITERAL})`; -const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; -const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; -const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; -const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; -const STAR = `${QMARK}*?`; + /** + * Pipes + */ -const POSIX_CHARS = { - DOT_LITERAL, - PLUS_LITERAL, - QMARK_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - QMARK, - END_ANCHOR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOT_SLASH, - NO_DOTS_SLASH, - QMARK_NO_DOT, - STAR, - START_ANCHOR -}; + if (value === '|') { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: 'text', value }); + continue; + } -/** - * Windows glob regex - */ + /** + * Commas + */ -const WINDOWS_CHARS = { - ...POSIX_CHARS, + if (value === ',') { + let output = value; - SLASH_LITERAL: `[${WIN_SLASH}]`, - QMARK: WIN_NO_SLASH, - STAR: `${WIN_NO_SLASH}*?`, - DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, - NO_DOT: `(?!${DOT_LITERAL})`, - NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, - NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, - QMARK_NO_DOT: `[^.${WIN_SLASH}]`, - START_ANCHOR: `(?:^|[${WIN_SLASH}])`, - END_ANCHOR: `(?:[${WIN_SLASH}]|$)` -}; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === 'braces') { + brace.comma = true; + output = '|'; + } -/** - * POSIX Bracket Regex - */ + push({ type: 'comma', value, output }); + continue; + } -const POSIX_REGEX_SOURCE = { - alnum: 'a-zA-Z0-9', - alpha: 'a-zA-Z', - ascii: '\\x00-\\x7F', - blank: ' \\t', - cntrl: '\\x00-\\x1F\\x7F', - digit: '0-9', - graph: '\\x21-\\x7E', - lower: 'a-z', - print: '\\x20-\\x7E ', - punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', - space: ' \\t\\r\\n\\v\\f', - upper: 'A-Z', - word: 'A-Za-z0-9_', - xdigit: 'A-Fa-f0-9' -}; + /** + * Slashes + */ -module.exports = { - MAX_LENGTH: 1024 * 64, - POSIX_REGEX_SOURCE, + if (value === '/') { + // if the beginning of the glob is "./", advance the start + // to the current index, and don't add the "./" characters + // to the state. This greatly simplifies lookbehinds when + // checking for BOS characters like "!" and "." (not "./") + if (prev.type === 'dot' && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ''; + state.output = ''; + tokens.pop(); + prev = bos; // reset "prev" to the first token + continue; + } - // regular expressions - REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, - REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, - REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, - REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, - REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, - REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + push({ type: 'slash', value, output: SLASH_LITERAL }); + continue; + } - // Replace globs with equivalent patterns to reduce parsing time. - REPLACEMENTS: { - '***': '*', - '**/**': '**', - '**/**/**': '**' - }, + /** + * Dots + */ - // Digits - CHAR_0: 48, /* 0 */ - CHAR_9: 57, /* 9 */ + if (value === '.') { + if (state.braces > 0 && prev.type === 'dot') { + if (prev.value === '.') prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = 'dots'; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } - // Alphabet chars. - CHAR_UPPERCASE_A: 65, /* A */ - CHAR_LOWERCASE_A: 97, /* a */ - CHAR_UPPERCASE_Z: 90, /* Z */ - CHAR_LOWERCASE_Z: 122, /* z */ + if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { + push({ type: 'text', value, output: DOT_LITERAL }); + continue; + } - CHAR_LEFT_PARENTHESES: 40, /* ( */ - CHAR_RIGHT_PARENTHESES: 41, /* ) */ + push({ type: 'dot', value, output: DOT_LITERAL }); + continue; + } - CHAR_ASTERISK: 42, /* * */ + /** + * Question marks + */ - // Non-alphabetic chars. - CHAR_AMPERSAND: 38, /* & */ - CHAR_AT: 64, /* @ */ - CHAR_BACKWARD_SLASH: 92, /* \ */ - CHAR_CARRIAGE_RETURN: 13, /* \r */ - CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ - CHAR_COLON: 58, /* : */ - CHAR_COMMA: 44, /* , */ - CHAR_DOT: 46, /* . */ - CHAR_DOUBLE_QUOTE: 34, /* " */ - CHAR_EQUAL: 61, /* = */ - CHAR_EXCLAMATION_MARK: 33, /* ! */ - CHAR_FORM_FEED: 12, /* \f */ - CHAR_FORWARD_SLASH: 47, /* / */ - CHAR_GRAVE_ACCENT: 96, /* ` */ - CHAR_HASH: 35, /* # */ - CHAR_HYPHEN_MINUS: 45, /* - */ - CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ - CHAR_LEFT_CURLY_BRACE: 123, /* { */ - CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ - CHAR_LINE_FEED: 10, /* \n */ - CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ - CHAR_PERCENT: 37, /* % */ - CHAR_PLUS: 43, /* + */ - CHAR_QUESTION_MARK: 63, /* ? */ - CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ - CHAR_RIGHT_CURLY_BRACE: 125, /* } */ - CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ - CHAR_SEMICOLON: 59, /* ; */ - CHAR_SINGLE_QUOTE: 39, /* ' */ - CHAR_SPACE: 32, /* */ - CHAR_TAB: 9, /* \t */ - CHAR_UNDERSCORE: 95, /* _ */ - CHAR_VERTICAL_LINE: 124, /* | */ - CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ + if (value === '?') { + const isGroup = prev && prev.value === '('; + if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('qmark', value); + continue; + } - SEP: path.sep, + if (prev && prev.type === 'paren') { + const next = peek(); + let output = value; - /** - * Create EXTGLOB_CHARS - */ + if (next === '<' && !utils.supportsLookbehinds()) { + throw new Error('Node.js v10 or higher is required for regex lookbehinds'); + } - extglobChars(chars) { - return { - '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, - '?': { type: 'qmark', open: '(?:', close: ')?' }, - '+': { type: 'plus', open: '(?:', close: ')+' }, - '*': { type: 'star', open: '(?:', close: ')*' }, - '@': { type: 'at', open: '(?:', close: ')' } - }; - }, + if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { + output = `\\${value}`; + } - /** - * Create GLOB_CHARS - */ + push({ type: 'text', value, output }); + continue; + } - globChars(win32) { - return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; - } -}; + if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { + push({ type: 'qmark', value, output: QMARK_NO_DOT }); + continue; + } + push({ type: 'qmark', value, output: QMARK }); + continue; + } -/***/ }), -/* 319 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Exclamation + */ -"use strict"; + if (value === '!') { + if (opts.noextglob !== true && peek() === '(') { + if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { + extglobOpen('negate', value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } -const constants = __webpack_require__(318); -const utils = __webpack_require__(317); + /** + * Plus + */ -/** - * Constants - */ + if (value === '+') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('plus', value); + continue; + } -const { - MAX_LENGTH, - POSIX_REGEX_SOURCE, - REGEX_NON_SPECIAL_CHARS, - REGEX_SPECIAL_CHARS_BACKREF, - REPLACEMENTS -} = constants; + if ((prev && prev.value === '(') || opts.regex === false) { + push({ type: 'plus', value, output: PLUS_LITERAL }); + continue; + } -/** - * Helpers - */ + if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { + push({ type: 'plus', value }); + continue; + } -const expandRange = (args, options) => { - if (typeof options.expandRange === 'function') { - return options.expandRange(...args, options); + push({ type: 'plus', value: PLUS_LITERAL }); + continue; + } + + /** + * Plain text + */ + + if (value === '@') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + push({ type: 'at', extglob: true, value, output: '' }); + continue; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Plain text + */ + + if (value !== '*') { + if (value === '$' || value === '^') { + value = `\\${value}`; + } + + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Stars + */ + + if (prev && (prev.type === 'globstar' || prev.star === true)) { + prev.type = 'star'; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen('star', value); + continue; + } + + if (prev.type === 'star') { + if (opts.noglobstar === true) { + consume(value); + continue; + } + + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === 'slash' || prior.type === 'bos'; + const afterStar = before && (before.type === 'star' || before.type === 'globstar'); + + if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { + push({ type: 'star', value, output: '' }); + continue; + } + + const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); + const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); + if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { + push({ type: 'star', value, output: '' }); + continue; + } + + // strip consecutive `/**/` + while (rest.slice(0, 3) === '/**') { + const after = input[state.index + 4]; + if (after && after !== '/') { + break; + } + rest = rest.slice(3); + consume('/**', 3); + } + + if (prior.type === 'bos' && eos()) { + prev.type = 'globstar'; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + + if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + + prev.type = 'globstar'; + prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + + if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { + const end = rest[1] !== void 0 ? '|$' : ''; + + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + + prev.type = 'globstar'; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + + state.output += prior.output + prev.output; + state.globstar = true; + + consume(value + advance()); + + push({ type: 'slash', value: '/', output: '' }); + continue; + } + + if (prior.type === 'bos' && rest[0] === '/') { + prev.type = 'globstar'; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: 'slash', value: '/', output: '' }); + continue; + } + + // remove single star from output + state.output = state.output.slice(0, -prev.output.length); + + // reset previous token to globstar + prev.type = 'globstar'; + prev.output = globstar(opts); + prev.value += value; + + // reset output with globstar + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + + const token = { type: 'star', value, output: star }; + + if (opts.bash === true) { + token.output = '.*?'; + if (prev.type === 'bos' || prev.type === 'slash') { + token.output = nodot + token.output; + } + push(token); + continue; + } + + if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { + token.output = value; + push(token); + continue; + } + + if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { + if (prev.type === 'dot') { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; + + } else { + state.output += nodot; + prev.output += nodot; + } + + if (peek() !== '*') { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } + + push(token); } - args.sort(); - const value = `[${args.join('-')}]`; + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); + state.output = utils.escapeLast(state.output, '['); + decrement('brackets'); + } - try { - /* eslint-disable-next-line no-new */ - new RegExp(value); - } catch (ex) { - return args.map(v => utils.escapeRegex(v)).join('..'); + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); + state.output = utils.escapeLast(state.output, '('); + decrement('parens'); } - return value; -}; + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); + state.output = utils.escapeLast(state.output, '{'); + decrement('braces'); + } -/** - * Create the message for a syntax error - */ + if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { + push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); + } -const syntaxError = (type, char) => { - return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; -}; + // rebuild the output if we had to backtrack at any point + if (state.backtrack === true) { + state.output = ''; -/** - * Parse the given input string. - * @param {String} input - * @param {Object} options - * @return {Object} - */ + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; -const parse = (input, options) => { - if (typeof input !== 'string') { - throw new TypeError('Expected a string'); + if (token.suffix) { + state.output += token.suffix; + } + } } - input = REPLACEMENTS[input] || input; + return state; +}; + +/** + * Fast paths for creating regular expressions for common glob patterns. + * This can significantly speed up processing and has very little downside + * impact when none of the fast paths match. + */ +parse.fastpaths = (input, options) => { const opts = { ...options }; const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - - let len = input.length; + const len = input.length; if (len > max) { throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); } - const bos = { type: 'bos', value: '', output: opts.prepend || '' }; - const tokens = [bos]; - - const capture = opts.capture ? '' : '?:'; + input = REPLACEMENTS[input] || input; const win32 = utils.isWindows(options); // create constants based on platform, for windows or posix - const PLATFORM_CHARS = constants.globChars(win32); - const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); - const { DOT_LITERAL, - PLUS_LITERAL, SLASH_LITERAL, ONE_CHAR, DOTS_SLASH, NO_DOT, - NO_DOT_SLASH, + NO_DOTS, NO_DOTS_SLASH, - QMARK, - QMARK_NO_DOT, STAR, START_ANCHOR - } = PLATFORM_CHARS; - - const globstar = (opts) => { - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; + } = constants.globChars(win32); - const nodot = opts.dot ? '' : NO_DOT; - const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; - let star = opts.bash === true ? globstar(opts) : STAR; + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? '' : '?:'; + const state = { negated: false, prefix: '' }; + let star = opts.bash === true ? '.*?' : STAR; if (opts.capture) { star = `(${star})`; } - // minimatch options support - if (typeof opts.noext === 'boolean') { - opts.noextglob = opts.noext; - } - - const state = { - input, - index: -1, - start: 0, - dot: opts.dot === true, - consumed: '', - output: '', - prefix: '', - backtrack: false, - negated: false, - brackets: 0, - braces: 0, - parens: 0, - quotes: 0, - globstar: false, - tokens + const globstar = (opts) => { + if (opts.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; }; - input = utils.removePrefix(input, state); - len = input.length; - - const extglobs = []; - const braces = []; - const stack = []; - let prev = bos; - let value; - - /** - * Tokenizing helpers - */ + const create = str => { + switch (str) { + case '*': + return `${nodot}${ONE_CHAR}${star}`; - const eos = () => state.index === len - 1; - const peek = state.peek = (n = 1) => input[state.index + n]; - const advance = state.advance = () => input[++state.index]; - const remaining = () => input.slice(state.index + 1); - const consume = (value = '', num = 0) => { - state.consumed += value; - state.index += num; - }; - const append = token => { - state.output += token.output != null ? token.output : token.value; - consume(token.value); - }; + case '.*': + return `${DOT_LITERAL}${ONE_CHAR}${star}`; - const negate = () => { - let count = 1; + case '*.*': + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { - advance(); - state.start++; - count++; - } + case '*/*': + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - if (count % 2 === 0) { - return false; - } + case '**': + return nodot + globstar(opts); - state.negated = true; - state.start++; - return true; - }; + case '**/*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - const increment = type => { - state[type]++; - stack.push(type); - }; + case '**/*.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - const decrement = type => { - state[type]--; - stack.pop(); - }; + case '**/.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - /** - * Push tokens onto the tokens array. This helper speeds up - * tokenizing by 1) helping us avoid backtracking as much as possible, - * and 2) helping us avoid creating extra tokens when consecutive - * characters are plain text. This improves performance and simplifies - * lookbehinds. - */ + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; - const push = tok => { - if (prev.type === 'globstar') { - const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); - const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); + const source = create(match[1]); + if (!source) return; - if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { - state.output = state.output.slice(0, -prev.output.length); - prev.type = 'star'; - prev.value = '*'; - prev.output = star; - state.output += prev.output; + return source + DOT_LITERAL + match[2]; } } + }; - if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { - extglobs[extglobs.length - 1].inner += tok.value; - } + const output = utils.removePrefix(input, state); + let source = create(output); - if (tok.value || tok.output) append(tok); - if (prev && prev.type === 'text' && tok.type === 'text') { - prev.value += tok.value; - prev.output = (prev.output || '') + tok.value; - return; - } + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } - tok.prev = prev; - tokens.push(tok); - prev = tok; - }; + return source; +}; - const extglobOpen = (type, value) => { - const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; +module.exports = parse; - token.prev = prev; - token.parens = state.parens; - token.output = state.output; - const output = (opts.capture ? '(' : '') + token.open; - increment('parens'); - push({ type, value, output: state.output ? '' : ONE_CHAR }); - push({ type: 'paren', extglob: true, value: advance(), output }); - extglobs.push(token); - }; +/***/ }), +/* 317 */ +/***/ (function(module, exports, __webpack_require__) { - const extglobClose = token => { - let output = token.close + (opts.capture ? ')' : ''); +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const merge2 = __webpack_require__(288); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} - if (token.type === 'negate') { - let extglobStar = star; - if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { - extglobStar = globstar(opts); - } - - if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { - output = token.close = `)$))${extglobStar}`; - } - - if (token.prev.type === 'bos' && eos()) { - state.negatedExtglob = true; - } - } - - push({ type: 'paren', extglob: true, value, output }); - decrement('parens'); - }; - - /** - * Fast paths - */ - - if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { - let backslashes = false; - - let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { - if (first === '\\') { - backslashes = true; - return m; - } - - if (first === '?') { - if (esc) { - return esc + first + (rest ? QMARK.repeat(rest.length) : ''); - } - if (index === 0) { - return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); - } - return QMARK.repeat(chars.length); - } - - if (first === '.') { - return DOT_LITERAL.repeat(chars.length); - } - - if (first === '*') { - if (esc) { - return esc + first + (rest ? star : ''); - } - return star; - } - return esc ? m : `\\${m}`; - }); - - if (backslashes === true) { - if (opts.unescape === true) { - output = output.replace(/\\/g, ''); - } else { - output = output.replace(/\\+/g, m => { - return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); - }); - } - } - - if (output === input && opts.contains === true) { - state.output = input; - return state; - } - - state.output = utils.wrapOutput(output, state, options); - return state; - } - - /** - * Tokenize input until we reach end-of-string - */ - - while (!eos()) { - value = advance(); - - if (value === '\u0000') { - continue; - } - - /** - * Escaped characters - */ - - if (value === '\\') { - const next = peek(); - - if (next === '/' && opts.bash !== true) { - continue; - } - - if (next === '.' || next === ';') { - continue; - } - - if (!next) { - value += '\\'; - push({ type: 'text', value }); - continue; - } - - // collapse slashes to reduce potential for exploits - const match = /^\\+/.exec(remaining()); - let slashes = 0; - - if (match && match[0].length > 2) { - slashes = match[0].length; - state.index += slashes; - if (slashes % 2 !== 0) { - value += '\\'; - } - } - - if (opts.unescape === true) { - value = advance() || ''; - } else { - value += advance() || ''; - } - - if (state.brackets === 0) { - push({ type: 'text', value }); - continue; - } - } - - /** - * If we're inside a regex character class, continue - * until we reach the closing bracket. - */ - - if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { - if (opts.posix !== false && value === ':') { - const inner = prev.value.slice(1); - if (inner.includes('[')) { - prev.posix = true; - - if (inner.includes(':')) { - const idx = prev.value.lastIndexOf('['); - const pre = prev.value.slice(0, idx); - const rest = prev.value.slice(idx + 2); - const posix = POSIX_REGEX_SOURCE[rest]; - if (posix) { - prev.value = pre + posix; - state.backtrack = true; - advance(); - - if (!bos.output && tokens.indexOf(prev) === 1) { - bos.output = ONE_CHAR; - } - continue; - } - } - } - } - - if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { - value = `\\${value}`; - } - - if (value === ']' && (prev.value === '[' || prev.value === '[^')) { - value = `\\${value}`; - } - - if (opts.posix === true && value === '!' && prev.value === '[') { - value = '^'; - } - - prev.value += value; - append({ value }); - continue; - } - - /** - * If we're inside a quoted string, continue - * until we reach the closing double quote. - */ - - if (state.quotes === 1 && value !== '"') { - value = utils.escapeRegex(value); - prev.value += value; - append({ value }); - continue; - } - - /** - * Double quotes - */ - - if (value === '"') { - state.quotes = state.quotes === 1 ? 0 : 1; - if (opts.keepQuotes === true) { - push({ type: 'text', value }); - } - continue; - } - - /** - * Parentheses - */ - - if (value === '(') { - increment('parens'); - push({ type: 'paren', value }); - continue; - } - - if (value === ')') { - if (state.parens === 0 && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '(')); - } - - const extglob = extglobs[extglobs.length - 1]; - if (extglob && state.parens === extglob.parens + 1) { - extglobClose(extglobs.pop()); - continue; - } - - push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); - decrement('parens'); - continue; - } - - /** - * Square brackets - */ - - if (value === '[') { - if (opts.nobracket === true || !remaining().includes(']')) { - if (opts.nobracket !== true && opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('closing', ']')); - } - - value = `\\${value}`; - } else { - increment('brackets'); - } - - push({ type: 'bracket', value }); - continue; - } - - if (value === ']') { - if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - if (state.brackets === 0) { - if (opts.strictBrackets === true) { - throw new SyntaxError(syntaxError('opening', '[')); - } - - push({ type: 'text', value, output: `\\${value}` }); - continue; - } - - decrement('brackets'); - - const prevValue = prev.value.slice(1); - if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { - value = `/${value}`; - } - - prev.value += value; - append({ value }); - - // when literal brackets are explicitly disabled - // assume we should match with a regex character class - if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { - continue; - } - - const escaped = utils.escapeRegex(prev.value); - state.output = state.output.slice(0, -prev.value.length); - - // when literal brackets are explicitly enabled - // assume we should escape the brackets to match literal characters - if (opts.literalBrackets === true) { - state.output += escaped; - prev.value = escaped; - continue; - } - - // when the user specifies nothing, try to match both - prev.value = `(${capture}${escaped}|${prev.value})`; - state.output += prev.value; - continue; - } - - /** - * Braces - */ - - if (value === '{' && opts.nobrace !== true) { - increment('braces'); - - const open = { - type: 'brace', - value, - output: '(', - outputIndex: state.output.length, - tokensIndex: state.tokens.length - }; - - braces.push(open); - push(open); - continue; - } - - if (value === '}') { - const brace = braces[braces.length - 1]; - - if (opts.nobrace === true || !brace) { - push({ type: 'text', value, output: value }); - continue; - } - - let output = ')'; - - if (brace.dots === true) { - const arr = tokens.slice(); - const range = []; - - for (let i = arr.length - 1; i >= 0; i--) { - tokens.pop(); - if (arr[i].type === 'brace') { - break; - } - if (arr[i].type !== 'dots') { - range.unshift(arr[i].value); - } - } - - output = expandRange(range, opts); - state.backtrack = true; - } - - if (brace.comma !== true && brace.dots !== true) { - const out = state.output.slice(0, brace.outputIndex); - const toks = state.tokens.slice(brace.tokensIndex); - brace.value = brace.output = '\\{'; - value = output = '\\}'; - state.output = out; - for (const t of toks) { - state.output += (t.output || t.value); - } - } - - push({ type: 'brace', value, output }); - decrement('braces'); - braces.pop(); - continue; - } - - /** - * Pipes - */ - - if (value === '|') { - if (extglobs.length > 0) { - extglobs[extglobs.length - 1].conditions++; - } - push({ type: 'text', value }); - continue; - } - - /** - * Commas - */ - - if (value === ',') { - let output = value; - - const brace = braces[braces.length - 1]; - if (brace && stack[stack.length - 1] === 'braces') { - brace.comma = true; - output = '|'; - } - - push({ type: 'comma', value, output }); - continue; - } - - /** - * Slashes - */ - - if (value === '/') { - // if the beginning of the glob is "./", advance the start - // to the current index, and don't add the "./" characters - // to the state. This greatly simplifies lookbehinds when - // checking for BOS characters like "!" and "." (not "./") - if (prev.type === 'dot' && state.index === state.start + 1) { - state.start = state.index + 1; - state.consumed = ''; - state.output = ''; - tokens.pop(); - prev = bos; // reset "prev" to the first token - continue; - } - - push({ type: 'slash', value, output: SLASH_LITERAL }); - continue; - } - - /** - * Dots - */ - - if (value === '.') { - if (state.braces > 0 && prev.type === 'dot') { - if (prev.value === '.') prev.output = DOT_LITERAL; - const brace = braces[braces.length - 1]; - prev.type = 'dots'; - prev.output += value; - prev.value += value; - brace.dots = true; - continue; - } - - if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { - push({ type: 'text', value, output: DOT_LITERAL }); - continue; - } - - push({ type: 'dot', value, output: DOT_LITERAL }); - continue; - } - - /** - * Question marks - */ - - if (value === '?') { - const isGroup = prev && prev.value === '('; - if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('qmark', value); - continue; - } - - if (prev && prev.type === 'paren') { - const next = peek(); - let output = value; - - if (next === '<' && !utils.supportsLookbehinds()) { - throw new Error('Node.js v10 or higher is required for regex lookbehinds'); - } - - if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { - output = `\\${value}`; - } - - push({ type: 'text', value, output }); - continue; - } - - if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { - push({ type: 'qmark', value, output: QMARK_NO_DOT }); - continue; - } - - push({ type: 'qmark', value, output: QMARK }); - continue; - } - - /** - * Exclamation - */ - - if (value === '!') { - if (opts.noextglob !== true && peek() === '(') { - if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { - extglobOpen('negate', value); - continue; - } - } - - if (opts.nonegate !== true && state.index === 0) { - negate(); - continue; - } - } - - /** - * Plus - */ - - if (value === '+') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - extglobOpen('plus', value); - continue; - } - - if ((prev && prev.value === '(') || opts.regex === false) { - push({ type: 'plus', value, output: PLUS_LITERAL }); - continue; - } - - if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { - push({ type: 'plus', value }); - continue; - } - - push({ type: 'plus', value: PLUS_LITERAL }); - continue; - } - - /** - * Plain text - */ - - if (value === '@') { - if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { - push({ type: 'at', extglob: true, value, output: '' }); - continue; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Plain text - */ - - if (value !== '*') { - if (value === '$' || value === '^') { - value = `\\${value}`; - } - - const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); - if (match) { - value += match[0]; - state.index += match[0].length; - } - - push({ type: 'text', value }); - continue; - } - - /** - * Stars - */ - - if (prev && (prev.type === 'globstar' || prev.star === true)) { - prev.type = 'star'; - prev.star = true; - prev.value += value; - prev.output = star; - state.backtrack = true; - state.globstar = true; - consume(value); - continue; - } - - let rest = remaining(); - if (opts.noextglob !== true && /^\([^?]/.test(rest)) { - extglobOpen('star', value); - continue; - } - - if (prev.type === 'star') { - if (opts.noglobstar === true) { - consume(value); - continue; - } - - const prior = prev.prev; - const before = prior.prev; - const isStart = prior.type === 'slash' || prior.type === 'bos'; - const afterStar = before && (before.type === 'star' || before.type === 'globstar'); - - if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { - push({ type: 'star', value, output: '' }); - continue; - } - - const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); - const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); - if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { - push({ type: 'star', value, output: '' }); - continue; - } - - // strip consecutive `/**/` - while (rest.slice(0, 3) === '/**') { - const after = input[state.index + 4]; - if (after && after !== '/') { - break; - } - rest = rest.slice(3); - consume('/**', 3); - } - - if (prior.type === 'bos' && eos()) { - prev.type = 'globstar'; - prev.value += value; - prev.output = globstar(opts); - state.output = prev.output; - state.globstar = true; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); - prev.value += value; - state.globstar = true; - state.output += prior.output + prev.output; - consume(value); - continue; - } - - if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { - const end = rest[1] !== void 0 ? '|$' : ''; - - state.output = state.output.slice(0, -(prior.output + prev.output).length); - prior.output = `(?:${prior.output}`; - - prev.type = 'globstar'; - prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; - prev.value += value; - - state.output += prior.output + prev.output; - state.globstar = true; - - consume(value + advance()); - - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - if (prior.type === 'bos' && rest[0] === '/') { - prev.type = 'globstar'; - prev.value += value; - prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; - state.output = prev.output; - state.globstar = true; - consume(value + advance()); - push({ type: 'slash', value: '/', output: '' }); - continue; - } - - // remove single star from output - state.output = state.output.slice(0, -prev.output.length); - - // reset previous token to globstar - prev.type = 'globstar'; - prev.output = globstar(opts); - prev.value += value; - - // reset output with globstar - state.output += prev.output; - state.globstar = true; - consume(value); - continue; - } - - const token = { type: 'star', value, output: star }; - - if (opts.bash === true) { - token.output = '.*?'; - if (prev.type === 'bos' || prev.type === 'slash') { - token.output = nodot + token.output; - } - push(token); - continue; - } - - if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { - token.output = value; - push(token); - continue; - } - - if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { - if (prev.type === 'dot') { - state.output += NO_DOT_SLASH; - prev.output += NO_DOT_SLASH; - - } else if (opts.dot === true) { - state.output += NO_DOTS_SLASH; - prev.output += NO_DOTS_SLASH; - - } else { - state.output += nodot; - prev.output += nodot; - } - - if (peek() !== '*') { - state.output += ONE_CHAR; - prev.output += ONE_CHAR; - } - } - - push(token); - } - - while (state.brackets > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); - state.output = utils.escapeLast(state.output, '['); - decrement('brackets'); - } - - while (state.parens > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); - state.output = utils.escapeLast(state.output, '('); - decrement('parens'); - } - - while (state.braces > 0) { - if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); - state.output = utils.escapeLast(state.output, '{'); - decrement('braces'); - } - - if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { - push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); - } - - // rebuild the output if we had to backtrack at any point - if (state.backtrack === true) { - state.output = ''; - - for (const token of state.tokens) { - state.output += token.output != null ? token.output : token.value; - - if (token.suffix) { - state.output += token.suffix; - } - } - } - - return state; -}; - -/** - * Fast paths for creating regular expressions for common glob patterns. - * This can significantly speed up processing and has very little downside - * impact when none of the fast paths match. - */ - -parse.fastpaths = (input, options) => { - const opts = { ...options }; - const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - const len = input.length; - if (len > max) { - throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); - } - - input = REPLACEMENTS[input] || input; - const win32 = utils.isWindows(options); - - // create constants based on platform, for windows or posix - const { - DOT_LITERAL, - SLASH_LITERAL, - ONE_CHAR, - DOTS_SLASH, - NO_DOT, - NO_DOTS, - NO_DOTS_SLASH, - STAR, - START_ANCHOR - } = constants.globChars(win32); - - const nodot = opts.dot ? NO_DOTS : NO_DOT; - const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; - const capture = opts.capture ? '' : '?:'; - const state = { negated: false, prefix: '' }; - let star = opts.bash === true ? '.*?' : STAR; - - if (opts.capture) { - star = `(${star})`; - } - - const globstar = (opts) => { - if (opts.noglobstar === true) return star; - return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; - }; - - const create = str => { - switch (str) { - case '*': - return `${nodot}${ONE_CHAR}${star}`; - - case '.*': - return `${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*.*': - return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '*/*': - return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - - case '**': - return nodot + globstar(opts); - - case '**/*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - - case '**/*.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - - case '**/.*': - return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - - default: { - const match = /^(.*?)\.(\w+)$/.exec(str); - if (!match) return; - - const source = create(match[1]); - if (!source) return; - - return source + DOT_LITERAL + match[2]; - } - } - }; - - const output = utils.removePrefix(input, state); - let source = create(output); - - if (source && opts.strictSlashes !== true) { - source += `${SLASH_LITERAL}?`; - } - - return source; -}; - -module.exports = parse; - - -/***/ }), -/* 320 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(291); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} - - -/***/ }), -/* 321 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; - - -/***/ }), -/* 322 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(323); -const provider_1 = __webpack_require__(350); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; - - -/***/ }), -/* 323 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const fsStat = __webpack_require__(324); -const fsWalk = __webpack_require__(329); -const reader_1 = __webpack_require__(349); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; - - -/***/ }), -/* 324 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(325); -const sync = __webpack_require__(326); -const settings_1 = __webpack_require__(327); -exports.Settings = settings_1.default; -function stat(path, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return async.read(path, getSettings(), optionsOrSettingsOrCallback); - } - async.read(path, getSettings(optionsOrSettingsOrCallback), callback); -} -exports.stat = stat; -function statSync(path, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - return sync.read(path, settings); -} -exports.statSync = statSync; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} - - -/***/ }), -/* 325 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function read(path, settings, callback) { - settings.fs.lstat(path, (lstatError, lstat) => { - if (lstatError !== null) { - return callFailureCallback(callback, lstatError); - } - if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { - return callSuccessCallback(callback, lstat); - } - settings.fs.stat(path, (statError, stat) => { - if (statError !== null) { - if (settings.throwErrorOnBrokenSymbolicLink) { - return callFailureCallback(callback, statError); - } - return callSuccessCallback(callback, lstat); - } - if (settings.markSymbolicLink) { - stat.isSymbolicLink = () => true; - } - callSuccessCallback(callback, stat); - }); - }); -} -exports.read = read; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, result) { - callback(null, result); -} - - -/***/ }), -/* 326 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function read(path, settings) { - const lstat = settings.fs.lstatSync(path); - if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { - return lstat; - } - try { - const stat = settings.fs.statSync(path); - if (settings.markSymbolicLink) { - stat.isSymbolicLink = () => true; - } - return stat; - } - catch (error) { - if (!settings.throwErrorOnBrokenSymbolicLink) { - return lstat; - } - throw error; - } -} -exports.read = read; - - -/***/ }), -/* 327 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(328); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true); - this.fs = fs.createFileSystemAdapter(this._options.fs); - this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); - } - _getValue(option, value) { - return option === undefined ? value : option; - } -} -exports.default = Settings; - - -/***/ }), -/* 328 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); -exports.FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - stat: fs.stat, - lstatSync: fs.lstatSync, - statSync: fs.statSync -}; -function createFileSystemAdapter(fsMethods) { - if (fsMethods === undefined) { - return exports.FILE_SYSTEM_ADAPTER; - } - return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); -} -exports.createFileSystemAdapter = createFileSystemAdapter; - - -/***/ }), -/* 329 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(330); -const stream_1 = __webpack_require__(345); -const sync_1 = __webpack_require__(346); -const settings_1 = __webpack_require__(348); -exports.Settings = settings_1.default; -function walk(directory, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return new async_1.default(directory, getSettings()).read(optionsOrSettingsOrCallback); - } - new async_1.default(directory, getSettings(optionsOrSettingsOrCallback)).read(callback); -} -exports.walk = walk; -function walkSync(directory, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - const provider = new sync_1.default(directory, settings); - return provider.read(); -} -exports.walkSync = walkSync; -function walkStream(directory, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - const provider = new stream_1.default(directory, settings); - return provider.read(); -} -exports.walkStream = walkStream; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} - - -/***/ }), -/* 330 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(331); -class AsyncProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new async_1.default(this._root, this._settings); - this._storage = new Set(); - } - read(callback) { - this._reader.onError((error) => { - callFailureCallback(callback, error); - }); - this._reader.onEntry((entry) => { - this._storage.add(entry); - }); - this._reader.onEnd(() => { - callSuccessCallback(callback, [...this._storage]); - }); - this._reader.read(); - } -} -exports.default = AsyncProvider; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, entries) { - callback(null, entries); -} - - -/***/ }), -/* 331 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const events_1 = __webpack_require__(155); -const fsScandir = __webpack_require__(332); -const fastq = __webpack_require__(341); -const common = __webpack_require__(343); -const reader_1 = __webpack_require__(344); -class AsyncReader extends reader_1.default { - constructor(_root, _settings) { - super(_root, _settings); - this._settings = _settings; - this._scandir = fsScandir.scandir; - this._emitter = new events_1.EventEmitter(); - this._queue = fastq(this._worker.bind(this), this._settings.concurrency); - this._isFatalError = false; - this._isDestroyed = false; - this._queue.drain = () => { - if (!this._isFatalError) { - this._emitter.emit('end'); - } - }; - } - read() { - this._isFatalError = false; - this._isDestroyed = false; - setImmediate(() => { - this._pushToQueue(this._root, this._settings.basePath); - }); - return this._emitter; - } - destroy() { - if (this._isDestroyed) { - throw new Error('The reader is already destroyed'); - } - this._isDestroyed = true; - this._queue.killAndDrain(); - } - onEntry(callback) { - this._emitter.on('entry', callback); - } - onError(callback) { - this._emitter.once('error', callback); - } - onEnd(callback) { - this._emitter.once('end', callback); - } - _pushToQueue(directory, base) { - const queueItem = { directory, base }; - this._queue.push(queueItem, (error) => { - if (error !== null) { - this._handleError(error); - } - }); - } - _worker(item, done) { - this._scandir(item.directory, this._settings.fsScandirSettings, (error, entries) => { - if (error !== null) { - return done(error, undefined); - } - for (const entry of entries) { - this._handleEntry(entry, item.base); - } - done(null, undefined); - }); - } - _handleError(error) { - if (!common.isFatalError(this._settings, error)) { - return; - } - this._isFatalError = true; - this._isDestroyed = true; - this._emitter.emit('error', error); - } - _handleEntry(entry, base) { - if (this._isDestroyed || this._isFatalError) { - return; - } - const fullpath = entry.path; - if (base !== undefined) { - entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); - } - if (common.isAppliedFilter(this._settings.entryFilter, entry)) { - this._emitEntry(entry); - } - if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { - this._pushToQueue(fullpath, entry.path); - } - } - _emitEntry(entry) { - this._emitter.emit('entry', entry); - } -} -exports.default = AsyncReader; - - -/***/ }), -/* 332 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(333); -const sync = __webpack_require__(338); -const settings_1 = __webpack_require__(339); -exports.Settings = settings_1.default; -function scandir(path, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return async.read(path, getSettings(), optionsOrSettingsOrCallback); - } - async.read(path, getSettings(optionsOrSettingsOrCallback), callback); -} -exports.scandir = scandir; -function scandirSync(path, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - return sync.read(path, settings); -} -exports.scandirSync = scandirSync; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} - - -/***/ }), -/* 333 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(324); -const rpl = __webpack_require__(334); -const constants_1 = __webpack_require__(335); -const utils = __webpack_require__(336); -function read(directory, settings, callback) { - if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { - return readdirWithFileTypes(directory, settings, callback); - } - return readdir(directory, settings, callback); -} -exports.read = read; -function readdirWithFileTypes(directory, settings, callback) { - settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => { - if (readdirError !== null) { - return callFailureCallback(callback, readdirError); - } - const entries = dirents.map((dirent) => ({ - dirent, - name: dirent.name, - path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` - })); - if (!settings.followSymbolicLinks) { - return callSuccessCallback(callback, entries); - } - const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings)); - rpl(tasks, (rplError, rplEntries) => { - if (rplError !== null) { - return callFailureCallback(callback, rplError); - } - callSuccessCallback(callback, rplEntries); - }); - }); -} -exports.readdirWithFileTypes = readdirWithFileTypes; -function makeRplTaskEntry(entry, settings) { - return (done) => { - if (!entry.dirent.isSymbolicLink()) { - return done(null, entry); - } - settings.fs.stat(entry.path, (statError, stats) => { - if (statError !== null) { - if (settings.throwErrorOnBrokenSymbolicLink) { - return done(statError); - } - return done(null, entry); - } - entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); - return done(null, entry); - }); - }; -} -function readdir(directory, settings, callback) { - settings.fs.readdir(directory, (readdirError, names) => { - if (readdirError !== null) { - return callFailureCallback(callback, readdirError); - } - const filepaths = names.map((name) => `${directory}${settings.pathSegmentSeparator}${name}`); - const tasks = filepaths.map((filepath) => { - return (done) => fsStat.stat(filepath, settings.fsStatSettings, done); - }); - rpl(tasks, (rplError, results) => { - if (rplError !== null) { - return callFailureCallback(callback, rplError); - } - const entries = []; - names.forEach((name, index) => { - const stats = results[index]; - const entry = { - name, - path: filepaths[index], - dirent: utils.fs.createDirentFromStats(name, stats) - }; - if (settings.stats) { - entry.stats = stats; - } - entries.push(entry); - }); - callSuccessCallback(callback, entries); - }); - }); -} -exports.readdir = readdir; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, result) { - callback(null, result); -} - - -/***/ }), -/* 334 */ -/***/ (function(module, exports) { - -module.exports = runParallel - -function runParallel (tasks, cb) { - var results, pending, keys - var isSync = true - - if (Array.isArray(tasks)) { - results = [] - pending = tasks.length - } else { - keys = Object.keys(tasks) - results = {} - pending = keys.length - } - - function done (err) { - function end () { - if (cb) cb(err, results) - cb = null - } - if (isSync) process.nextTick(end) - else end() - } - - function each (i, err, result) { - results[i] = result - if (--pending === 0 || err) { - done(err) - } - } - - if (!pending) { - // empty - done(null) - } else if (keys) { - // object - keys.forEach(function (key) { - tasks[key](function (err, result) { each(key, err, result) }) - }) - } else { - // array - tasks.forEach(function (task, i) { - task(function (err, result) { each(i, err, result) }) - }) - } - - isSync = false -} - - -/***/ }), -/* 335 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.'); -const MAJOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[0], 10); -const MINOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[1], 10); -const SUPPORTED_MAJOR_VERSION = 10; -const SUPPORTED_MINOR_VERSION = 10; -const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION; -const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION; -/** - * IS `true` for Node.js 10.10 and greater. - */ -exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR; - - -/***/ }), -/* 336 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(337); -exports.fs = fs; - - -/***/ }), -/* 337 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; - - -/***/ }), -/* 338 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(324); -const constants_1 = __webpack_require__(335); -const utils = __webpack_require__(336); -function read(directory, settings) { - if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { - return readdirWithFileTypes(directory, settings); - } - return readdir(directory, settings); -} -exports.read = read; -function readdirWithFileTypes(directory, settings) { - const dirents = settings.fs.readdirSync(directory, { withFileTypes: true }); - return dirents.map((dirent) => { - const entry = { - dirent, - name: dirent.name, - path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` - }; - if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) { - try { - const stats = settings.fs.statSync(entry.path); - entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); - } - catch (error) { - if (settings.throwErrorOnBrokenSymbolicLink) { - throw error; - } - } - } - return entry; - }); -} -exports.readdirWithFileTypes = readdirWithFileTypes; -function readdir(directory, settings) { - const names = settings.fs.readdirSync(directory); - return names.map((name) => { - const entryPath = `${directory}${settings.pathSegmentSeparator}${name}`; - const stats = fsStat.statSync(entryPath, settings.fsStatSettings); - const entry = { - name, - path: entryPath, - dirent: utils.fs.createDirentFromStats(name, stats) - }; - if (settings.stats) { - entry.stats = stats; - } - return entry; - }); -} -exports.readdir = readdir; - - -/***/ }), -/* 339 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(324); -const fs = __webpack_require__(340); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false); - this.fs = fs.createFileSystemAdapter(this._options.fs); - this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); - this.stats = this._getValue(this._options.stats, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); - this.fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this.followSymbolicLinks, - fs: this.fs, - throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink - }); - } - _getValue(option, value) { - return option === undefined ? value : option; - } -} -exports.default = Settings; - - -/***/ }), -/* 340 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); -exports.FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - stat: fs.stat, - lstatSync: fs.lstatSync, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -function createFileSystemAdapter(fsMethods) { - if (fsMethods === undefined) { - return exports.FILE_SYSTEM_ADAPTER; - } - return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); -} -exports.createFileSystemAdapter = createFileSystemAdapter; - - -/***/ }), -/* 341 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var reusify = __webpack_require__(342) - -function fastqueue (context, worker, concurrency) { - if (typeof context === 'function') { - concurrency = worker - worker = context - context = null - } - - var cache = reusify(Task) - var queueHead = null - var queueTail = null - var _running = 0 - - var self = { - push: push, - drain: noop, - saturated: noop, - pause: pause, - paused: false, - concurrency: concurrency, - running: running, - resume: resume, - idle: idle, - length: length, - unshift: unshift, - empty: noop, - kill: kill, - killAndDrain: killAndDrain - } - - return self - - function running () { - return _running - } - - function pause () { - self.paused = true - } - - function length () { - var current = queueHead - var counter = 0 - - while (current) { - current = current.next - counter++ - } - - return counter - } - - function resume () { - if (!self.paused) return - self.paused = false - for (var i = 0; i < self.concurrency; i++) { - _running++ - release() - } - } - - function idle () { - return _running === 0 && self.length() === 0 - } - - function push (value, done) { - var current = cache.get() - - current.context = context - current.release = release - current.value = value - current.callback = done || noop - - if (_running === self.concurrency || self.paused) { - if (queueTail) { - queueTail.next = current - queueTail = current - } else { - queueHead = current - queueTail = current - self.saturated() - } - } else { - _running++ - worker.call(context, current.value, current.worked) - } - } - - function unshift (value, done) { - var current = cache.get() - - current.context = context - current.release = release - current.value = value - current.callback = done || noop - - if (_running === self.concurrency || self.paused) { - if (queueHead) { - current.next = queueHead - queueHead = current - } else { - queueHead = current - queueTail = current - self.saturated() - } - } else { - _running++ - worker.call(context, current.value, current.worked) - } - } - - function release (holder) { - if (holder) { - cache.release(holder) - } - var next = queueHead - if (next) { - if (!self.paused) { - if (queueTail === queueHead) { - queueTail = null - } - queueHead = next.next - next.next = null - worker.call(context, next.value, next.worked) - if (queueTail === null) { - self.empty() - } - } else { - _running-- - } - } else if (--_running === 0) { - self.drain() - } - } - - function kill () { - queueHead = null - queueTail = null - self.drain = noop - } - - function killAndDrain () { - queueHead = null - queueTail = null - self.drain() - self.drain = noop - } -} - -function noop () {} - -function Task () { - this.value = null - this.callback = noop - this.next = null - this.release = noop - this.context = null - - var self = this - - this.worked = function worked (err, result) { - var callback = self.callback - self.value = null - self.callback = noop - callback.call(self.context, err, result) - self.release(self) - } -} - -module.exports = fastqueue - - -/***/ }), -/* 342 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -function reusify (Constructor) { - var head = new Constructor() - var tail = head - - function get () { - var current = head - - if (current.next) { - head = current.next - } else { - head = new Constructor() - tail = head - } - - current.next = null - - return current - } - - function release (obj) { - tail.next = obj - tail = obj - } - - return { - get: get, - release: release - } -} - -module.exports = reusify - - -/***/ }), -/* 343 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function isFatalError(settings, error) { - if (settings.errorFilter === null) { - return true; - } - return !settings.errorFilter(error); -} -exports.isFatalError = isFatalError; -function isAppliedFilter(filter, value) { - return filter === null || filter(value); -} -exports.isAppliedFilter = isAppliedFilter; -function replacePathSegmentSeparator(filepath, separator) { - return filepath.split(/[\\/]/).join(separator); -} -exports.replacePathSegmentSeparator = replacePathSegmentSeparator; -function joinPathSegments(a, b, separator) { - if (a === '') { - return b; - } - return a + separator + b; -} -exports.joinPathSegments = joinPathSegments; - - -/***/ }), -/* 344 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(343); -class Reader { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._root = common.replacePathSegmentSeparator(_root, _settings.pathSegmentSeparator); - } -} -exports.default = Reader; - - -/***/ }), -/* 345 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const async_1 = __webpack_require__(331); -class StreamProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new async_1.default(this._root, this._settings); - this._stream = new stream_1.Readable({ - objectMode: true, - read: () => { }, - destroy: this._reader.destroy.bind(this._reader) - }); - } - read() { - this._reader.onError((error) => { - this._stream.emit('error', error); - }); - this._reader.onEntry((entry) => { - this._stream.push(entry); - }); - this._reader.onEnd(() => { - this._stream.push(null); - }); - this._reader.read(); - return this._stream; - } -} -exports.default = StreamProvider; - - -/***/ }), -/* 346 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(347); -class SyncProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new sync_1.default(this._root, this._settings); - } - read() { - return this._reader.read(); - } -} -exports.default = SyncProvider; - - -/***/ }), -/* 347 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(332); -const common = __webpack_require__(343); -const reader_1 = __webpack_require__(344); -class SyncReader extends reader_1.default { - constructor() { - super(...arguments); - this._scandir = fsScandir.scandirSync; - this._storage = new Set(); - this._queue = new Set(); - } - read() { - this._pushToQueue(this._root, this._settings.basePath); - this._handleQueue(); - return [...this._storage]; - } - _pushToQueue(directory, base) { - this._queue.add({ directory, base }); - } - _handleQueue() { - for (const item of this._queue.values()) { - this._handleDirectory(item.directory, item.base); - } - } - _handleDirectory(directory, base) { - try { - const entries = this._scandir(directory, this._settings.fsScandirSettings); - for (const entry of entries) { - this._handleEntry(entry, base); - } - } - catch (error) { - this._handleError(error); - } - } - _handleError(error) { - if (!common.isFatalError(this._settings, error)) { - return; - } - throw error; - } - _handleEntry(entry, base) { - const fullpath = entry.path; - if (base !== undefined) { - entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); - } - if (common.isAppliedFilter(this._settings.entryFilter, entry)) { - this._pushToStorage(entry); - } - if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { - this._pushToQueue(fullpath, entry.path); - } - } - _pushToStorage(entry) { - this._storage.add(entry); - } -} -exports.default = SyncReader; - - -/***/ }), -/* 348 */ -/***/ (function(module, exports, __webpack_require__) { +/***/ }), +/* 318 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsScandir = __webpack_require__(332); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.basePath = this._getValue(this._options.basePath, undefined); - this.concurrency = this._getValue(this._options.concurrency, Infinity); - this.deepFilter = this._getValue(this._options.deepFilter, null); - this.entryFilter = this._getValue(this._options.entryFilter, null); - this.errorFilter = this._getValue(this._options.errorFilter, null); - this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); - this.fsScandirSettings = new fsScandir.Settings({ - followSymbolicLinks: this._options.followSymbolicLinks, - fs: this._options.fs, - pathSegmentSeparator: this._options.pathSegmentSeparator, - stats: this._options.stats, - throwErrorOnBrokenSymbolicLink: this._options.throwErrorOnBrokenSymbolicLink - }); - } - _getValue(option, value) { - return option === undefined ? value : option; - } +function isString(input) { + return typeof input === 'string'; } -exports.default = Settings; - - -/***/ }), -/* 349 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(324); -const utils = __webpack_require__(294); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } +exports.isString = isString; +function isEmpty(input) { + return input === ''; } -exports.default = Reader; +exports.isEmpty = isEmpty; /***/ }), -/* 350 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const deep_1 = __webpack_require__(351); -const entry_1 = __webpack_require__(354); -const error_1 = __webpack_require__(355); -const entry_2 = __webpack_require__(356); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); +const stream_1 = __webpack_require__(320); +const provider_1 = __webpack_require__(347); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); } } -exports.default = Provider; +exports.default = ProviderAsync; /***/ }), -/* 351 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -const partial_1 = __webpack_require__(352); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - const depth = this._getEntryLevel(basePath, entry.path); - if (this._isSkippedByDeep(depth)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(entryDepth) { - return entryDepth >= this._settings.deep; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _getEntryLevel(basePath, entryPath) { - const basePathDepth = basePath.split('/').length; - const entryPathDepth = entryPath.split('/').length; - return entryPathDepth - (basePath === '' ? 0 : basePathDepth); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); +const stream_1 = __webpack_require__(137); +const fsStat = __webpack_require__(321); +const fsWalk = __webpack_require__(326); +const reader_1 = __webpack_require__(346); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; } - _isSkippedByNegativePatterns(entryPath, negativeRe) { - return !utils.pattern.matchAny(entryPath, negativeRe); + dynamic(root, options) { + return this._walkStream(root, options); } -} -exports.default = DeepFilter; - - -/***/ }), -/* 352 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(353); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); } - if (!segment.dynamic && segment.pattern === part) { - return true; + if (index === filepaths.length - 1) { + stream.end(); } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; - - -/***/ }), -/* 353 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); } + return stream; } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; + throw error; }); } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); } } -exports.default = Matcher; +exports.default = ReaderStream; /***/ }), -/* 354 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique) { - if (this._isDuplicateEntry(entry)) { - return false; - } - this._createIndexRecord(entry); - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - return this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entry, negativeRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entry.path); - return this._isMatchToPatterns(fullpath, negativeRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); +const async = __webpack_require__(322); +const sync = __webpack_require__(323); +const settings_1 = __webpack_require__(324); +exports.Settings = settings_1.default; +function stat(path, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return async.read(path, getSettings(), optionsOrSettingsOrCallback); } + async.read(path, getSettings(optionsOrSettingsOrCallback), callback); } -exports.default = EntryFilter; - - -/***/ }), -/* 355 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; +exports.stat = stat; +function statSync(path, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + return sync.read(path, settings); +} +exports.statSync = statSync; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; } + return new settings_1.default(settingsOrOptions); } -exports.default = ErrorFilter; /***/ }), -/* 356 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(294); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; +function read(path, settings, callback) { + settings.fs.lstat(path, (lstatError, lstat) => { + if (lstatError !== null) { + return callFailureCallback(callback, lstatError); } - if (!this._settings.objectMode) { - return filepath; + if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { + return callSuccessCallback(callback, lstat); } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } + settings.fs.stat(path, (statError, stat) => { + if (statError !== null) { + if (settings.throwErrorOnBrokenSymbolicLink) { + return callFailureCallback(callback, statError); + } + return callSuccessCallback(callback, lstat); + } + if (settings.markSymbolicLink) { + stat.isSymbolicLink = () => true; + } + callSuccessCallback(callback, stat); + }); + }); } -exports.default = EntryTransformer; - - -/***/ }), -/* 357 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const stream_2 = __webpack_require__(323); -const provider_1 = __webpack_require__(350); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } +exports.read = read; +function callFailureCallback(callback, error) { + callback(error); } -exports.default = ProviderStream; - - -/***/ }), -/* 358 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(359); -const provider_1 = __webpack_require__(350); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } +function callSuccessCallback(callback, result) { + callback(null, result); } -exports.default = ProviderSync; /***/ }), -/* 359 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(324); -const fsWalk = __webpack_require__(329); -const reader_1 = __webpack_require__(349); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); +function read(path, settings) { + const lstat = settings.fs.lstatSync(path); + if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { + return lstat; } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); + try { + const stat = settings.fs.statSync(path); + if (settings.markSymbolicLink) { + stat.isSymbolicLink = () => true; } - return entries; + return stat; } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; + catch (error) { + if (!settings.throwErrorOnBrokenSymbolicLink) { + return lstat; } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); + throw error; } } -exports.default = ReaderSync; +exports.read = read; /***/ }), -/* 360 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); -const os = __webpack_require__(120); -const CPU_COUNT = os.cpus().length; -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; +const fs = __webpack_require__(325); class Settings { constructor(_options = {}) { this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } + this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true); + this.fs = fs.createFileSystemAdapter(this._options.fs); + this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); } _getValue(option, value) { return option === undefined ? value : option; } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } } exports.default = Settings; /***/ }), -/* 361 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const path = __webpack_require__(4); -const pathType = __webpack_require__(362); - -const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; - -const getPath = (filepath, cwd) => { - const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; - return path.isAbsolute(pth) ? pth : path.join(cwd, pth); -}; - -const addExtensions = (file, extensions) => { - if (path.extname(file)) { - return `**/${file}`; - } - - return `**/${file}.${getExtensions(extensions)}`; -}; - -const getGlob = (directory, options) => { - if (options.files && !Array.isArray(options.files)) { - throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); - } - - if (options.extensions && !Array.isArray(options.extensions)) { - throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); - } - - if (options.files && options.extensions) { - return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); - } - - if (options.files) { - return options.files.map(x => path.posix.join(directory, `**/${x}`)); - } - - if (options.extensions) { - return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; - } - - return [path.posix.join(directory, '**')]; -}; - -module.exports = async (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; - - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); - } - - const globs = await Promise.all([].concat(input).map(async x => { - const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); - return isDirectory ? getGlob(x, options) : x; - })); - - return [].concat.apply([], globs); // eslint-disable-line prefer-spread -}; - -module.exports.sync = (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; - - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); - } - - const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); - - return [].concat.apply([], globs); // eslint-disable-line prefer-spread -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(133); +exports.FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + stat: fs.stat, + lstatSync: fs.lstatSync, + statSync: fs.statSync +}; +function createFileSystemAdapter(fsMethods) { + if (fsMethods === undefined) { + return exports.FILE_SYSTEM_ADAPTER; + } + return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); +} +exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 362 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(133); - -async function isType(fsStatType, statsMethodName, filePath) { - if (typeof filePath !== 'string') { - throw new TypeError(`Expected a string, got ${typeof filePath}`); - } - - try { - const stats = await promisify(fs[fsStatType])(filePath); - return stats[statsMethodName](); - } catch (error) { - if (error.code === 'ENOENT') { - return false; - } - - throw error; - } -} - -function isTypeSync(fsStatType, statsMethodName, filePath) { - if (typeof filePath !== 'string') { - throw new TypeError(`Expected a string, got ${typeof filePath}`); - } - - try { - return fs[fsStatType](filePath)[statsMethodName](); - } catch (error) { - if (error.code === 'ENOENT') { - return false; - } - - throw error; - } -} - -exports.isFile = isType.bind(null, 'stat', 'isFile'); -exports.isDirectory = isType.bind(null, 'stat', 'isDirectory'); -exports.isSymlink = isType.bind(null, 'lstat', 'isSymbolicLink'); -exports.isFileSync = isTypeSync.bind(null, 'statSync', 'isFile'); -exports.isDirectorySync = isTypeSync.bind(null, 'statSync', 'isDirectory'); -exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); + +Object.defineProperty(exports, "__esModule", { value: true }); +const async_1 = __webpack_require__(327); +const stream_1 = __webpack_require__(342); +const sync_1 = __webpack_require__(343); +const settings_1 = __webpack_require__(345); +exports.Settings = settings_1.default; +function walk(directory, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return new async_1.default(directory, getSettings()).read(optionsOrSettingsOrCallback); + } + new async_1.default(directory, getSettings(optionsOrSettingsOrCallback)).read(callback); +} +exports.walk = walk; +function walkSync(directory, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + const provider = new sync_1.default(directory, settings); + return provider.read(); +} +exports.walkSync = walkSync; +function walkStream(directory, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + const provider = new stream_1.default(directory, settings); + return provider.read(); +} +exports.walkStream = walkStream; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; + } + return new settings_1.default(settingsOrOptions); +} /***/ }), -/* 363 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(133); -const path = __webpack_require__(4); -const fastGlob = __webpack_require__(292); -const gitIgnore = __webpack_require__(364); -const slash = __webpack_require__(365); - -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/flow-typed/**', - '**/coverage/**', - '**/.git' -]; - -const readFileP = promisify(fs.readFile); - -const mapGitIgnorePatternTo = base => ignore => { - if (ignore.startsWith('!')) { - return '!' + path.posix.join(base, ignore.slice(1)); - } - - return path.posix.join(base, ignore); -}; - -const parseGitIgnore = (content, options) => { - const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); - - return content - .split(/\r?\n/) - .filter(Boolean) - .filter(line => !line.startsWith('#')) - .map(mapGitIgnorePatternTo(base)); -}; - -const reduceIgnore = files => { - return files.reduce((ignores, file) => { - ignores.add(parseGitIgnore(file.content, { - cwd: file.cwd, - fileName: file.filePath - })); - return ignores; - }, gitIgnore()); -}; - -const ensureAbsolutePathForCwd = (cwd, p) => { - if (path.isAbsolute(p)) { - if (p.startsWith(cwd)) { - return p; - } - - throw new Error(`Path ${p} is not in cwd ${cwd}`); - } - - return path.join(cwd, p); -}; - -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); -}; - -const getFile = async (file, cwd) => { - const filePath = path.join(cwd, file); - const content = await readFileP(filePath, 'utf8'); - - return { - cwd, - filePath, - content - }; -}; - -const getFileSync = (file, cwd) => { - const filePath = path.join(cwd, file); - const content = fs.readFileSync(filePath, 'utf8'); - - return { - cwd, - filePath, - content - }; -}; - -const normalizeOptions = ({ - ignore = [], - cwd = slash(process.cwd()) -} = {}) => { - return {ignore, cwd}; -}; - -module.exports = async options => { - options = normalizeOptions(options); - - const paths = await fastGlob('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); - - const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); - const ignores = reduceIgnore(files); - - return getIsIgnoredPredecate(ignores, options.cwd); -}; - -module.exports.sync = options => { - options = normalizeOptions(options); - - const paths = fastGlob.sync('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); - - const files = paths.map(file => getFileSync(file, options.cwd)); - const ignores = reduceIgnore(files); - - return getIsIgnoredPredecate(ignores, options.cwd); -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async_1 = __webpack_require__(328); +class AsyncProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new async_1.default(this._root, this._settings); + this._storage = new Set(); + } + read(callback) { + this._reader.onError((error) => { + callFailureCallback(callback, error); + }); + this._reader.onEntry((entry) => { + this._storage.add(entry); + }); + this._reader.onEnd(() => { + callSuccessCallback(callback, [...this._storage]); + }); + this._reader.read(); + } +} +exports.default = AsyncProvider; +function callFailureCallback(callback, error) { + callback(error); +} +function callSuccessCallback(callback, entries) { + callback(null, entries); +} /***/ }), -/* 364 */ -/***/ (function(module, exports) { - -// A simple implementation of make-array -function makeArray (subject) { - return Array.isArray(subject) - ? subject - : [subject] -} - -const EMPTY = '' -const SPACE = ' ' -const ESCAPE = '\\' -const REGEX_TEST_BLANK_LINE = /^\s+$/ -const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/ -const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/ -const REGEX_SPLITALL_CRLF = /\r?\n/g -// /foo, -// ./foo, -// ../foo, -// . -// .. -const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/ - -const SLASH = '/' -const KEY_IGNORE = typeof Symbol !== 'undefined' - ? Symbol.for('node-ignore') - /* istanbul ignore next */ - : 'node-ignore' - -const define = (object, key, value) => - Object.defineProperty(object, key, {value}) - -const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g - -// Sanitize the range of a regular expression -// The cases are complicated, see test cases for details -const sanitizeRange = range => range.replace( - REGEX_REGEXP_RANGE, - (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) - ? match - // Invalid range (out of order) which is ok for gitignore rules but - // fatal for JavaScript regular expression, so eliminate it. - : EMPTY -) - -// See fixtures #59 -const cleanRangeBackSlash = slashes => { - const {length} = slashes - return slashes.slice(0, length - length % 2) -} - -// > If the pattern ends with a slash, -// > it is removed for the purpose of the following description, -// > but it would only find a match with a directory. -// > In other words, foo/ will match a directory foo and paths underneath it, -// > but will not match a regular file or a symbolic link foo -// > (this is consistent with the way how pathspec works in general in Git). -// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' -// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call -// you could use option `mark: true` with `glob` - -// '`foo/`' should not continue with the '`..`' -const REPLACERS = [ +/* 328 */ +/***/ (function(module, exports, __webpack_require__) { - // > Trailing spaces are ignored unless they are quoted with backslash ("\") - [ - // (a\ ) -> (a ) - // (a ) -> (a) - // (a \ ) -> (a ) - /\\?\s+$/, - match => match.indexOf('\\') === 0 - ? SPACE - : EMPTY - ], +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const events_1 = __webpack_require__(155); +const fsScandir = __webpack_require__(329); +const fastq = __webpack_require__(338); +const common = __webpack_require__(340); +const reader_1 = __webpack_require__(341); +class AsyncReader extends reader_1.default { + constructor(_root, _settings) { + super(_root, _settings); + this._settings = _settings; + this._scandir = fsScandir.scandir; + this._emitter = new events_1.EventEmitter(); + this._queue = fastq(this._worker.bind(this), this._settings.concurrency); + this._isFatalError = false; + this._isDestroyed = false; + this._queue.drain = () => { + if (!this._isFatalError) { + this._emitter.emit('end'); + } + }; + } + read() { + this._isFatalError = false; + this._isDestroyed = false; + setImmediate(() => { + this._pushToQueue(this._root, this._settings.basePath); + }); + return this._emitter; + } + destroy() { + if (this._isDestroyed) { + throw new Error('The reader is already destroyed'); + } + this._isDestroyed = true; + this._queue.killAndDrain(); + } + onEntry(callback) { + this._emitter.on('entry', callback); + } + onError(callback) { + this._emitter.once('error', callback); + } + onEnd(callback) { + this._emitter.once('end', callback); + } + _pushToQueue(directory, base) { + const queueItem = { directory, base }; + this._queue.push(queueItem, (error) => { + if (error !== null) { + this._handleError(error); + } + }); + } + _worker(item, done) { + this._scandir(item.directory, this._settings.fsScandirSettings, (error, entries) => { + if (error !== null) { + return done(error, undefined); + } + for (const entry of entries) { + this._handleEntry(entry, item.base); + } + done(null, undefined); + }); + } + _handleError(error) { + if (!common.isFatalError(this._settings, error)) { + return; + } + this._isFatalError = true; + this._isDestroyed = true; + this._emitter.emit('error', error); + } + _handleEntry(entry, base) { + if (this._isDestroyed || this._isFatalError) { + return; + } + const fullpath = entry.path; + if (base !== undefined) { + entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); + } + if (common.isAppliedFilter(this._settings.entryFilter, entry)) { + this._emitEntry(entry); + } + if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { + this._pushToQueue(fullpath, entry.path); + } + } + _emitEntry(entry) { + this._emitter.emit('entry', entry); + } +} +exports.default = AsyncReader; - // replace (\ ) with ' ' - [ - /\\\s/g, - () => SPACE - ], - // Escape metacharacters - // which is written down by users but means special for regular expressions. +/***/ }), +/* 329 */ +/***/ (function(module, exports, __webpack_require__) { - // > There are 12 characters with special meanings: - // > - the backslash \, - // > - the caret ^, - // > - the dollar sign $, - // > - the period or dot ., - // > - the vertical bar or pipe symbol |, - // > - the question mark ?, - // > - the asterisk or star *, - // > - the plus sign +, - // > - the opening parenthesis (, - // > - the closing parenthesis ), - // > - and the opening square bracket [, - // > - the opening curly brace {, - // > These special characters are often called "metacharacters". - [ - /[\\$.|*+(){^]/g, - match => `\\${match}` - ], +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async = __webpack_require__(330); +const sync = __webpack_require__(335); +const settings_1 = __webpack_require__(336); +exports.Settings = settings_1.default; +function scandir(path, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return async.read(path, getSettings(), optionsOrSettingsOrCallback); + } + async.read(path, getSettings(optionsOrSettingsOrCallback), callback); +} +exports.scandir = scandir; +function scandirSync(path, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + return sync.read(path, settings); +} +exports.scandirSync = scandirSync; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; + } + return new settings_1.default(settingsOrOptions); +} - [ - // > a question mark (?) matches a single character - /(?!\\)\?/g, - () => '[^/]' - ], - // leading slash - [ +/***/ }), +/* 330 */ +/***/ (function(module, exports, __webpack_require__) { - // > A leading slash matches the beginning of the pathname. - // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". - // A leading slash matches the beginning of the pathname - /^\//, - () => '^' - ], +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(321); +const rpl = __webpack_require__(331); +const constants_1 = __webpack_require__(332); +const utils = __webpack_require__(333); +function read(directory, settings, callback) { + if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { + return readdirWithFileTypes(directory, settings, callback); + } + return readdir(directory, settings, callback); +} +exports.read = read; +function readdirWithFileTypes(directory, settings, callback) { + settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => { + if (readdirError !== null) { + return callFailureCallback(callback, readdirError); + } + const entries = dirents.map((dirent) => ({ + dirent, + name: dirent.name, + path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` + })); + if (!settings.followSymbolicLinks) { + return callSuccessCallback(callback, entries); + } + const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings)); + rpl(tasks, (rplError, rplEntries) => { + if (rplError !== null) { + return callFailureCallback(callback, rplError); + } + callSuccessCallback(callback, rplEntries); + }); + }); +} +exports.readdirWithFileTypes = readdirWithFileTypes; +function makeRplTaskEntry(entry, settings) { + return (done) => { + if (!entry.dirent.isSymbolicLink()) { + return done(null, entry); + } + settings.fs.stat(entry.path, (statError, stats) => { + if (statError !== null) { + if (settings.throwErrorOnBrokenSymbolicLink) { + return done(statError); + } + return done(null, entry); + } + entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); + return done(null, entry); + }); + }; +} +function readdir(directory, settings, callback) { + settings.fs.readdir(directory, (readdirError, names) => { + if (readdirError !== null) { + return callFailureCallback(callback, readdirError); + } + const filepaths = names.map((name) => `${directory}${settings.pathSegmentSeparator}${name}`); + const tasks = filepaths.map((filepath) => { + return (done) => fsStat.stat(filepath, settings.fsStatSettings, done); + }); + rpl(tasks, (rplError, results) => { + if (rplError !== null) { + return callFailureCallback(callback, rplError); + } + const entries = []; + names.forEach((name, index) => { + const stats = results[index]; + const entry = { + name, + path: filepaths[index], + dirent: utils.fs.createDirentFromStats(name, stats) + }; + if (settings.stats) { + entry.stats = stats; + } + entries.push(entry); + }); + callSuccessCallback(callback, entries); + }); + }); +} +exports.readdir = readdir; +function callFailureCallback(callback, error) { + callback(error); +} +function callSuccessCallback(callback, result) { + callback(null, result); +} - // replace special metacharacter slash after the leading slash - [ - /\//g, - () => '\\/' - ], - [ - // > A leading "**" followed by a slash means match in all directories. - // > For example, "**/foo" matches file or directory "foo" anywhere, - // > the same as pattern "foo". - // > "**/foo/bar" matches file or directory "bar" anywhere that is directly - // > under directory "foo". - // Notice that the '*'s have been replaced as '\\*' - /^\^*\\\*\\\*\\\//, +/***/ }), +/* 331 */ +/***/ (function(module, exports) { - // '**/foo' <-> 'foo' - () => '^(?:.*\\/)?' - ], +module.exports = runParallel - // starting - [ - // there will be no leading '/' - // (which has been replaced by section "leading slash") - // If starts with '**', adding a '^' to the regular expression also works - /^(?=[^^])/, - function startingReplacer () { - // If has a slash `/` at the beginning or middle - return !/\/(?!$)/.test(this) - // > Prior to 2.22.1 - // > If the pattern does not contain a slash /, - // > Git treats it as a shell glob pattern - // Actually, if there is only a trailing slash, - // git also treats it as a shell glob pattern +function runParallel (tasks, cb) { + var results, pending, keys + var isSync = true - // After 2.22.1 (compatible but clearer) - // > If there is a separator at the beginning or middle (or both) - // > of the pattern, then the pattern is relative to the directory - // > level of the particular .gitignore file itself. - // > Otherwise the pattern may also match at any level below - // > the .gitignore level. - ? '(?:^|\\/)' + if (Array.isArray(tasks)) { + results = [] + pending = tasks.length + } else { + keys = Object.keys(tasks) + results = {} + pending = keys.length + } - // > Otherwise, Git treats the pattern as a shell glob suitable for - // > consumption by fnmatch(3) - : '^' + function done (err) { + function end () { + if (cb) cb(err, results) + cb = null } - ], - - // two globstars - [ - // Use lookahead assertions so that we could match more than one `'/**'` - /\\\/\\\*\\\*(?=\\\/|$)/g, - - // Zero, one or several directories - // should not use '*', or it will be replaced by the next replacer + if (isSync) process.nextTick(end) + else end() + } - // Check if it is not the last `'/**'` - (_, index, str) => index + 6 < str.length + function each (i, err, result) { + results[i] = result + if (--pending === 0 || err) { + done(err) + } + } - // case: /**/ - // > A slash followed by two consecutive asterisks then a slash matches - // > zero or more directories. - // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. - // '/**/' - ? '(?:\\/[^\\/]+)*' + if (!pending) { + // empty + done(null) + } else if (keys) { + // object + keys.forEach(function (key) { + tasks[key](function (err, result) { each(key, err, result) }) + }) + } else { + // array + tasks.forEach(function (task, i) { + task(function (err, result) { each(i, err, result) }) + }) + } - // case: /** - // > A trailing `"/**"` matches everything inside. + isSync = false +} - // #21: everything inside but it should not include the current folder - : '\\/.+' - ], - // intermediate wildcards - [ - // Never replace escaped '*' - // ignore rule '\*' will match the path '*' +/***/ }), +/* 332 */ +/***/ (function(module, exports, __webpack_require__) { - // 'abc.*/' -> go - // 'abc.*' -> skip this rule - /(^|[^\\]+)\\\*(?=.+)/g, +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.'); +const MAJOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[0], 10); +const MINOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[1], 10); +const SUPPORTED_MAJOR_VERSION = 10; +const SUPPORTED_MINOR_VERSION = 10; +const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION; +const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION; +/** + * IS `true` for Node.js 10.10 and greater. + */ +exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR; - // '*.js' matches '.js' - // '*.js' doesn't match 'abc' - (_, p1) => `${p1}[^\\/]*` - ], - [ - // unescape, revert step 3 except for back slash - // For example, if a user escape a '\\*', - // after step 3, the result will be '\\\\\\*' - /\\\\\\(?=[$.|*+(){^])/g, - () => ESCAPE - ], +/***/ }), +/* 333 */ +/***/ (function(module, exports, __webpack_require__) { - [ - // '\\\\' -> '\\' - /\\\\/g, - () => ESCAPE - ], +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(334); +exports.fs = fs; - [ - // > The range notation, e.g. [a-zA-Z], - // > can be used to match one of the characters in a range. - // `\` is escaped by step 3 - /(\\)?\[([^\]/]*?)(\\*)($|\])/g, - (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE - // '\\[bar]' -> '\\\\[bar\\]' - ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` - : close === ']' - ? endEscape.length % 2 === 0 - // A normal case, and it is a range notation - // '[bar]' - // '[bar\\\\]' - ? `[${sanitizeRange(range)}${endEscape}]` - // Invalid range notaton - // '[bar\\]' -> '[bar\\\\]' - : '[]' - : '[]' - ], +/***/ }), +/* 334 */ +/***/ (function(module, exports, __webpack_require__) { - // ending - [ - // 'js' will not match 'js.' - // 'ab' will not match 'abc' - /(?:[^*])$/, +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; - // WTF! - // https://git-scm.com/docs/gitignore - // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) - // which re-fixes #24, #38 - // > If there is a separator at the end of the pattern then the pattern - // > will only match directories, otherwise the pattern can match both - // > files and directories. +/***/ }), +/* 335 */ +/***/ (function(module, exports, __webpack_require__) { - // 'js*' will not match 'a.js' - // 'js/' will not match 'a.js' - // 'js' will match 'a.js' and 'a.js/' - match => /\/$/.test(match) - // foo/ will not match 'foo' - ? `${match}$` - // foo matches 'foo' and 'foo/' - : `${match}(?=$|\\/$)` - ], +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(321); +const constants_1 = __webpack_require__(332); +const utils = __webpack_require__(333); +function read(directory, settings) { + if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { + return readdirWithFileTypes(directory, settings); + } + return readdir(directory, settings); +} +exports.read = read; +function readdirWithFileTypes(directory, settings) { + const dirents = settings.fs.readdirSync(directory, { withFileTypes: true }); + return dirents.map((dirent) => { + const entry = { + dirent, + name: dirent.name, + path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` + }; + if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) { + try { + const stats = settings.fs.statSync(entry.path); + entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); + } + catch (error) { + if (settings.throwErrorOnBrokenSymbolicLink) { + throw error; + } + } + } + return entry; + }); +} +exports.readdirWithFileTypes = readdirWithFileTypes; +function readdir(directory, settings) { + const names = settings.fs.readdirSync(directory); + return names.map((name) => { + const entryPath = `${directory}${settings.pathSegmentSeparator}${name}`; + const stats = fsStat.statSync(entryPath, settings.fsStatSettings); + const entry = { + name, + path: entryPath, + dirent: utils.fs.createDirentFromStats(name, stats) + }; + if (settings.stats) { + entry.stats = stats; + } + return entry; + }); +} +exports.readdir = readdir; - // trailing wildcard - [ - /(\^|\\\/)?\\\*$/, - (_, p1) => { - const prefix = p1 - // '\^': - // '/*' does not match EMPTY - // '/*' does not match everything - // '\\\/': - // 'abc/*' does not match 'abc/' - ? `${p1}[^/]+` +/***/ }), +/* 336 */ +/***/ (function(module, exports, __webpack_require__) { - // 'a*' matches 'a' - // 'a*' matches 'aa' - : '[^/]*' +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(321); +const fs = __webpack_require__(337); +class Settings { + constructor(_options = {}) { + this._options = _options; + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false); + this.fs = fs.createFileSystemAdapter(this._options.fs); + this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); + this.stats = this._getValue(this._options.stats, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); + this.fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this.followSymbolicLinks, + fs: this.fs, + throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink + }); + } + _getValue(option, value) { + return option === undefined ? value : option; + } +} +exports.default = Settings; - return `${prefix}(?=$|\\/$)` - } - ], -] -// A simple cache, because an ignore rule only has only one certain meaning -const regexCache = Object.create(null) +/***/ }), +/* 337 */ +/***/ (function(module, exports, __webpack_require__) { -// @param {pattern} -const makeRegex = (pattern, negative, ignorecase) => { - const r = regexCache[pattern] - if (r) { - return r - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(133); +exports.FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + stat: fs.stat, + lstatSync: fs.lstatSync, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +function createFileSystemAdapter(fsMethods) { + if (fsMethods === undefined) { + return exports.FILE_SYSTEM_ADAPTER; + } + return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); +} +exports.createFileSystemAdapter = createFileSystemAdapter; - // const replacers = negative - // ? NEGATIVE_REPLACERS - // : POSITIVE_REPLACERS - const source = REPLACERS.reduce( - (prev, current) => prev.replace(current[0], current[1].bind(pattern)), - pattern - ) +/***/ }), +/* 338 */ +/***/ (function(module, exports, __webpack_require__) { - return regexCache[pattern] = ignorecase - ? new RegExp(source, 'i') - : new RegExp(source) -} +"use strict"; -const isString = subject => typeof subject === 'string' -// > A blank line matches no files, so it can serve as a separator for readability. -const checkPattern = pattern => pattern - && isString(pattern) - && !REGEX_TEST_BLANK_LINE.test(pattern) +var reusify = __webpack_require__(339) - // > A line starting with # serves as a comment. - && pattern.indexOf('#') !== 0 +function fastqueue (context, worker, concurrency) { + if (typeof context === 'function') { + concurrency = worker + worker = context + context = null + } -const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF) + var cache = reusify(Task) + var queueHead = null + var queueTail = null + var _running = 0 -class IgnoreRule { - constructor ( - origin, - pattern, - negative, - regex - ) { - this.origin = origin - this.pattern = pattern - this.negative = negative - this.regex = regex + var self = { + push: push, + drain: noop, + saturated: noop, + pause: pause, + paused: false, + concurrency: concurrency, + running: running, + resume: resume, + idle: idle, + length: length, + unshift: unshift, + empty: noop, + kill: kill, + killAndDrain: killAndDrain } -} -const createRule = (pattern, ignorecase) => { - const origin = pattern - let negative = false + return self - // > An optional prefix "!" which negates the pattern; - if (pattern.indexOf('!') === 0) { - negative = true - pattern = pattern.substr(1) + function running () { + return _running } - pattern = pattern - // > Put a backslash ("\") in front of the first "!" for patterns that - // > begin with a literal "!", for example, `"\!important!.txt"`. - .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') - // > Put a backslash ("\") in front of the first hash for patterns that - // > begin with a hash. - .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') - - const regex = makeRegex(pattern, negative, ignorecase) + function pause () { + self.paused = true + } - return new IgnoreRule( - origin, - pattern, - negative, - regex - ) -} + function length () { + var current = queueHead + var counter = 0 -const throwError = (message, Ctor) => { - throw new Ctor(message) -} + while (current) { + current = current.next + counter++ + } -const checkPath = (path, originalPath, doThrow) => { - if (!isString(path)) { - return doThrow( - `path must be a string, but got \`${originalPath}\``, - TypeError - ) + return counter } - // We don't know if we should ignore EMPTY, so throw - if (!path) { - return doThrow(`path must not be empty`, TypeError) + function resume () { + if (!self.paused) return + self.paused = false + for (var i = 0; i < self.concurrency; i++) { + _running++ + release() + } } - // Check if it is a relative path - if (checkPath.isNotRelative(path)) { - const r = '`path.relative()`d' - return doThrow( - `path should be a ${r} string, but got "${originalPath}"`, - RangeError - ) + function idle () { + return _running === 0 && self.length() === 0 } - return true -} - -const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path) + function push (value, done) { + var current = cache.get() -checkPath.isNotRelative = isNotRelative -checkPath.convert = p => p + current.context = context + current.release = release + current.value = value + current.callback = done || noop -class Ignore { - constructor ({ - ignorecase = true - } = {}) { - this._rules = [] - this._ignorecase = ignorecase - define(this, KEY_IGNORE, true) - this._initCache() + if (_running === self.concurrency || self.paused) { + if (queueTail) { + queueTail.next = current + queueTail = current + } else { + queueHead = current + queueTail = current + self.saturated() + } + } else { + _running++ + worker.call(context, current.value, current.worked) + } } - _initCache () { - this._ignoreCache = Object.create(null) - this._testCache = Object.create(null) - } + function unshift (value, done) { + var current = cache.get() - _addPattern (pattern) { - // #32 - if (pattern && pattern[KEY_IGNORE]) { - this._rules = this._rules.concat(pattern._rules) - this._added = true - return - } + current.context = context + current.release = release + current.value = value + current.callback = done || noop - if (checkPattern(pattern)) { - const rule = createRule(pattern, this._ignorecase) - this._added = true - this._rules.push(rule) + if (_running === self.concurrency || self.paused) { + if (queueHead) { + current.next = queueHead + queueHead = current + } else { + queueHead = current + queueTail = current + self.saturated() + } + } else { + _running++ + worker.call(context, current.value, current.worked) } } - // @param {Array | string | Ignore} pattern - add (pattern) { - this._added = false - - makeArray( - isString(pattern) - ? splitPattern(pattern) - : pattern - ).forEach(this._addPattern, this) - - // Some rules have just added to the ignore, - // making the behavior changed. - if (this._added) { - this._initCache() + function release (holder) { + if (holder) { + cache.release(holder) + } + var next = queueHead + if (next) { + if (!self.paused) { + if (queueTail === queueHead) { + queueTail = null + } + queueHead = next.next + next.next = null + worker.call(context, next.value, next.worked) + if (queueTail === null) { + self.empty() + } + } else { + _running-- + } + } else if (--_running === 0) { + self.drain() } - - return this } - // legacy - addPattern (pattern) { - return this.add(pattern) + function kill () { + queueHead = null + queueTail = null + self.drain = noop } - // | ignored : unignored - // negative | 0:0 | 0:1 | 1:0 | 1:1 - // -------- | ------- | ------- | ------- | -------- - // 0 | TEST | TEST | SKIP | X - // 1 | TESTIF | SKIP | TEST | X - - // - SKIP: always skip - // - TEST: always test - // - TESTIF: only test if checkUnignored - // - X: that never happen - - // @param {boolean} whether should check if the path is unignored, - // setting `checkUnignored` to `false` could reduce additional - // path matching. - - // @returns {TestResult} true if a file is ignored - _testOne (path, checkUnignored) { - let ignored = false - let unignored = false + function killAndDrain () { + queueHead = null + queueTail = null + self.drain() + self.drain = noop + } +} - this._rules.forEach(rule => { - const {negative} = rule - if ( - unignored === negative && ignored !== unignored - || negative && !ignored && !unignored && !checkUnignored - ) { - return - } +function noop () {} - const matched = rule.regex.test(path) +function Task () { + this.value = null + this.callback = noop + this.next = null + this.release = noop + this.context = null - if (matched) { - ignored = !negative - unignored = negative - } - }) + var self = this - return { - ignored, - unignored - } + this.worked = function worked (err, result) { + var callback = self.callback + self.value = null + self.callback = noop + callback.call(self.context, err, result) + self.release(self) } +} - // @returns {TestResult} - _test (originalPath, cache, checkUnignored, slices) { - const path = originalPath - // Supports nullable path - && checkPath.convert(originalPath) - - checkPath(path, originalPath, throwError) +module.exports = fastqueue - return this._t(path, cache, checkUnignored, slices) - } - _t (path, cache, checkUnignored, slices) { - if (path in cache) { - return cache[path] - } +/***/ }), +/* 339 */ +/***/ (function(module, exports, __webpack_require__) { - if (!slices) { - // path/to/a.js - // ['path', 'to', 'a.js'] - slices = path.split(SLASH) - } +"use strict"; - slices.pop() - // If the path has no parent directory, just test it - if (!slices.length) { - return cache[path] = this._testOne(path, checkUnignored) - } +function reusify (Constructor) { + var head = new Constructor() + var tail = head - const parent = this._t( - slices.join(SLASH) + SLASH, - cache, - checkUnignored, - slices - ) + function get () { + var current = head - // If the path contains a parent directory, check the parent first - return cache[path] = parent.ignored - // > It is not possible to re-include a file if a parent directory of - // > that file is excluded. - ? parent - : this._testOne(path, checkUnignored) - } + if (current.next) { + head = current.next + } else { + head = new Constructor() + tail = head + } - ignores (path) { - return this._test(path, this._ignoreCache, false).ignored - } + current.next = null - createFilter () { - return path => !this.ignores(path) + return current } - filter (paths) { - return makeArray(paths).filter(this.createFilter()) + function release (obj) { + tail.next = obj + tail = obj } - // @returns {TestResult} - test (path) { - return this._test(path, this._testCache, true) + return { + get: get, + release: release } } -const factory = options => new Ignore(options) +module.exports = reusify -const returnFalse = () => false -const isPathValid = path => - checkPath(path && checkPath.convert(path), path, returnFalse) +/***/ }), +/* 340 */ +/***/ (function(module, exports, __webpack_require__) { -factory.isPathValid = isPathValid +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function isFatalError(settings, error) { + if (settings.errorFilter === null) { + return true; + } + return !settings.errorFilter(error); +} +exports.isFatalError = isFatalError; +function isAppliedFilter(filter, value) { + return filter === null || filter(value); +} +exports.isAppliedFilter = isAppliedFilter; +function replacePathSegmentSeparator(filepath, separator) { + return filepath.split(/[\\/]/).join(separator); +} +exports.replacePathSegmentSeparator = replacePathSegmentSeparator; +function joinPathSegments(a, b, separator) { + if (a === '') { + return b; + } + return a + separator + b; +} +exports.joinPathSegments = joinPathSegments; -// Fixes typescript -factory.default = factory -module.exports = factory +/***/ }), +/* 341 */ +/***/ (function(module, exports, __webpack_require__) { -// Windows -// -------------------------------------------------------------- -/* istanbul ignore if */ -if ( - // Detect `process` so that it can run in browsers. - typeof process !== 'undefined' - && ( - process.env && process.env.IGNORE_TEST_WIN32 - || process.platform === 'win32' - ) -) { - /* eslint no-control-regex: "off" */ - const makePosix = str => /^\\\\\?\\/.test(str) - || /["<>|\u0000-\u001F]+/u.test(str) - ? str - : str.replace(/\\/g, '/') +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const common = __webpack_require__(340); +class Reader { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._root = common.replacePathSegmentSeparator(_root, _settings.pathSegmentSeparator); + } +} +exports.default = Reader; - checkPath.convert = makePosix - // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/' - // 'd:\\foo' - const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i - checkPath.isNotRelative = path => - REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) - || isNotRelative(path) -} +/***/ }), +/* 342 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(137); +const async_1 = __webpack_require__(328); +class StreamProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new async_1.default(this._root, this._settings); + this._stream = new stream_1.Readable({ + objectMode: true, + read: () => { }, + destroy: this._reader.destroy.bind(this._reader) + }); + } + read() { + this._reader.onError((error) => { + this._stream.emit('error', error); + }); + this._reader.onEntry((entry) => { + this._stream.push(entry); + }); + this._reader.onEnd(() => { + this._stream.push(null); + }); + this._reader.read(); + return this._stream; + } +} +exports.default = StreamProvider; /***/ }), -/* 365 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -module.exports = path => { - const isExtendedLengthPath = /^\\\\\?\\/.test(path); - const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex - - if (isExtendedLengthPath || hasNonAscii) { - return path; - } - - return path.replace(/\\/g, '/'); -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(344); +class SyncProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new sync_1.default(this._root, this._settings); + } + read() { + return this._reader.read(); + } +} +exports.default = SyncProvider; /***/ }), -/* 366 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const {Transform} = __webpack_require__(137); - -class ObjectTransform extends Transform { - constructor() { - super({ - objectMode: true - }); - } -} - -class FilterStream extends ObjectTransform { - constructor(filter) { - super(); - this._filter = filter; - } - - _transform(data, encoding, callback) { - if (this._filter(data)) { - this.push(data); - } - - callback(); - } -} - -class UniqueStream extends ObjectTransform { - constructor() { - super(); - this._pushed = new Set(); - } - - _transform(data, encoding, callback) { - if (!this._pushed.has(data)) { - this.push(data); - this._pushed.add(data); - } - - callback(); - } -} - -module.exports = { - FilterStream, - UniqueStream -}; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsScandir = __webpack_require__(329); +const common = __webpack_require__(340); +const reader_1 = __webpack_require__(341); +class SyncReader extends reader_1.default { + constructor() { + super(...arguments); + this._scandir = fsScandir.scandirSync; + this._storage = new Set(); + this._queue = new Set(); + } + read() { + this._pushToQueue(this._root, this._settings.basePath); + this._handleQueue(); + return [...this._storage]; + } + _pushToQueue(directory, base) { + this._queue.add({ directory, base }); + } + _handleQueue() { + for (const item of this._queue.values()) { + this._handleDirectory(item.directory, item.base); + } + } + _handleDirectory(directory, base) { + try { + const entries = this._scandir(directory, this._settings.fsScandirSettings); + for (const entry of entries) { + this._handleEntry(entry, base); + } + } + catch (error) { + this._handleError(error); + } + } + _handleError(error) { + if (!common.isFatalError(this._settings, error)) { + return; + } + throw error; + } + _handleEntry(entry, base) { + const fullpath = entry.path; + if (base !== undefined) { + entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); + } + if (common.isAppliedFilter(this._settings.entryFilter, entry)) { + this._pushToStorage(entry); + } + if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { + this._pushToQueue(fullpath, entry.path); + } + } + _pushToStorage(entry) { + this._storage.add(entry); + } +} +exports.default = SyncReader; /***/ }), -/* 367 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { -/*! - * is-glob - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - -var isExtglob = __webpack_require__(302); -var chars = { '{': '}', '(': ')', '[': ']'}; -var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; -var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - -module.exports = function isGlob(str, options) { - if (typeof str !== 'string' || str === '') { - return false; - } - - if (isExtglob(str)) { - return true; - } - - var regex = strictRegex; - var match; - - // optionally relax regex - if (options && options.strict === false) { - regex = relaxedRegex; - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsScandir = __webpack_require__(329); +class Settings { + constructor(_options = {}) { + this._options = _options; + this.basePath = this._getValue(this._options.basePath, undefined); + this.concurrency = this._getValue(this._options.concurrency, Infinity); + this.deepFilter = this._getValue(this._options.deepFilter, null); + this.entryFilter = this._getValue(this._options.entryFilter, null); + this.errorFilter = this._getValue(this._options.errorFilter, null); + this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); + this.fsScandirSettings = new fsScandir.Settings({ + followSymbolicLinks: this._options.followSymbolicLinks, + fs: this._options.fs, + pathSegmentSeparator: this._options.pathSegmentSeparator, + stats: this._options.stats, + throwErrorOnBrokenSymbolicLink: this._options.throwErrorOnBrokenSymbolicLink + }); + } + _getValue(option, value) { + return option === undefined ? value : option; + } +} +exports.default = Settings; - while ((match = regex.exec(str))) { - if (match[2]) return true; - var idx = match.index + match[0].length; - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - var open = match[1]; - var close = open ? chars[open] : null; - if (open && close) { - var n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } - } +/***/ }), +/* 346 */ +/***/ (function(module, exports, __webpack_require__) { - str = str.slice(idx); - } - return false; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(321); +const utils = __webpack_require__(291); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; /***/ }), -/* 368 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const deep_1 = __webpack_require__(348); +const entry_1 = __webpack_require__(351); +const error_1 = __webpack_require__(352); +const entry_2 = __webpack_require__(353); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; -const path = __webpack_require__(4); - -module.exports = path_ => { - let cwd = process.cwd(); - - path_ = path.resolve(path_); - if (process.platform === 'win32') { - cwd = cwd.toLowerCase(); - path_ = path_.toLowerCase(); - } +/***/ }), +/* 348 */ +/***/ (function(module, exports, __webpack_require__) { - return path_ === cwd; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +const partial_1 = __webpack_require__(349); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + const depth = this._getEntryLevel(basePath, entry.path); + if (this._isSkippedByDeep(depth)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(entryDepth) { + return entryDepth >= this._settings.deep; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _getEntryLevel(basePath, entryPath) { + const basePathDepth = basePath.split('/').length; + const entryPathDepth = entryPath.split('/').length; + return entryPathDepth - (basePath === '' ? 0 : basePathDepth); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, negativeRe) { + return !utils.pattern.matchAny(entryPath, negativeRe); + } +} +exports.default = DeepFilter; /***/ }), -/* 369 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__(350); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; -const path = __webpack_require__(4); -module.exports = (childPath, parentPath) => { - childPath = path.resolve(childPath); - parentPath = path.resolve(parentPath); +/***/ }), +/* 350 */ +/***/ (function(module, exports, __webpack_require__) { - if (process.platform === 'win32') { - childPath = childPath.toLowerCase(); - parentPath = parentPath.toLowerCase(); - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + } +} +exports.default = Matcher; - if (childPath === parentPath) { - return false; - } - childPath += path.sep; - parentPath += path.sep; +/***/ }), +/* 351 */ +/***/ (function(module, exports, __webpack_require__) { - return childPath.startsWith(parentPath); -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique) { + if (this._isDuplicateEntry(entry)) { + return false; + } + this._createIndexRecord(entry); + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + return this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entry, negativeRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils.path.makeAbsolute(this._settings.cwd, entry.path); + return this._isMatchToPatterns(fullpath, negativeRe); + } + _isMatchToPatterns(entryPath, patternsRe) { + const filepath = utils.path.removeLeadingDotSegment(entryPath); + return utils.pattern.matchAny(filepath, patternsRe); + } +} +exports.default = EntryFilter; /***/ }), -/* 370 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { -const assert = __webpack_require__(139) -const path = __webpack_require__(4) -const fs = __webpack_require__(133) -let glob = undefined -try { - glob = __webpack_require__(146) -} catch (_err) { - // treat glob as optional. -} - -const defaultGlobOpts = { - nosort: true, - silent: true -} - -// for EMFILE handling -let timeout = 0 - -const isWindows = (process.platform === "win32") - -const defaults = options => { - const methods = [ - 'unlink', - 'chmod', - 'stat', - 'lstat', - 'rmdir', - 'readdir' - ] - methods.forEach(m => { - options[m] = options[m] || fs[m] - m = m + 'Sync' - options[m] = options[m] || fs[m] - }) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} +exports.default = ErrorFilter; - options.maxBusyTries = options.maxBusyTries || 3 - options.emfileWait = options.emfileWait || 1000 - if (options.glob === false) { - options.disableGlob = true - } - if (options.disableGlob !== true && glob === undefined) { - throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') - } - options.disableGlob = options.disableGlob || false - options.glob = options.glob || defaultGlobOpts -} -const rimraf = (p, options, cb) => { - if (typeof options === 'function') { - cb = options - options = {} - } +/***/ }), +/* 353 */ +/***/ (function(module, exports, __webpack_require__) { - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert.equal(typeof cb, 'function', 'rimraf: callback function required') - assert(options, 'rimraf: invalid options argument provided') - assert.equal(typeof options, 'object', 'rimraf: options should be object') +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(291); +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} +exports.default = EntryTransformer; - defaults(options) - let busyTries = 0 - let errState = null - let n = 0 +/***/ }), +/* 354 */ +/***/ (function(module, exports, __webpack_require__) { - const next = (er) => { - errState = errState || er - if (--n === 0) - cb(errState) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(137); +const stream_2 = __webpack_require__(320); +const provider_1 = __webpack_require__(347); +class ProviderStream extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderStream; - const afterGlob = (er, results) => { - if (er) - return cb(er) - n = results.length - if (n === 0) - return cb() +/***/ }), +/* 355 */ +/***/ (function(module, exports, __webpack_require__) { - results.forEach(p => { - const CB = (er) => { - if (er) { - if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && - busyTries < options.maxBusyTries) { - busyTries ++ - // try again, with the same exact callback as this one. - return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(356); +const provider_1 = __webpack_require__(347); +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderSync; - // this one won't happen if graceful-fs is used. - if (er.code === "EMFILE" && timeout < options.emfileWait) { - return setTimeout(() => rimraf_(p, options, CB), timeout ++) - } - // already gone - if (er.code === "ENOENT") er = null - } +/***/ }), +/* 356 */ +/***/ (function(module, exports, __webpack_require__) { - timeout = 0 - next(er) - } - rimraf_(p, options, CB) - }) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(321); +const fsWalk = __webpack_require__(326); +const reader_1 = __webpack_require__(346); +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; + } + dynamic(root, options) { + return this._walkSync(root, options); + } + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; + } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} +exports.default = ReaderSync; - if (options.disableGlob || !glob.hasMagic(p)) - return afterGlob(null, [p]) - options.lstat(p, (er, stat) => { - if (!er) - return afterGlob(null, [p]) +/***/ }), +/* 357 */ +/***/ (function(module, exports, __webpack_require__) { - glob(p, options.glob, afterGlob) - }) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(133); +const os = __webpack_require__(120); +const CPU_COUNT = os.cpus().length; +exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; + } + if (this.stats) { + this.objectMode = true; + } + } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } +} +exports.default = Settings; -} -// Two possible strategies. -// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR -// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR -// -// Both result in an extra syscall when you guess wrong. However, there -// are likely far more normal files in the world than directories. This -// is based on the assumption that a the average number of files per -// directory is >= 1. -// -// If anyone ever complains about this, then I guess the strategy could -// be made configurable somehow. But until then, YAGNI. -const rimraf_ = (p, options, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +/***/ }), +/* 358 */ +/***/ (function(module, exports, __webpack_require__) { - // sunos lets the root user unlink directories, which is... weird. - // so we have to lstat here and make sure it's not a dir. - options.lstat(p, (er, st) => { - if (er && er.code === "ENOENT") - return cb(null) +"use strict"; - // Windows can EPERM on stat. Life is suffering. - if (er && er.code === "EPERM" && isWindows) - fixWinEPERM(p, options, er, cb) +const path = __webpack_require__(4); +const pathType = __webpack_require__(359); - if (st && st.isDirectory()) - return rmdir(p, options, er, cb) +const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; - options.unlink(p, er => { - if (er) { - if (er.code === "ENOENT") - return cb(null) - if (er.code === "EPERM") - return (isWindows) - ? fixWinEPERM(p, options, er, cb) - : rmdir(p, options, er, cb) - if (er.code === "EISDIR") - return rmdir(p, options, er, cb) - } - return cb(er) - }) - }) -} +const getPath = (filepath, cwd) => { + const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; + return path.isAbsolute(pth) ? pth : path.join(cwd, pth); +}; -const fixWinEPERM = (p, options, er, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +const addExtensions = (file, extensions) => { + if (path.extname(file)) { + return `**/${file}`; + } - options.chmod(p, 0o666, er2 => { - if (er2) - cb(er2.code === "ENOENT" ? null : er) - else - options.stat(p, (er3, stats) => { - if (er3) - cb(er3.code === "ENOENT" ? null : er) - else if (stats.isDirectory()) - rmdir(p, options, er, cb) - else - options.unlink(p, cb) - }) - }) -} + return `**/${file}.${getExtensions(extensions)}`; +}; -const fixWinEPERMSync = (p, options, er) => { - assert(p) - assert(options) +const getGlob = (directory, options) => { + if (options.files && !Array.isArray(options.files)) { + throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); + } - try { - options.chmodSync(p, 0o666) - } catch (er2) { - if (er2.code === "ENOENT") - return - else - throw er - } + if (options.extensions && !Array.isArray(options.extensions)) { + throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); + } - let stats - try { - stats = options.statSync(p) - } catch (er3) { - if (er3.code === "ENOENT") - return - else - throw er - } + if (options.files && options.extensions) { + return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); + } - if (stats.isDirectory()) - rmdirSync(p, options, er) - else - options.unlinkSync(p) -} + if (options.files) { + return options.files.map(x => path.posix.join(directory, `**/${x}`)); + } -const rmdir = (p, options, originalEr, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') + if (options.extensions) { + return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; + } - // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) - // if we guessed wrong, and it's not a directory, then - // raise the original error. - options.rmdir(p, er => { - if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) - rmkids(p, options, cb) - else if (er && er.code === "ENOTDIR") - cb(originalEr) - else - cb(er) - }) -} + return [path.posix.join(directory, '**')]; +}; -const rmkids = (p, options, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +module.exports = async (input, options) => { + options = { + cwd: process.cwd(), + ...options + }; - options.readdir(p, (er, files) => { - if (er) - return cb(er) - let n = files.length - if (n === 0) - return options.rmdir(p, cb) - let errState - files.forEach(f => { - rimraf(path.join(p, f), options, er => { - if (errState) - return - if (er) - return cb(errState = er) - if (--n === 0) - options.rmdir(p, cb) - }) - }) - }) -} + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); + } -// this looks simpler, and is strictly *faster*, but will -// tie up the JavaScript thread and fail on excessively -// deep directory trees. -const rimrafSync = (p, options) => { - options = options || {} - defaults(options) + const globs = await Promise.all([].concat(input).map(async x => { + const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); + return isDirectory ? getGlob(x, options) : x; + })); - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert(options, 'rimraf: missing options') - assert.equal(typeof options, 'object', 'rimraf: options should be object') + return [].concat.apply([], globs); // eslint-disable-line prefer-spread +}; - let results +module.exports.sync = (input, options) => { + options = { + cwd: process.cwd(), + ...options + }; - if (options.disableGlob || !glob.hasMagic(p)) { - results = [p] - } else { - try { - options.lstatSync(p) - results = [p] - } catch (er) { - results = glob.sync(p, options.glob) - } - } + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); + } - if (!results.length) - return + const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); - for (let i = 0; i < results.length; i++) { - const p = results[i] + return [].concat.apply([], globs); // eslint-disable-line prefer-spread +}; - let st - try { - st = options.lstatSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - // Windows can EPERM on stat. Life is suffering. - if (er.code === "EPERM" && isWindows) - fixWinEPERMSync(p, options, er) - } +/***/ }), +/* 359 */ +/***/ (function(module, exports, __webpack_require__) { - try { - // sunos lets the root user unlink directories, which is... weird. - if (st && st.isDirectory()) - rmdirSync(p, options, null) - else - options.unlinkSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "EPERM") - return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) - if (er.code !== "EISDIR") - throw er +"use strict"; - rmdirSync(p, options, er) - } - } -} +const {promisify} = __webpack_require__(111); +const fs = __webpack_require__(133); -const rmdirSync = (p, options, originalEr) => { - assert(p) - assert(options) +async function isType(fsStatType, statsMethodName, filePath) { + if (typeof filePath !== 'string') { + throw new TypeError(`Expected a string, got ${typeof filePath}`); + } - try { - options.rmdirSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "ENOTDIR") - throw originalEr - if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") - rmkidsSync(p, options) - } + try { + const stats = await promisify(fs[fsStatType])(filePath); + return stats[statsMethodName](); + } catch (error) { + if (error.code === 'ENOENT') { + return false; + } + + throw error; + } } -const rmkidsSync = (p, options) => { - assert(p) - assert(options) - options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) +function isTypeSync(fsStatType, statsMethodName, filePath) { + if (typeof filePath !== 'string') { + throw new TypeError(`Expected a string, got ${typeof filePath}`); + } - // We only end up here once we got ENOTEMPTY at least once, and - // at this point, we are guaranteed to have removed all the kids. - // So, we know that it won't be ENOENT or ENOTDIR or anything else. - // try really hard to delete stuff on windows, because it has a - // PROFOUNDLY annoying habit of not closing handles promptly when - // files are deleted, resulting in spurious ENOTEMPTY errors. - const retries = isWindows ? 100 : 1 - let i = 0 - do { - let threw = true - try { - const ret = options.rmdirSync(p, options) - threw = false - return ret - } finally { - if (++i < retries && threw) - continue - } - } while (true) + try { + return fs[fsStatType](filePath)[statsMethodName](); + } catch (error) { + if (error.code === 'ENOENT') { + return false; + } + + throw error; + } } -module.exports = rimraf -rimraf.sync = rimrafSync +exports.isFile = isType.bind(null, 'stat', 'isFile'); +exports.isDirectory = isType.bind(null, 'stat', 'isDirectory'); +exports.isSymlink = isType.bind(null, 'lstat', 'isSymbolicLink'); +exports.isFileSync = isTypeSync.bind(null, 'statSync', 'isFile'); +exports.isDirectorySync = isTypeSync.bind(null, 'statSync', 'isDirectory'); +exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 371 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(372); +const {promisify} = __webpack_require__(111); +const fs = __webpack_require__(133); +const path = __webpack_require__(4); +const fastGlob = __webpack_require__(289); +const gitIgnore = __webpack_require__(361); +const slash = __webpack_require__(362); -module.exports = async ( - iterable, - mapper, - { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/flow-typed/**', + '**/coverage/**', + '**/.git' +]; - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } +const readFileP = promisify(fs.readFile); - const ret = []; - const errors = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; +const mapGitIgnorePatternTo = base => ignore => { + if (ignore.startsWith('!')) { + return '!' + path.posix.join(base, ignore.slice(1)); + } - const next = () => { - if (isRejected) { - return; - } + return path.posix.join(base, ignore); +}; - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; +const parseGitIgnore = (content, options) => { + const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); - if (nextItem.done) { - isIterableDone = true; + return content + .split(/\r?\n/) + .filter(Boolean) + .filter(line => !line.startsWith('#')) + .map(mapGitIgnorePatternTo(base)); +}; - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { - resolve(ret); - } - } +const reduceIgnore = files => { + return files.reduce((ignores, file) => { + ignores.add(parseGitIgnore(file.content, { + cwd: file.cwd, + fileName: file.filePath + })); + return ignores; + }, gitIgnore()); +}; - return; - } +const ensureAbsolutePathForCwd = (cwd, p) => { + if (path.isAbsolute(p)) { + if (p.startsWith(cwd)) { + return p; + } - resolvingCount++; + throw new Error(`Path ${p} is not in cwd ${cwd}`); + } - (async () => { - try { - const element = await nextItem.value; - ret[i] = await mapper(element, i); - resolvingCount--; - next(); - } catch (error) { - if (stopOnError) { - isRejected = true; - reject(error); - } else { - errors.push(error); - resolvingCount--; - next(); - } - } - })(); - }; + return path.join(cwd, p); +}; - for (let i = 0; i < concurrency; i++) { - next(); +const getIsIgnoredPredecate = (ignores, cwd) => { + return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); +}; - if (isIterableDone) { - break; - } - } - }); +const getFile = async (file, cwd) => { + const filePath = path.join(cwd, file); + const content = await readFileP(filePath, 'utf8'); + + return { + cwd, + filePath, + content + }; }; +const getFileSync = (file, cwd) => { + const filePath = path.join(cwd, file); + const content = fs.readFileSync(filePath, 'utf8'); -/***/ }), -/* 372 */ -/***/ (function(module, exports, __webpack_require__) { + return { + cwd, + filePath, + content + }; +}; -"use strict"; +const normalizeOptions = ({ + ignore = [], + cwd = slash(process.cwd()) +} = {}) => { + return {ignore, cwd}; +}; -const indentString = __webpack_require__(373); -const cleanStack = __webpack_require__(374); +module.exports = async options => { + options = normalizeOptions(options); -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); + const paths = await fastGlob('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); -class AggregateError extends Error { - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } + const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); + const ignores = reduceIgnore(files); - errors = [...errors].map(error => { - if (error instanceof Error) { - return error; - } + return getIsIgnoredPredecate(ignores, options.cwd); +}; - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } +module.exports.sync = options => { + options = normalizeOptions(options); - return new Error(error); - }); + const paths = fastGlob.sync('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); + const files = paths.map(file => getFileSync(file, options.cwd)); + const ignores = reduceIgnore(files); - this.name = 'AggregateError'; + return getIsIgnoredPredecate(ignores, options.cwd); +}; - Object.defineProperty(this, '_errors', {value: errors}); - } - * [Symbol.iterator]() { - for (const error of this._errors) { - yield error; - } - } -} +/***/ }), +/* 361 */ +/***/ (function(module, exports) { -module.exports = AggregateError; +// A simple implementation of make-array +function makeArray (subject) { + return Array.isArray(subject) + ? subject + : [subject] +} +const EMPTY = '' +const SPACE = ' ' +const ESCAPE = '\\' +const REGEX_TEST_BLANK_LINE = /^\s+$/ +const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/ +const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/ +const REGEX_SPLITALL_CRLF = /\r?\n/g +// /foo, +// ./foo, +// ../foo, +// . +// .. +const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/ -/***/ }), -/* 373 */ -/***/ (function(module, exports, __webpack_require__) { +const SLASH = '/' +const KEY_IGNORE = typeof Symbol !== 'undefined' + ? Symbol.for('node-ignore') + /* istanbul ignore next */ + : 'node-ignore' -"use strict"; +const define = (object, key, value) => + Object.defineProperty(object, key, {value}) +const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g -module.exports = (string, count = 1, options) => { - options = { - indent: ' ', - includeEmptyLines: false, - ...options - }; +// Sanitize the range of a regular expression +// The cases are complicated, see test cases for details +const sanitizeRange = range => range.replace( + REGEX_REGEXP_RANGE, + (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) + ? match + // Invalid range (out of order) which is ok for gitignore rules but + // fatal for JavaScript regular expression, so eliminate it. + : EMPTY +) - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } +// See fixtures #59 +const cleanRangeBackSlash = slashes => { + const {length} = slashes + return slashes.slice(0, length - length % 2) +} - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } +// > If the pattern ends with a slash, +// > it is removed for the purpose of the following description, +// > but it would only find a match with a directory. +// > In other words, foo/ will match a directory foo and paths underneath it, +// > but will not match a regular file or a symbolic link foo +// > (this is consistent with the way how pathspec works in general in Git). +// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' +// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call +// you could use option `mark: true` with `glob` - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } +// '`foo/`' should not continue with the '`..`' +const REPLACERS = [ - if (count === 0) { - return string; - } + // > Trailing spaces are ignored unless they are quoted with backslash ("\") + [ + // (a\ ) -> (a ) + // (a ) -> (a) + // (a \ ) -> (a ) + /\\?\s+$/, + match => match.indexOf('\\') === 0 + ? SPACE + : EMPTY + ], - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; + // replace (\ ) with ' ' + [ + /\\\s/g, + () => SPACE + ], - return string.replace(regex, options.indent.repeat(count)); -}; + // Escape metacharacters + // which is written down by users but means special for regular expressions. + // > There are 12 characters with special meanings: + // > - the backslash \, + // > - the caret ^, + // > - the dollar sign $, + // > - the period or dot ., + // > - the vertical bar or pipe symbol |, + // > - the question mark ?, + // > - the asterisk or star *, + // > - the plus sign +, + // > - the opening parenthesis (, + // > - the closing parenthesis ), + // > - and the opening square bracket [, + // > - the opening curly brace {, + // > These special characters are often called "metacharacters". + [ + /[\\$.|*+(){^]/g, + match => `\\${match}` + ], -/***/ }), -/* 374 */ -/***/ (function(module, exports, __webpack_require__) { + [ + // > a question mark (?) matches a single character + /(?!\\)\?/g, + () => '[^/]' + ], -"use strict"; + // leading slash + [ -const os = __webpack_require__(120); + // > A leading slash matches the beginning of the pathname. + // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". + // A leading slash matches the beginning of the pathname + /^\//, + () => '^' + ], -const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; -const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; -const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir(); + // replace special metacharacter slash after the leading slash + [ + /\//g, + () => '\\/' + ], -module.exports = (stack, options) => { - options = Object.assign({pretty: false}, options); + [ + // > A leading "**" followed by a slash means match in all directories. + // > For example, "**/foo" matches file or directory "foo" anywhere, + // > the same as pattern "foo". + // > "**/foo/bar" matches file or directory "bar" anywhere that is directly + // > under directory "foo". + // Notice that the '*'s have been replaced as '\\*' + /^\^*\\\*\\\*\\\//, - return stack.replace(/\\/g, '/') - .split('\n') - .filter(line => { - const pathMatches = line.match(extractPathRegex); - if (pathMatches === null || !pathMatches[1]) { - return true; - } + // '**/foo' <-> 'foo' + () => '^(?:.*\\/)?' + ], - const match = pathMatches[1]; + // starting + [ + // there will be no leading '/' + // (which has been replaced by section "leading slash") + // If starts with '**', adding a '^' to the regular expression also works + /^(?=[^^])/, + function startingReplacer () { + // If has a slash `/` at the beginning or middle + return !/\/(?!$)/.test(this) + // > Prior to 2.22.1 + // > If the pattern does not contain a slash /, + // > Git treats it as a shell glob pattern + // Actually, if there is only a trailing slash, + // git also treats it as a shell glob pattern - // Electron - if ( - match.includes('.app/Contents/Resources/electron.asar') || - match.includes('.app/Contents/Resources/default_app.asar') - ) { - return false; - } + // After 2.22.1 (compatible but clearer) + // > If there is a separator at the beginning or middle (or both) + // > of the pattern, then the pattern is relative to the directory + // > level of the particular .gitignore file itself. + // > Otherwise the pattern may also match at any level below + // > the .gitignore level. + ? '(?:^|\\/)' - return !pathRegex.test(match); - }) - .filter(line => line.trim() !== '') - .map(line => { - if (options.pretty) { - return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); - } + // > Otherwise, Git treats the pattern as a shell glob suitable for + // > consumption by fnmatch(3) + : '^' + } + ], - return line; - }) - .join('\n'); -}; + // two globstars + [ + // Use lookahead assertions so that we could match more than one `'/**'` + /\\\/\\\*\\\*(?=\\\/|$)/g, + // Zero, one or several directories + // should not use '*', or it will be replaced by the next replacer -/***/ }), -/* 375 */ -/***/ (function(module, exports, __webpack_require__) { + // Check if it is not the last `'/**'` + (_, index, str) => index + 6 < str.length -"use strict"; + // case: /**/ + // > A slash followed by two consecutive asterisks then a slash matches + // > zero or more directories. + // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. + // '/**/' + ? '(?:\\/[^\\/]+)*' -const chalk = __webpack_require__(376); -const cliCursor = __webpack_require__(385); -const cliSpinners = __webpack_require__(389); -const logSymbols = __webpack_require__(391); + // case: /** + // > A trailing `"/**"` matches everything inside. -class Ora { - constructor(options) { - if (typeof options === 'string') { - options = { - text: options - }; - } + // #21: everything inside but it should not include the current folder + : '\\/.+' + ], - this.options = Object.assign({ - text: '', - color: 'cyan', - stream: process.stderr - }, options); + // intermediate wildcards + [ + // Never replace escaped '*' + // ignore rule '\*' will match the path '*' - const sp = this.options.spinner; - this.spinner = typeof sp === 'object' ? sp : (process.platform === 'win32' ? cliSpinners.line : (cliSpinners[sp] || cliSpinners.dots)); // eslint-disable-line no-nested-ternary + // 'abc.*/' -> go + // 'abc.*' -> skip this rule + /(^|[^\\]+)\\\*(?=.+)/g, - if (this.spinner.frames === undefined) { - throw new Error('Spinner must define `frames`'); - } + // '*.js' matches '.js' + // '*.js' doesn't match 'abc' + (_, p1) => `${p1}[^\\/]*` + ], - this.text = this.options.text; - this.color = this.options.color; - this.interval = this.options.interval || this.spinner.interval || 100; - this.stream = this.options.stream; - this.id = null; - this.frameIndex = 0; - this.enabled = typeof this.options.enabled === 'boolean' ? this.options.enabled : ((this.stream && this.stream.isTTY) && !process.env.CI); - } - frame() { - const frames = this.spinner.frames; - let frame = frames[this.frameIndex]; + [ + // unescape, revert step 3 except for back slash + // For example, if a user escape a '\\*', + // after step 3, the result will be '\\\\\\*' + /\\\\\\(?=[$.|*+(){^])/g, + () => ESCAPE + ], - if (this.color) { - frame = chalk[this.color](frame); - } + [ + // '\\\\' -> '\\' + /\\\\/g, + () => ESCAPE + ], - this.frameIndex = ++this.frameIndex % frames.length; + [ + // > The range notation, e.g. [a-zA-Z], + // > can be used to match one of the characters in a range. - return frame + ' ' + this.text; - } - clear() { - if (!this.enabled) { - return this; - } + // `\` is escaped by step 3 + /(\\)?\[([^\]/]*?)(\\*)($|\])/g, + (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE + // '\\[bar]' -> '\\\\[bar\\]' + ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` + : close === ']' + ? endEscape.length % 2 === 0 + // A normal case, and it is a range notation + // '[bar]' + // '[bar\\\\]' + ? `[${sanitizeRange(range)}${endEscape}]` + // Invalid range notaton + // '[bar\\]' -> '[bar\\\\]' + : '[]' + : '[]' + ], - this.stream.clearLine(); - this.stream.cursorTo(0); + // ending + [ + // 'js' will not match 'js.' + // 'ab' will not match 'abc' + /(?:[^*])$/, - return this; - } - render() { - this.clear(); - this.stream.write(this.frame()); + // WTF! + // https://git-scm.com/docs/gitignore + // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) + // which re-fixes #24, #38 - return this; - } - start(text) { - if (text) { - this.text = text; - } + // > If there is a separator at the end of the pattern then the pattern + // > will only match directories, otherwise the pattern can match both + // > files and directories. - if (!this.enabled || this.id) { - return this; - } + // 'js*' will not match 'a.js' + // 'js/' will not match 'a.js' + // 'js' will match 'a.js' and 'a.js/' + match => /\/$/.test(match) + // foo/ will not match 'foo' + ? `${match}$` + // foo matches 'foo' and 'foo/' + : `${match}(?=$|\\/$)` + ], - cliCursor.hide(this.stream); - this.render(); - this.id = setInterval(this.render.bind(this), this.interval); + // trailing wildcard + [ + /(\^|\\\/)?\\\*$/, + (_, p1) => { + const prefix = p1 + // '\^': + // '/*' does not match EMPTY + // '/*' does not match everything - return this; - } - stop() { - if (!this.enabled) { - return this; - } + // '\\\/': + // 'abc/*' does not match 'abc/' + ? `${p1}[^/]+` - clearInterval(this.id); - this.id = null; - this.frameIndex = 0; - this.clear(); - cliCursor.show(this.stream); + // 'a*' matches 'a' + // 'a*' matches 'aa' + : '[^/]*' - return this; - } - succeed(text) { - return this.stopAndPersist({symbol: logSymbols.success, text}); - } - fail(text) { - return this.stopAndPersist({symbol: logSymbols.error, text}); - } - warn(text) { - return this.stopAndPersist({symbol: logSymbols.warning, text}); - } - info(text) { - return this.stopAndPersist({symbol: logSymbols.info, text}); - } - stopAndPersist(options) { - if (!this.enabled) { - return this; - } + return `${prefix}(?=$|\\/$)` + } + ], +] - // Legacy argument - // TODO: Deprecate sometime in the future - if (typeof options === 'string') { - options = { - symbol: options - }; - } +// A simple cache, because an ignore rule only has only one certain meaning +const regexCache = Object.create(null) - options = options || {}; +// @param {pattern} +const makeRegex = (pattern, negative, ignorecase) => { + const r = regexCache[pattern] + if (r) { + return r + } - this.stop(); - this.stream.write(`${options.symbol || ' '} ${options.text || this.text}\n`); + // const replacers = negative + // ? NEGATIVE_REPLACERS + // : POSITIVE_REPLACERS - return this; - } -} + const source = REPLACERS.reduce( + (prev, current) => prev.replace(current[0], current[1].bind(pattern)), + pattern + ) -module.exports = function (opts) { - return new Ora(opts); -}; + return regexCache[pattern] = ignorecase + ? new RegExp(source, 'i') + : new RegExp(source) +} -module.exports.promise = (action, options) => { - if (typeof action.then !== 'function') { - throw new TypeError('Parameter `action` must be a Promise'); - } +const isString = subject => typeof subject === 'string' - const spinner = new Ora(options); - spinner.start(); +// > A blank line matches no files, so it can serve as a separator for readability. +const checkPattern = pattern => pattern + && isString(pattern) + && !REGEX_TEST_BLANK_LINE.test(pattern) - action.then( - () => { - spinner.succeed(); - }, - () => { - spinner.fail(); - } - ); + // > A line starting with # serves as a comment. + && pattern.indexOf('#') !== 0 - return spinner; -}; +const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF) +class IgnoreRule { + constructor ( + origin, + pattern, + negative, + regex + ) { + this.origin = origin + this.pattern = pattern + this.negative = negative + this.regex = regex + } +} -/***/ }), -/* 376 */ -/***/ (function(module, exports, __webpack_require__) { +const createRule = (pattern, ignorecase) => { + const origin = pattern + let negative = false -"use strict"; + // > An optional prefix "!" which negates the pattern; + if (pattern.indexOf('!') === 0) { + negative = true + pattern = pattern.substr(1) + } -const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(377); -const stdoutColor = __webpack_require__(382).stdout; + pattern = pattern + // > Put a backslash ("\") in front of the first "!" for patterns that + // > begin with a literal "!", for example, `"\!important!.txt"`. + .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') + // > Put a backslash ("\") in front of the first hash for patterns that + // > begin with a hash. + .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') -const template = __webpack_require__(384); + const regex = makeRegex(pattern, negative, ignorecase) -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); + return new IgnoreRule( + origin, + pattern, + negative, + regex + ) +} -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; +const throwError = (message, Ctor) => { + throw new Ctor(message) +} -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); +const checkPath = (path, originalPath, doThrow) => { + if (!isString(path)) { + return doThrow( + `path must be a string, but got \`${originalPath}\``, + TypeError + ) + } -const styles = Object.create(null); + // We don't know if we should ignore EMPTY, so throw + if (!path) { + return doThrow(`path must not be empty`, TypeError) + } -function applyOptions(obj, options) { - options = options || {}; + // Check if it is a relative path + if (checkPath.isNotRelative(path)) { + const r = '`path.relative()`d' + return doThrow( + `path should be a ${r} string, but got "${originalPath}"`, + RangeError + ) + } - // Detect level if not set manually - const scLevel = stdoutColor ? stdoutColor.level : 0; - obj.level = options.level === undefined ? scLevel : options.level; - obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; + return true } -function Chalk(options) { - // We check for this.template here since calling `chalk.constructor()` - // by itself will have a `this` of a previously constructed chalk object - if (!this || !(this instanceof Chalk) || this.template) { - const chalk = {}; - applyOptions(chalk, options); - - chalk.template = function () { - const args = [].slice.call(arguments); - return chalkTag.apply(null, [chalk.template].concat(args)); - }; +const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path) - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); +checkPath.isNotRelative = isNotRelative +checkPath.convert = p => p - chalk.template.constructor = Chalk; +class Ignore { + constructor ({ + ignorecase = true + } = {}) { + this._rules = [] + this._ignorecase = ignorecase + define(this, KEY_IGNORE, true) + this._initCache() + } - return chalk.template; - } + _initCache () { + this._ignoreCache = Object.create(null) + this._testCache = Object.create(null) + } - applyOptions(this, options); -} + _addPattern (pattern) { + // #32 + if (pattern && pattern[KEY_IGNORE]) { + this._rules = this._rules.concat(pattern._rules) + this._added = true + return + } -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; -} + if (checkPattern(pattern)) { + const rule = createRule(pattern, this._ignorecase) + this._added = true + this._rules.push(rule) + } + } -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); + // @param {Array | string | Ignore} pattern + add (pattern) { + this._added = false - styles[key] = { - get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); - } - }; -} + makeArray( + isString(pattern) + ? splitPattern(pattern) + : pattern + ).forEach(this._addPattern, this) -styles.visible = { - get() { - return build.call(this, this._styles || [], true, 'visible'); - } -}; + // Some rules have just added to the ignore, + // making the behavior changed. + if (this._added) { + this._initCache() + } -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } + return this + } - styles[model] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.color.close, - closeRe: ansiStyles.color.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} + // legacy + addPattern (pattern) { + return this.add(pattern) + } -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } + // | ignored : unignored + // negative | 0:0 | 0:1 | 1:0 | 1:1 + // -------- | ------- | ------- | ------- | -------- + // 0 | TEST | TEST | SKIP | X + // 1 | TESTIF | SKIP | TEST | X - const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); - styles[bgModel] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.bgColor.close, - closeRe: ansiStyles.bgColor.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} + // - SKIP: always skip + // - TEST: always test + // - TESTIF: only test if checkUnignored + // - X: that never happen -const proto = Object.defineProperties(() => {}, styles); + // @param {boolean} whether should check if the path is unignored, + // setting `checkUnignored` to `false` could reduce additional + // path matching. -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; + // @returns {TestResult} true if a file is ignored + _testOne (path, checkUnignored) { + let ignored = false + let unignored = false - builder._styles = _styles; - builder._empty = _empty; + this._rules.forEach(rule => { + const {negative} = rule + if ( + unignored === negative && ignored !== unignored + || negative && !ignored && !unignored && !checkUnignored + ) { + return + } - const self = this; + const matched = rule.regex.test(path) - Object.defineProperty(builder, 'level', { - enumerable: true, - get() { - return self.level; - }, - set(level) { - self.level = level; - } - }); + if (matched) { + ignored = !negative + unignored = negative + } + }) - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); + return { + ignored, + unignored + } + } - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + // @returns {TestResult} + _test (originalPath, cache, checkUnignored, slices) { + const path = originalPath + // Supports nullable path + && checkPath.convert(originalPath) - // `__proto__` is used because we must return a function, but there is - // no way to create a function with a different prototype - builder.__proto__ = proto; // eslint-disable-line no-proto + checkPath(path, originalPath, throwError) - return builder; -} + return this._t(path, cache, checkUnignored, slices) + } -function applyStyle() { - // Support varags, but simply cast to string in case there's only one arg - const args = arguments; - const argsLen = args.length; - let str = String(arguments[0]); + _t (path, cache, checkUnignored, slices) { + if (path in cache) { + return cache[path] + } - if (argsLen === 0) { - return ''; - } + if (!slices) { + // path/to/a.js + // ['path', 'to', 'a.js'] + slices = path.split(SLASH) + } - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } + slices.pop() - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; - } + // If the path has no parent directory, just test it + if (!slices.length) { + return cache[path] = this._testOne(path, checkUnignored) + } - // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, - // see https://github.com/chalk/chalk/issues/58 - // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. - const originalDim = ansiStyles.dim.open; - if (isSimpleWindowsTerm && this.hasGrey) { - ansiStyles.dim.open = ''; - } + const parent = this._t( + slices.join(SLASH) + SLASH, + cache, + checkUnignored, + slices + ) - for (const code of this._styles.slice().reverse()) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - str = code.open + str.replace(code.closeRe, code.open) + code.close; + // If the path contains a parent directory, check the parent first + return cache[path] = parent.ignored + // > It is not possible to re-include a file if a parent directory of + // > that file is excluded. + ? parent + : this._testOne(path, checkUnignored) + } - // Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS - // https://github.com/chalk/chalk/pull/92 - str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); - } + ignores (path) { + return this._test(path, this._ignoreCache, false).ignored + } - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; + createFilter () { + return path => !this.ignores(path) + } - return str; + filter (paths) { + return makeArray(paths).filter(this.createFilter()) + } + + // @returns {TestResult} + test (path) { + return this._test(path, this._testCache, true) + } } -function chalkTag(chalk, strings) { - if (!Array.isArray(strings)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return [].slice.call(arguments, 1).join(' '); - } +const factory = options => new Ignore(options) - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; +const returnFalse = () => false - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); - } +const isPathValid = path => + checkPath(path && checkPath.convert(path), path, returnFalse) - return template(chalk, parts.join('')); -} +factory.isPathValid = isPathValid -Object.defineProperties(Chalk.prototype, styles); +// Fixes typescript +factory.default = factory -module.exports = Chalk(); // eslint-disable-line new-cap -module.exports.supportsColor = stdoutColor; -module.exports.default = module.exports; // For TypeScript +module.exports = factory + +// Windows +// -------------------------------------------------------------- +/* istanbul ignore if */ +if ( + // Detect `process` so that it can run in browsers. + typeof process !== 'undefined' + && ( + process.env && process.env.IGNORE_TEST_WIN32 + || process.platform === 'win32' + ) +) { + /* eslint no-control-regex: "off" */ + const makePosix = str => /^\\\\\?\\/.test(str) + || /["<>|\u0000-\u001F]+/u.test(str) + ? str + : str.replace(/\\/g, '/') + + checkPath.convert = makePosix + + // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/' + // 'd:\\foo' + const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i + checkPath.isNotRelative = path => + REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) + || isNotRelative(path) +} /***/ }), -/* 377 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(378); -const wrapAnsi16 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${code + offset}m`; -}; +module.exports = path => { + const isExtendedLengthPath = /^\\\\\?\\/.test(path); + const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex -const wrapAnsi256 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};5;${code}m`; -}; + if (isExtendedLengthPath || hasNonAscii) { + return path; + } -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; + return path.replace(/\\/g, '/'); }; -function assembleStyles() { - const codes = new Map(); - const styles = { - modifier: { - reset: [0, 0], - // 21 isn't widely supported and 22 does the same thing - bold: [1, 22], - dim: [2, 22], - italic: [3, 23], - underline: [4, 24], - inverse: [7, 27], - hidden: [8, 28], - strikethrough: [9, 29] - }, - color: { - black: [30, 39], - red: [31, 39], - green: [32, 39], - yellow: [33, 39], - blue: [34, 39], - magenta: [35, 39], - cyan: [36, 39], - white: [37, 39], - gray: [90, 39], - // Bright color - redBright: [91, 39], - greenBright: [92, 39], - yellowBright: [93, 39], - blueBright: [94, 39], - magentaBright: [95, 39], - cyanBright: [96, 39], - whiteBright: [97, 39] - }, - bgColor: { - bgBlack: [40, 49], - bgRed: [41, 49], - bgGreen: [42, 49], - bgYellow: [43, 49], - bgBlue: [44, 49], - bgMagenta: [45, 49], - bgCyan: [46, 49], - bgWhite: [47, 49], +/***/ }), +/* 363 */ +/***/ (function(module, exports, __webpack_require__) { - // Bright color - bgBlackBright: [100, 49], - bgRedBright: [101, 49], - bgGreenBright: [102, 49], - bgYellowBright: [103, 49], - bgBlueBright: [104, 49], - bgMagentaBright: [105, 49], - bgCyanBright: [106, 49], - bgWhiteBright: [107, 49] - } - }; +"use strict"; - // Fix humans - styles.color.grey = styles.color.gray; +const {Transform} = __webpack_require__(137); - for (const groupName of Object.keys(styles)) { - const group = styles[groupName]; +class ObjectTransform extends Transform { + constructor() { + super({ + objectMode: true + }); + } +} - for (const styleName of Object.keys(group)) { - const style = group[styleName]; +class FilterStream extends ObjectTransform { + constructor(filter) { + super(); + this._filter = filter; + } - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; + _transform(data, encoding, callback) { + if (this._filter(data)) { + this.push(data); + } - group[styleName] = styles[styleName]; + callback(); + } +} - codes.set(style[0], style[1]); - } +class UniqueStream extends ObjectTransform { + constructor() { + super(); + this._pushed = new Set(); + } - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); + _transform(data, encoding, callback) { + if (!this._pushed.has(data)) { + this.push(data); + this._pushed.add(data); + } - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); + callback(); } +} - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; +module.exports = { + FilterStream, + UniqueStream +}; - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; - styles.color.ansi = { - ansi: wrapAnsi16(ansi2ansi, 0) - }; - styles.color.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 0) - }; - styles.color.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 0) - }; +/***/ }), +/* 364 */ +/***/ (function(module, exports, __webpack_require__) { - styles.bgColor.ansi = { - ansi: wrapAnsi16(ansi2ansi, 10) - }; - styles.bgColor.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 10) - }; - styles.bgColor.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 10) - }; +/*! + * is-glob + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; - } +var isExtglob = __webpack_require__(299); +var chars = { '{': '}', '(': ')', '[': ']'}; +var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; +var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - const suite = colorConvert[key]; +module.exports = function isGlob(str, options) { + if (typeof str !== 'string' || str === '') { + return false; + } - if (key === 'ansi16') { - key = 'ansi'; - } + if (isExtglob(str)) { + return true; + } - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } + var regex = strictRegex; + var match; - if ('ansi256' in suite) { - styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); - styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); - } + // optionally relax regex + if (options && options.strict === false) { + regex = relaxedRegex; + } - if ('rgb' in suite) { - styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); - styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); - } - } + while ((match = regex.exec(str))) { + if (match[2]) return true; + var idx = match.index + match[0].length; - return styles; -} + // if an open bracket/brace/paren is escaped, + // set the index to the next closing character + var open = match[1]; + var close = open ? chars[open] : null; + if (open && close) { + var n = str.indexOf(close, idx); + if (n !== -1) { + idx = n + 1; + } + } -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); + str = str.slice(idx); + } + return false; +}; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), -/* 378 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(379); -var route = __webpack_require__(381); - -var convert = {}; - -var models = Object.keys(conversions); +"use strict"; -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } +const path = __webpack_require__(4); - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } +module.exports = path_ => { + let cwd = process.cwd(); - return fn(args); - }; + path_ = path.resolve(path_); - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; + if (process.platform === 'win32') { + cwd = cwd.toLowerCase(); + path_ = path_.toLowerCase(); } - return wrappedFn; -} + return path_ === cwd; +}; -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } +/***/ }), +/* 366 */ +/***/ (function(module, exports, __webpack_require__) { - var result = fn(args); +"use strict"; - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } +const path = __webpack_require__(4); - return result; - }; +module.exports = (childPath, parentPath) => { + childPath = path.resolve(childPath); + parentPath = path.resolve(parentPath); - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; + if (process.platform === 'win32') { + childPath = childPath.toLowerCase(); + parentPath = parentPath.toLowerCase(); } - return wrappedFn; + if (childPath === parentPath) { + return false; + } + + childPath += path.sep; + parentPath += path.sep; + + return childPath.startsWith(parentPath); +}; + + +/***/ }), +/* 367 */ +/***/ (function(module, exports, __webpack_require__) { + +const assert = __webpack_require__(139) +const path = __webpack_require__(4) +const fs = __webpack_require__(133) +let glob = undefined +try { + glob = __webpack_require__(146) +} catch (_err) { + // treat glob as optional. } -models.forEach(function (fromModel) { - convert[fromModel] = {}; +const defaultGlobOpts = { + nosort: true, + silent: true +} - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); +// for EMFILE handling +let timeout = 0 - var routes = route(fromModel); - var routeModels = Object.keys(routes); +const isWindows = (process.platform === "win32") - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; +const defaults = options => { + const methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(m => { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + if (options.glob === false) { + options.disableGlob = true + } + if (options.disableGlob !== true && glob === undefined) { + throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') + } + options.disableGlob = options.disableGlob || false + options.glob = options.glob || defaultGlobOpts +} -module.exports = convert; +const rimraf = (p, options, cb) => { + if (typeof options === 'function') { + cb = options + options = {} + } + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.equal(typeof options, 'object', 'rimraf: options should be object') -/***/ }), -/* 379 */ -/***/ (function(module, exports, __webpack_require__) { + defaults(options) -/* MIT license */ -var cssKeywords = __webpack_require__(380); + let busyTries = 0 + let errState = null + let n = 0 -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) + const next = (er) => { + errState = errState || er + if (--n === 0) + cb(errState) + } -var reverseKeywords = {}; -for (var key in cssKeywords) { - if (cssKeywords.hasOwnProperty(key)) { - reverseKeywords[cssKeywords[key]] = key; - } -} + const afterGlob = (er, results) => { + if (er) + return cb(er) -var convert = module.exports = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} -}; + n = results.length + if (n === 0) + return cb() -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } + results.forEach(p => { + const CB = (er) => { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + // try again, with the same exact callback as this one. + return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) + } - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(() => rimraf_(p, options, CB), timeout ++) + } - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } + // already gone + if (er.code === "ENOENT") er = null + } - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); - } -} + timeout = 0 + next(er) + } + rimraf_(p, options, CB) + }) + } -convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } + options.lstat(p, (er, stat) => { + if (!er) + return afterGlob(null, [p]) - h = Math.min(h * 60, 360); + glob(p, options.glob, afterGlob) + }) - if (h < 0) { - h += 360; - } +} - l = (min + max) / 2; +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +const rimraf_ = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); - } + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, (er, st) => { + if (er && er.code === "ENOENT") + return cb(null) - return [h, s * 100, l * 100]; -}; + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === "EPERM" && isWindows) + fixWinEPERM(p, options, er, cb) -convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; + options.unlink(p, er => { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} - if (diff === 0) { - h = s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); +const fixWinEPERM = (p, options, er, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - } + options.chmod(p, 0o666, er2 => { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, (er3, stats) => { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) +} - return [ - h * 360, - s * 100, - v * 100 - ]; -}; +const fixWinEPERMSync = (p, options, er) => { + assert(p) + assert(options) -convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); + try { + options.chmodSync(p, 0o666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + let stats + try { + stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er + } - return [h, w * 100, b * 100]; -}; + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) +} -convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; +const rmdir = (p, options, originalEr, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, er => { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} - return [c * 100, m * 100, y * 100, k * 100]; -}; +const rmkids = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ -function comparativeDistance(x, y) { - return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) - ); + options.readdir(p, (er, files) => { + if (er) + return cb(er) + let n = files.length + if (n === 0) + return options.rmdir(p, cb) + let errState + files.forEach(f => { + rimraf(path.join(p, f), options, er => { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) } -convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } - - var currentClosestDistance = Infinity; - var currentClosestKeyword; +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +const rimrafSync = (p, options) => { + options = options || {} + defaults(options) - for (var keyword in cssKeywords) { - if (cssKeywords.hasOwnProperty(keyword)) { - var value = cssKeywords[keyword]; + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') - // Compute comparative distance - var distance = comparativeDistance(rgb, value); + let results - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } - } - } + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + options.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, options.glob) + } + } - return currentClosestKeyword; -}; + if (!results.length) + return -convert.keyword.rgb = function (keyword) { - return cssKeywords[keyword]; -}; + for (let i = 0; i < results.length; i++) { + const p = results[i] -convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; + let st + try { + st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + // Windows can EPERM on stat. Life is suffering. + if (er.code === "EPERM" && isWindows) + fixWinEPERMSync(p, options, er) + } - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er - return [x * 100, y * 100, z * 100]; -}; + rmdirSync(p, options, er) + } + } +} -convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; +const rmdirSync = (p, options, originalEr) => { + assert(p) + assert(options) - x /= 95.047; - y /= 100; - z /= 108.883; + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) + } +} - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); +const rmkidsSync = (p, options) => { + assert(p) + assert(options) + options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + const retries = isWindows ? 100 : 1 + let i = 0 + do { + let threw = true + try { + const ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) + continue + } + } while (true) +} - return [l, a, b]; -}; +module.exports = rimraf +rimraf.sync = rimrafSync -convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; - if (s === 0) { - val = l * 255; - return [val, val, val]; - } +/***/ }), +/* 368 */ +/***/ (function(module, exports, __webpack_require__) { - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } +"use strict"; - t1 = 2 * l - t2; +const AggregateError = __webpack_require__(369); - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } - if (t3 > 1) { - t3--; +module.exports = async ( + iterable, + mapper, + { + concurrency = Infinity, + stopOnError = true + } = {} +) => { + return new Promise((resolve, reject) => { + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); } - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); } - rgb[i] = val * 255; - } - - return rgb; -}; + const ret = []; + const errors = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; -convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; + const next = () => { + if (isRejected) { + return; + } - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; - return [h, sv * 100, v * 100]; -}; + if (nextItem.done) { + isIterableDone = true; -convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; + if (resolvingCount === 0) { + if (!stopOnError && errors.length !== 0) { + reject(new AggregateError(errors)); + } else { + resolve(ret); + } + } - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); - v *= 255; + return; + } - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -}; + resolvingCount++; -convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; + (async () => { + try { + const element = await nextItem.value; + ret[i] = await mapper(element, i); + resolvingCount--; + next(); + } catch (error) { + if (stopOnError) { + isRejected = true; + reject(error); + } else { + errors.push(error); + resolvingCount--; + next(); + } + } + })(); + }; - l = (2 - s) * v; - lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; + for (let i = 0; i < concurrency; i++) { + next(); - return [h, sl * 100, l * 100]; + if (isIterableDone) { + break; + } + } + }); }; -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } +/***/ }), +/* 369 */ +/***/ (function(module, exports, __webpack_require__) { - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; +"use strict"; - if ((i & 0x01) !== 0) { - f = 1 - f; - } +const indentString = __webpack_require__(370); +const cleanStack = __webpack_require__(371); - n = wh + f * (v - wh); // linear interpolation +const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); - var r; - var g; - var b; - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } +class AggregateError extends Error { + constructor(errors) { + if (!Array.isArray(errors)) { + throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); + } - return [r * 255, g * 255, b * 255]; -}; + errors = [...errors].map(error => { + if (error instanceof Error) { + return error; + } -convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; + if (error !== null && typeof error === 'object') { + // Handle plain error objects with message property and/or possibly other metadata + return Object.assign(new Error(error.message), error); + } - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); + return new Error(error); + }); - return [r * 255, g * 255, b * 255]; -}; + let message = errors + .map(error => { + // The `stack` property is not standardized, so we can't assume it exists + return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); + }) + .join('\n'); + message = '\n' + indentString(message, 4); + super(message); -convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; + this.name = 'AggregateError'; - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + Object.defineProperty(this, '_errors', {value: errors}); + } - // assume sRGB - r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r * 12.92; + * [Symbol.iterator]() { + for (const error of this._errors) { + yield error; + } + } +} - g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g * 12.92; +module.exports = AggregateError; - b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b * 12.92; - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); +/***/ }), +/* 370 */ +/***/ (function(module, exports, __webpack_require__) { - return [r * 255, g * 255, b * 255]; -}; +"use strict"; -convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; - x /= 95.047; - y /= 100; - z /= 108.883; +module.exports = (string, count = 1, options) => { + options = { + indent: ' ', + includeEmptyLines: false, + ...options + }; - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + if (typeof string !== 'string') { + throw new TypeError( + `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` + ); + } - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + if (typeof count !== 'number') { + throw new TypeError( + `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` + ); + } - return [l, a, b]; -}; + if (typeof options.indent !== 'string') { + throw new TypeError( + `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` + ); + } -convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; + if (count === 0) { + return string; + } - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; + const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + return string.replace(regex, options.indent.repeat(count)); +}; - x *= 95.047; - y *= 100; - z *= 108.883; - return [x, y, z]; -}; +/***/ }), +/* 371 */ +/***/ (function(module, exports, __webpack_require__) { -convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; +"use strict"; - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; +const os = __webpack_require__(120); - if (h < 0) { - h += 360; - } +const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; +const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; +const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir(); - c = Math.sqrt(a * a + b * b); +module.exports = (stack, options) => { + options = Object.assign({pretty: false}, options); - return [l, c, h]; -}; + return stack.replace(/\\/g, '/') + .split('\n') + .filter(line => { + const pathMatches = line.match(extractPathRegex); + if (pathMatches === null || !pathMatches[1]) { + return true; + } -convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; + const match = pathMatches[1]; - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); + // Electron + if ( + match.includes('.app/Contents/Resources/electron.asar') || + match.includes('.app/Contents/Resources/default_app.asar') + ) { + return false; + } - return [l, a, b]; -}; + return !pathRegex.test(match); + }) + .filter(line => line.trim() !== '') + .map(line => { + if (options.pretty) { + return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); + } -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + return line; + }) + .join('\n'); +}; - value = Math.round(value / 50); - if (value === 0) { - return 30; - } +/***/ }), +/* 372 */ +/***/ (function(module, exports, __webpack_require__) { - var ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); +"use strict"; - if (value === 2) { - ansi += 60; - } +const chalk = __webpack_require__(373); +const cliCursor = __webpack_require__(378); +const cliSpinners = __webpack_require__(382); +const logSymbols = __webpack_require__(384); - return ansi; -}; +class Ora { + constructor(options) { + if (typeof options === 'string') { + options = { + text: options + }; + } -convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; + this.options = Object.assign({ + text: '', + color: 'cyan', + stream: process.stderr + }, options); -convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; + const sp = this.options.spinner; + this.spinner = typeof sp === 'object' ? sp : (process.platform === 'win32' ? cliSpinners.line : (cliSpinners[sp] || cliSpinners.dots)); // eslint-disable-line no-nested-ternary - // we use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; + if (this.spinner.frames === undefined) { + throw new Error('Spinner must define `frames`'); } - if (r > 248) { - return 231; + this.text = this.options.text; + this.color = this.options.color; + this.interval = this.options.interval || this.spinner.interval || 100; + this.stream = this.options.stream; + this.id = null; + this.frameIndex = 0; + this.enabled = typeof this.options.enabled === 'boolean' ? this.options.enabled : ((this.stream && this.stream.isTTY) && !process.env.CI); + } + frame() { + const frames = this.spinner.frames; + let frame = frames[this.frameIndex]; + + if (this.color) { + frame = chalk[this.color](frame); } - return Math.round(((r - 8) / 247) * 24) + 232; + this.frameIndex = ++this.frameIndex % frames.length; + + return frame + ' ' + this.text; } + clear() { + if (!this.enabled) { + return this; + } - var ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); + this.stream.clearLine(); + this.stream.cursorTo(0); - return ansi; -}; + return this; + } + render() { + this.clear(); + this.stream.write(this.frame()); -convert.ansi16.rgb = function (args) { - var color = args % 10; + return this; + } + start(text) { + if (text) { + this.text = text; + } - // handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; + if (!this.enabled || this.id) { + return this; } - color = color / 10.5 * 255; + cliCursor.hide(this.stream); + this.render(); + this.id = setInterval(this.render.bind(this), this.interval); - return [color, color, color]; + return this; } + stop() { + if (!this.enabled) { + return this; + } - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; - - return [r, g, b]; -}; + clearInterval(this.id); + this.id = null; + this.frameIndex = 0; + this.clear(); + cliCursor.show(this.stream); -convert.ansi256.rgb = function (args) { - // handle greyscale - if (args >= 232) { - var c = (args - 232) * 10 + 8; - return [c, c, c]; + return this; + } + succeed(text) { + return this.stopAndPersist({symbol: logSymbols.success, text}); + } + fail(text) { + return this.stopAndPersist({symbol: logSymbols.error, text}); } + warn(text) { + return this.stopAndPersist({symbol: logSymbols.warning, text}); + } + info(text) { + return this.stopAndPersist({symbol: logSymbols.info, text}); + } + stopAndPersist(options) { + if (!this.enabled) { + return this; + } - args -= 16; + // Legacy argument + // TODO: Deprecate sometime in the future + if (typeof options === 'string') { + options = { + symbol: options + }; + } - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; + options = options || {}; - return [r, g, b]; -}; + this.stop(); + this.stream.write(`${options.symbol || ' '} ${options.text || this.text}\n`); -convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); + return this; + } +} - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; +module.exports = function (opts) { + return new Ora(opts); }; -convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; +module.exports.promise = (action, options) => { + if (typeof action.then !== 'function') { + throw new TypeError('Parameter `action` must be a Promise'); } - var colorString = match[0]; - - if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { - return char + char; - }).join(''); - } + const spinner = new Ora(options); + spinner.start(); - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; + action.then( + () => { + spinner.succeed(); + }, + () => { + spinner.fail(); + } + ); - return [r, g, b]; + return spinner; }; -convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; - - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma + 4; - } +/***/ }), +/* 373 */ +/***/ (function(module, exports, __webpack_require__) { - hue /= 6; - hue %= 1; +"use strict"; - return [hue * 360, chroma * 100, grayscale * 100]; -}; +const escapeStringRegexp = __webpack_require__(178); +const ansiStyles = __webpack_require__(374); +const stdoutColor = __webpack_require__(375).stdout; -convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; +const template = __webpack_require__(377); - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; - return [hsl[0], c * 100, f * 100]; -}; +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); -convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; +const styles = Object.create(null); - var c = s * v; - var f = 0; +function applyOptions(obj, options) { + options = options || {}; - if (c < 1.0) { - f = (v - c) / (1 - c); - } + // Detect level if not set manually + const scLevel = stdoutColor ? stdoutColor.level : 0; + obj.level = options.level === undefined ? scLevel : options.level; + obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; +} - return [hsv[0], c * 100, f * 100]; -}; +function Chalk(options) { + // We check for this.template here since calling `chalk.constructor()` + // by itself will have a `this` of a previously constructed chalk object + if (!this || !(this instanceof Chalk) || this.template) { + const chalk = {}; + applyOptions(chalk, options); -convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; + chalk.template.constructor = Chalk; - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; + return chalk.template; } - mg = (1.0 - c) * g; + applyOptions(this, options); +} - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; +} -convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); - var v = c + g * (1.0 - c); - var f = 0; + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; +} - if (v > 0.0) { - f = c / v; +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); } - - return [hcg[0], f * 100, v * 100]; }; -convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; - - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; } - return [hcg[0], s * 100, l * 100]; -}; - -convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; -}; - -convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; + styles[model] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.color.close, + closeRe: ansiStyles.color.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} - if (c < 1) { - g = (v - c) / (1 - c); +ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); +for (const model of Object.keys(ansiStyles.bgColor.ansi)) { + if (skipModels.has(model)) { + continue; } - return [hwb[0], c * 100, g * 100]; -}; - -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; - -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; - -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.bgColor.close, + closeRe: ansiStyles.bgColor.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} -convert.gray.hsl = convert.gray.hsv = function (args) { - return [0, 0, args[0]]; -}; +const proto = Object.defineProperties(() => {}, styles); -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; + builder._styles = _styles; + builder._empty = _empty; -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; + const self = this; -convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; + Object.defineProperty(builder, 'level', { + enumerable: true, + get() { + return self.level; + }, + set(level) { + self.level = level; + } + }); - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; + Object.defineProperty(builder, 'enabled', { + enumerable: true, + get() { + return self.enabled; + }, + set(enabled) { + self.enabled = enabled; + } + }); -convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; -}; + // See below for fix regarding invisible grey/dim combination on Windows + builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto -/***/ }), -/* 380 */ -/***/ (function(module, exports, __webpack_require__) { + return builder; +} -"use strict"; - - -module.exports = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; +function applyStyle() { + // Support varags, but simply cast to string in case there's only one arg + const args = arguments; + const argsLen = args.length; + let str = String(arguments[0]); + if (argsLen === 0) { + return ''; + } -/***/ }), -/* 381 */ -/***/ (function(module, exports, __webpack_require__) { + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } -var conversions = __webpack_require__(379); + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } -/* - this function routes a model to all other models. + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + const originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && this.hasGrey) { + ansiStyles.dim.open = ''; + } - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). + for (const code of this._styles.slice().reverse()) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; - conversions that are not possible simply are not included. -*/ + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); + } -function buildGraph() { - var graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); + // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue + ansiStyles.dim.open = originalDim; - for (var len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; + return str; +} + +function chalkTag(chalk, strings) { + if (!Array.isArray(strings)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return [].slice.call(arguments, 1).join(' '); } - return graph; + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; + + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } + + return template(chalk, parts.join('')); } -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop +Object.defineProperties(Chalk.prototype, styles); - graph[fromModel].distance = 0; +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript - while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; +/***/ }), +/* 374 */ +/***/ (function(module, exports, __webpack_require__) { - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } +"use strict"; +/* WEBPACK VAR INJECTION */(function(module) { +const colorConvert = __webpack_require__(180); - return graph; -} +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${code + offset}m`; +}; -function link(from, to) { - return function (args) { - return to(from(args)); +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39], + + // Bright color + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } }; -} -function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; + // Fix humans + styles.color.grey = styles.color.gray; - var cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; + + for (const styleName of Object.keys(group)) { + const style = group[styleName]; + + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); } - fn.conversion = path; - return fn; -} + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; -module.exports = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; - if (node.parent === null) { - // no possible conversion, or this node is the source model. + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; + + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { continue; } - conversion[toModel] = wrapConversion(toModel, graph); + const suite = colorConvert[key]; + + if (key === 'ansi16') { + key = 'ansi'; + } + + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } + + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } + + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } } - return conversion; -}; + return styles; +} +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), -/* 382 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(120); -const hasFlag = __webpack_require__(383); +const hasFlag = __webpack_require__(376); const env = process.env; @@ -50052,7 +47781,7 @@ module.exports = { /***/ }), -/* 383 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50067,7 +47796,7 @@ module.exports = (flag, argv) => { /***/ }), -/* 384 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50202,12 +47931,12 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 385 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(386); +const restoreCursor = __webpack_require__(379); let hidden = false; @@ -50248,12 +47977,12 @@ exports.toggle = (force, stream) => { /***/ }), -/* 386 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(387); +const onetime = __webpack_require__(380); const signalExit = __webpack_require__(217); module.exports = onetime(() => { @@ -50264,12 +47993,12 @@ module.exports = onetime(() => { /***/ }), -/* 387 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(388); +const mimicFn = __webpack_require__(381); module.exports = (fn, opts) => { // TODO: Remove this in v3 @@ -50310,7 +48039,7 @@ module.exports = (fn, opts) => { /***/ }), -/* 388 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50326,27 +48055,27 @@ module.exports = (to, from) => { /***/ }), -/* 389 */ +/* 382 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(390); +module.exports = __webpack_require__(383); /***/ }), -/* 390 */ +/* 383 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]}}"); /***/ }), -/* 391 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(392); +const chalk = __webpack_require__(385); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -50368,16 +48097,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 392 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(393); +const ansiStyles = __webpack_require__(386); const stdoutColor = __webpack_require__(184).stdout; -const template = __webpack_require__(394); +const template = __webpack_require__(387); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -50603,7 +48332,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 393 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50776,7 +48505,7 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), -/* 394 */ +/* 387 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50911,7 +48640,7 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 395 */ +/* 388 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50972,7 +48701,7 @@ const RunCommand = { }; /***/ }), -/* 396 */ +/* 389 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50982,7 +48711,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(397); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(390); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51067,14 +48796,14 @@ const WatchCommand = { }; /***/ }), -/* 397 */ +/* 390 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __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__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(398); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(391); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51141,141 +48870,141 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 398 */ +/* 391 */ /***/ (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__(399); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(392); /* 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__(400); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(393); /* 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__(401); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(394); /* 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__(402); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(395); /* 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__(403); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(396); /* 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__(404); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(397); /* 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__(405); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(398); /* 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__(406); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(399); /* 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__(407); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(400); /* 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__(408); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(401); /* 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__(409); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(402); /* 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__(80); /* 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__(410); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(403); /* 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__(411); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(404); /* 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__(412); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(405); /* 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__(413); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(406); /* 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__(414); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(407); /* 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__(415); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(408); /* 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__(416); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(409); /* 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__(418); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(411); /* 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__(419); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(412); /* 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__(420); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(413); /* 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__(421); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(414); /* 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__(422); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(415); /* 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__(423); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(416); /* 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__(426); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(419); /* 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__(427); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(420); /* 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__(428); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(421); /* 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__(429); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(422); /* 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__(430); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(423); /* 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__(104); /* 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__(431); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(424); /* 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__(432); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(425); /* 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__(433); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(426); /* 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__(434); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(427); /* 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__(31); /* 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__(435); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(428); /* 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__(436); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(429); /* 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__(437); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(430); /* 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__(66); /* 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__(439); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(432); /* 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__(440); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(433); /* 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__(441); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(434); /* 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__(444); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(437); /* 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__(81); @@ -51286,175 +49015,175 @@ __webpack_require__.r(__webpack_exports__); /* 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__(445); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(438); /* 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__(446); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(439); /* 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__(447); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(440); /* 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__(448); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(441); /* 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__(41); /* 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__(449); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(442); /* 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__(450); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(443); /* 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__(451); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(444); /* 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__(452); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(445); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(446); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(450); /* 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__(442); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(435); /* 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__(458); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(454); /* 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__(30); /* 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__(462); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(456); /* 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__(443); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(436); /* 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__(464); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(470); /* 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__(425); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(418); /* 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__(438); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(431); /* 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__(478); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(472); /* 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__(480); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(473); /* 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__(481); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(474); /* 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__(482); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(475); /* 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__(424); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(417); /* 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__(483); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(476); /* 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__(484); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(477); /* 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__(485); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(478); /* 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__(486); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(479); /* 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__(487); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(480); /* 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__(488); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(481); /* 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__(489); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(482); /* 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__(490); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(483); /* 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__(491); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(484); /* 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__(492); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(485); /* 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__(493); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(486); /* 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__(494); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(487); /* 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__(495); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(488); /* 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 */ @@ -51566,7 +49295,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 399 */ +/* 392 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51647,14 +49376,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 400 */ +/* 393 */ /***/ (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__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(399); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(392); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(107); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -51670,7 +49399,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 401 */ +/* 394 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51719,7 +49448,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 402 */ +/* 395 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51820,7 +49549,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 403 */ +/* 396 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51981,7 +49710,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 404 */ +/* 397 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52101,7 +49830,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 405 */ +/* 398 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52196,7 +49925,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 399 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52260,7 +49989,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52276,7 +50005,7 @@ function combineAll(project) { /***/ }), -/* 408 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52308,7 +50037,7 @@ function combineLatest() { /***/ }), -/* 409 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52328,7 +50057,7 @@ function concat() { /***/ }), -/* 410 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52344,13 +50073,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 411 */ +/* 404 */ /***/ (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__(410); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(403); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -52360,7 +50089,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 412 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52425,7 +50154,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 413 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52513,7 +50242,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 414 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52589,7 +50318,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 415 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52639,7 +50368,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 416 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52647,7 +50376,7 @@ __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__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -52746,7 +50475,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 417 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52760,7 +50489,7 @@ function isDate(value) { /***/ }), -/* 418 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52906,7 +50635,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 419 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52944,7 +50673,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 420 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53022,7 +50751,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 421 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53093,13 +50822,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 422 */ +/* 415 */ /***/ (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__(421); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -53109,7 +50838,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 423 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53117,9 +50846,9 @@ __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__(62); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(424); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(415); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(425); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(418); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -53141,7 +50870,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 424 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53207,7 +50936,7 @@ function defaultErrorFactory() { /***/ }), -/* 425 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53269,7 +50998,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 426 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53291,7 +51020,7 @@ function endWith() { /***/ }), -/* 427 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53353,7 +51082,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 428 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53410,7 +51139,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 429 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53510,7 +51239,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53629,7 +51358,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 431 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53667,7 +51396,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 432 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53739,13 +51468,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 426 */ /***/ (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__(432); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -53755,7 +51484,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 434 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53763,9 +51492,9 @@ __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__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(425); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(415); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(424); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(418); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(417); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -53782,7 +51511,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 435 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53819,7 +51548,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 436 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53863,7 +51592,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53871,9 +51600,9 @@ __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__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(438); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(424); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(415); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(431); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(417); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -53890,7 +51619,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 438 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53967,7 +51696,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 439 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54006,7 +51735,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54056,13 +51785,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 441 */ +/* 434 */ /***/ (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__(442); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -54075,15 +51804,15 @@ function max(comparer) { /***/ }), -/* 442 */ +/* 435 */ /***/ (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__(443); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(438); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(415); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(431); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(408); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -54104,7 +51833,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 443 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54186,7 +51915,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54206,7 +51935,7 @@ function merge() { /***/ }), -/* 445 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54231,7 +51960,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 446 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54346,13 +52075,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 447 */ +/* 440 */ /***/ (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__(442); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -54365,7 +52094,7 @@ function min(comparer) { /***/ }), -/* 448 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54414,7 +52143,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 449 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54508,7 +52237,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 450 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54556,7 +52285,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 451 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54579,7 +52308,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 452 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54619,14 +52348,14 @@ function plucker(props, length) { /***/ }), -/* 453 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -54639,14 +52368,14 @@ function publish(selector) { /***/ }), -/* 454 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -54657,14 +52386,14 @@ function publishBehavior(value) { /***/ }), -/* 455 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -54675,14 +52404,14 @@ function publishLast() { /***/ }), -/* 456 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -54698,7 +52427,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 457 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54725,7 +52454,7 @@ function race() { /***/ }), -/* 458 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54790,7 +52519,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 459 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54886,7 +52615,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 460 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54939,7 +52668,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 461 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55027,7 +52756,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 462 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55084,7 +52813,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55144,7 +52873,7 @@ function dispatchNotification(state) { /***/ }), -/* 464 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55267,13 +52996,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(448); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(441); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -55290,7 +53019,7 @@ function share() { /***/ }), -/* 466 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55355,7 +53084,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 467 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55435,7 +53164,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 468 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55477,7 +53206,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 469 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55539,7 +53268,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 470 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55600,7 +53329,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 471 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55656,7 +53385,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55685,13 +53414,13 @@ function startWith() { /***/ }), -/* 473 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(474); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(467); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -55716,7 +53445,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 474 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55780,13 +53509,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __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__(476); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(469); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -55798,7 +53527,7 @@ function switchAll() { /***/ }), -/* 476 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55892,13 +53621,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 477 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(476); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(469); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -55908,7 +53637,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 478 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55958,7 +53687,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56026,7 +53755,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 480 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56114,7 +53843,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 481 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56218,7 +53947,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 482 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56227,7 +53956,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(481); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(474); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -56316,7 +54045,7 @@ function dispatchNext(arg) { /***/ }), -/* 483 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56324,7 +54053,7 @@ __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__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(443); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(436); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -56360,7 +54089,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 484 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56368,7 +54097,7 @@ __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__(55); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(485); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(478); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -56385,7 +54114,7 @@ function timeout(due, scheduler) { /***/ }), -/* 485 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56393,7 +54122,7 @@ __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__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); /* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); /* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -56467,7 +54196,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 486 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56497,13 +54226,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 487 */ +/* 480 */ /***/ (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__(442); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(435); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -56520,7 +54249,7 @@ function toArray() { /***/ }), -/* 488 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56600,7 +54329,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 489 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56690,7 +54419,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 490 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56860,7 +54589,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 491 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57003,7 +54732,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 492 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57100,7 +54829,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 493 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57195,7 +54924,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 494 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57217,7 +54946,7 @@ function zip() { /***/ }), -/* 495 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57233,7 +54962,7 @@ function zipAll(project) { /***/ }), -/* 496 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57242,8 +54971,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(162); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(497); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(498); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(490); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(491); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -57325,7 +55054,7 @@ function toArray(value) { } /***/ }), -/* 497 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57478,7 +55207,7 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 498 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57486,12 +55215,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(499); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(492); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(369); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(366); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(280); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(279); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(145); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(276); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -57521,6 +55251,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope + /** * Helper class for dealing with a set of projects as children of * the Kibana project. The kbn/pm is currently implemented to be @@ -57535,7 +55266,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope class Kibana { static async loadFrom(rootPath) { - return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_3__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])({ + return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_4__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])({ rootPath }))); } @@ -57594,7 +55325,7 @@ class Kibana { getProjectAndDeps(name) { const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_3__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + return Object(_projects__WEBPACK_IMPORTED_MODULE_4__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); } /** filter the projects to just those matching certain paths/include/exclude tags */ @@ -57603,7 +55334,7 @@ class Kibana { const allProjects = this.getAllProjects(); const filteredProjects = new Map(); const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { + const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { rootPath: this.kibanaProject.path })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); @@ -57629,18 +55360,38 @@ class Kibana { return !this.isPartOfRepo(project); } + resolveAllProductionDependencies(yarnLock, log) { + const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ + project: this.kibanaProject, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ + project: this.getProject('x-pack'), + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + } + } /***/ }), -/* 499 */ +/* 492 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(149); -const arrayUnion = __webpack_require__(500); -const arrayDiffer = __webpack_require__(501); -const arrify = __webpack_require__(502); +const arrayUnion = __webpack_require__(493); +const arrayDiffer = __webpack_require__(494); +const arrify = __webpack_require__(495); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -57664,7 +55415,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 500 */ +/* 493 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57676,7 +55427,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 501 */ +/* 494 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57691,7 +55442,7 @@ module.exports = arrayDiffer; /***/ }), -/* 502 */ +/* 495 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57721,12 +55472,12 @@ module.exports = arrify; /***/ }), -/* 503 */ +/* 496 */ /***/ (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__(504); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(497); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); /* @@ -57750,19 +55501,19 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 504 */ +/* 497 */ /***/ (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__(505); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(498); /* 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__(288); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(285); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(276); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(143); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(164); @@ -57898,7 +55649,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 505 */ +/* 498 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57906,13 +55657,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(155); const path = __webpack_require__(4); const os = __webpack_require__(120); -const pAll = __webpack_require__(506); -const arrify = __webpack_require__(508); -const globby = __webpack_require__(509); -const isGlob = __webpack_require__(719); -const cpFile = __webpack_require__(720); -const junk = __webpack_require__(732); -const CpyError = __webpack_require__(733); +const pAll = __webpack_require__(499); +const arrify = __webpack_require__(501); +const globby = __webpack_require__(502); +const isGlob = __webpack_require__(700); +const cpFile = __webpack_require__(701); +const junk = __webpack_require__(713); +const CpyError = __webpack_require__(714); const defaultOptions = { ignoreJunk: true @@ -58031,12 +55782,12 @@ module.exports = (source, destination, { /***/ }), -/* 506 */ +/* 499 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(507); +const pMap = __webpack_require__(500); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -58044,7 +55795,7 @@ module.exports.default = module.exports; /***/ }), -/* 507 */ +/* 500 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58123,7 +55874,7 @@ module.exports.default = pMap; /***/ }), -/* 508 */ +/* 501 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58153,17 +55904,17 @@ module.exports = arrify; /***/ }), -/* 509 */ +/* 502 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(510); +const arrayUnion = __webpack_require__(503); const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(512); -const dirGlob = __webpack_require__(712); -const gitignore = __webpack_require__(715); +const fastGlob = __webpack_require__(505); +const dirGlob = __webpack_require__(693); +const gitignore = __webpack_require__(696); const DEFAULT_FILTER = () => false; @@ -58308,12 +56059,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 510 */ +/* 503 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(511); +var arrayUniq = __webpack_require__(504); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -58321,7 +56072,7 @@ module.exports = function () { /***/ }), -/* 511 */ +/* 504 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58390,10 +56141,10 @@ if ('Set' in global) { /***/ }), -/* 512 */ +/* 505 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(513); +const pkg = __webpack_require__(506); module.exports = pkg.async; module.exports.default = pkg.async; @@ -58406,19 +56157,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 513 */ +/* 506 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(514); -var taskManager = __webpack_require__(515); -var reader_async_1 = __webpack_require__(683); -var reader_stream_1 = __webpack_require__(707); -var reader_sync_1 = __webpack_require__(708); -var arrayUtils = __webpack_require__(710); -var streamUtils = __webpack_require__(711); +var optionsManager = __webpack_require__(507); +var taskManager = __webpack_require__(508); +var reader_async_1 = __webpack_require__(664); +var reader_stream_1 = __webpack_require__(688); +var reader_sync_1 = __webpack_require__(689); +var arrayUtils = __webpack_require__(691); +var streamUtils = __webpack_require__(692); /** * Synchronous API. */ @@ -58484,7 +56235,7 @@ function isString(source) { /***/ }), -/* 514 */ +/* 507 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58522,13 +56273,13 @@ exports.prepare = prepare; /***/ }), -/* 515 */ +/* 508 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(516); +var patternUtils = __webpack_require__(509); /** * Generate tasks based on parent directory of each pattern. */ @@ -58619,16 +56370,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 516 */ +/* 509 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(517); -var isGlob = __webpack_require__(520); -var micromatch = __webpack_require__(521); +var globParent = __webpack_require__(510); +var isGlob = __webpack_require__(513); +var micromatch = __webpack_require__(514); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -58774,15 +56525,15 @@ exports.matchAny = matchAny; /***/ }), -/* 517 */ +/* 510 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(518); -var pathDirname = __webpack_require__(519); +var isglob = __webpack_require__(511); +var pathDirname = __webpack_require__(512); var isWin32 = __webpack_require__(120).platform() === 'win32'; module.exports = function globParent(str) { @@ -58805,7 +56556,7 @@ module.exports = function globParent(str) { /***/ }), -/* 518 */ +/* 511 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -58815,7 +56566,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(302); +var isExtglob = __webpack_require__(299); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -58836,7 +56587,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 519 */ +/* 512 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58986,7 +56737,7 @@ module.exports.win32 = win32; /***/ }), -/* 520 */ +/* 513 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -58996,7 +56747,7 @@ module.exports.win32 = win32; * Released under the MIT License. */ -var isExtglob = __webpack_require__(302); +var isExtglob = __webpack_require__(299); var chars = { '{': '}', '(': ')', '[': ']'}; module.exports = function isGlob(str, options) { @@ -59038,7 +56789,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 521 */ +/* 514 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59049,18 +56800,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(111); -var braces = __webpack_require__(522); -var toRegex = __webpack_require__(635); -var extend = __webpack_require__(643); +var braces = __webpack_require__(515); +var toRegex = __webpack_require__(617); +var extend = __webpack_require__(625); /** * Local dependencies */ -var compilers = __webpack_require__(646); -var parsers = __webpack_require__(679); -var cache = __webpack_require__(680); -var utils = __webpack_require__(681); +var compilers = __webpack_require__(628); +var parsers = __webpack_require__(660); +var cache = __webpack_require__(661); +var utils = __webpack_require__(662); var MAX_LENGTH = 1024 * 64; /** @@ -59922,7 +57673,7 @@ module.exports = micromatch; /***/ }), -/* 522 */ +/* 515 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59932,18 +57683,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(523); -var unique = __webpack_require__(537); -var extend = __webpack_require__(532); +var toRegex = __webpack_require__(516); +var unique = __webpack_require__(528); +var extend = __webpack_require__(525); /** * Local dependencies */ -var compilers = __webpack_require__(538); -var parsers = __webpack_require__(555); -var Braces = __webpack_require__(565); -var utils = __webpack_require__(539); +var compilers = __webpack_require__(529); +var parsers = __webpack_require__(544); +var Braces = __webpack_require__(554); +var utils = __webpack_require__(530); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -60247,15 +57998,15 @@ module.exports = braces; /***/ }), -/* 523 */ +/* 516 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(524); -var extend = __webpack_require__(532); -var not = __webpack_require__(534); +var define = __webpack_require__(517); +var extend = __webpack_require__(525); +var not = __webpack_require__(527); var MAX_LENGTH = 1024 * 64; /** @@ -60402,7 +58153,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 524 */ +/* 517 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60415,7 +58166,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(525); +var isDescriptor = __webpack_require__(518); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -60440,7 +58191,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 525 */ +/* 518 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60453,9 +58204,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(526); -var isAccessor = __webpack_require__(527); -var isData = __webpack_require__(530); +var typeOf = __webpack_require__(519); +var isAccessor = __webpack_require__(520); +var isData = __webpack_require__(523); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -60469,7 +58220,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 526 */ +/* 519 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -60622,7 +58373,7 @@ function isBuffer(val) { /***/ }), -/* 527 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60635,7 +58386,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(528); +var typeOf = __webpack_require__(521); // accessor descriptor properties var accessor = { @@ -60698,10 +58449,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 528 */ +/* 521 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(529); +var isBuffer = __webpack_require__(522); var toString = Object.prototype.toString; /** @@ -60820,7 +58571,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 529 */ +/* 522 */ /***/ (function(module, exports) { /*! @@ -60847,7 +58598,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 530 */ +/* 523 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60860,7 +58611,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(531); +var typeOf = __webpack_require__(524); // data descriptor properties var data = { @@ -60909,10 +58660,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 531 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(529); +var isBuffer = __webpack_require__(522); var toString = Object.prototype.toString; /** @@ -61031,13 +58782,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 532 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(533); +var isObject = __webpack_require__(526); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -61071,7 +58822,7 @@ function hasOwn(obj, key) { /***/ }), -/* 533 */ +/* 526 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61091,13 +58842,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 534 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(535); +var extend = __webpack_require__(525); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -61164,67 +58915,7 @@ module.exports = toRegex; /***/ }), -/* 535 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isObject = __webpack_require__(536); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; - - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; - -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} - -/** - * Returns true if the given `key` is an own property of `obj`. - */ - -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - - -/***/ }), -/* 536 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 537 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61274,13 +58965,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 538 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(539); +var utils = __webpack_require__(530); module.exports = function(braces, options) { braces.compiler @@ -61563,25 +59254,25 @@ function hasQueue(node) { /***/ }), -/* 539 */ +/* 530 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(540); +var splitString = __webpack_require__(531); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(532); -utils.flatten = __webpack_require__(546); -utils.isObject = __webpack_require__(544); -utils.fillRange = __webpack_require__(547); -utils.repeat = __webpack_require__(554); -utils.unique = __webpack_require__(537); +utils.extend = __webpack_require__(525); +utils.flatten = __webpack_require__(537); +utils.isObject = __webpack_require__(535); +utils.fillRange = __webpack_require__(538); +utils.repeat = __webpack_require__(543); +utils.unique = __webpack_require__(528); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -61913,7 +59604,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 540 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61926,7 +59617,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(541); +var extend = __webpack_require__(532); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -62091,14 +59782,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 541 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(542); -var assignSymbols = __webpack_require__(545); +var isExtendable = __webpack_require__(533); +var assignSymbols = __webpack_require__(536); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -62158,7 +59849,7 @@ function isEnum(obj, key) { /***/ }), -/* 542 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62171,7 +59862,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(543); +var isPlainObject = __webpack_require__(534); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -62179,7 +59870,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 543 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62192,7 +59883,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(544); +var isObject = __webpack_require__(535); function isObjectObject(o) { return isObject(o) === true @@ -62223,7 +59914,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 544 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62242,7 +59933,7 @@ module.exports = function isObject(val) { /***/ }), -/* 545 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62289,7 +59980,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 546 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62318,7 +60009,7 @@ function flat(arr, res) { /***/ }), -/* 547 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62332,10 +60023,10 @@ function flat(arr, res) { var util = __webpack_require__(111); -var isNumber = __webpack_require__(548); -var extend = __webpack_require__(550); -var repeat = __webpack_require__(552); -var toRegex = __webpack_require__(553); +var isNumber = __webpack_require__(539); +var extend = __webpack_require__(525); +var repeat = __webpack_require__(541); +var toRegex = __webpack_require__(542); /** * Return a range of numbers or letters. @@ -62533,7 +60224,7 @@ module.exports = fillRange; /***/ }), -/* 548 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62546,7 +60237,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(549); +var typeOf = __webpack_require__(540); module.exports = function isNumber(num) { var type = typeOf(num); @@ -62562,10 +60253,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 549 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(529); +var isBuffer = __webpack_require__(522); var toString = Object.prototype.toString; /** @@ -62684,67 +60375,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 550 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isObject = __webpack_require__(551); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; - - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; - -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} - -/** - * Returns true if the given `key` is an own property of `obj`. - */ - -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - - -/***/ }), -/* 551 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 552 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62821,7 +60452,7 @@ function repeat(str, num) { /***/ }), -/* 553 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62834,8 +60465,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(552); -var isNumber = __webpack_require__(548); +var repeat = __webpack_require__(541); +var isNumber = __webpack_require__(539); var cache = {}; function toRegexRange(min, max, options) { @@ -63122,7 +60753,7 @@ module.exports = toRegexRange; /***/ }), -/* 554 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -63147,14 +60778,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 555 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(556); -var utils = __webpack_require__(539); +var Node = __webpack_require__(545); +var utils = __webpack_require__(530); /** * Braces parsers @@ -63514,15 +61145,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 556 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(544); -var define = __webpack_require__(557); -var utils = __webpack_require__(564); +var isObject = __webpack_require__(535); +var define = __webpack_require__(546); +var utils = __webpack_require__(553); var ownNames; /** @@ -64013,7 +61644,7 @@ exports = module.exports = Node; /***/ }), -/* 557 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64026,7 +61657,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(558); +var isDescriptor = __webpack_require__(547); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -64051,7 +61682,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 558 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64064,9 +61695,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(559); -var isAccessor = __webpack_require__(560); -var isData = __webpack_require__(562); +var typeOf = __webpack_require__(548); +var isAccessor = __webpack_require__(549); +var isData = __webpack_require__(551); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -64080,7 +61711,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 559 */ +/* 548 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64215,7 +61846,7 @@ function isBuffer(val) { /***/ }), -/* 560 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64228,7 +61859,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(561); +var typeOf = __webpack_require__(550); // accessor descriptor properties var accessor = { @@ -64291,7 +61922,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 561 */ +/* 550 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64426,7 +62057,7 @@ function isBuffer(val) { /***/ }), -/* 562 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64439,7 +62070,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(563); +var typeOf = __webpack_require__(552); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -64482,7 +62113,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 563 */ +/* 552 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -64617,13 +62248,13 @@ function isBuffer(val) { /***/ }), -/* 564 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(549); +var typeOf = __webpack_require__(540); var utils = module.exports; /** @@ -65643,17 +63274,17 @@ function assert(val, message) { /***/ }), -/* 565 */ +/* 554 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(532); -var Snapdragon = __webpack_require__(566); -var compilers = __webpack_require__(538); -var parsers = __webpack_require__(555); -var utils = __webpack_require__(539); +var extend = __webpack_require__(525); +var Snapdragon = __webpack_require__(555); +var compilers = __webpack_require__(529); +var parsers = __webpack_require__(544); +var utils = __webpack_require__(530); /** * Customize Snapdragon parser and renderer @@ -65754,17 +63385,17 @@ module.exports = Braces; /***/ }), -/* 566 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(567); -var define = __webpack_require__(593); -var Compiler = __webpack_require__(603); -var Parser = __webpack_require__(632); -var utils = __webpack_require__(612); +var Base = __webpack_require__(556); +var define = __webpack_require__(517); +var Compiler = __webpack_require__(585); +var Parser = __webpack_require__(614); +var utils = __webpack_require__(594); var regexCache = {}; var cache = {}; @@ -65935,20 +63566,20 @@ module.exports.Parser = Parser; /***/ }), -/* 567 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var define = __webpack_require__(568); -var CacheBase = __webpack_require__(569); -var Emitter = __webpack_require__(570); -var isObject = __webpack_require__(544); -var merge = __webpack_require__(587); -var pascal = __webpack_require__(590); -var cu = __webpack_require__(591); +var define = __webpack_require__(557); +var CacheBase = __webpack_require__(558); +var Emitter = __webpack_require__(559); +var isObject = __webpack_require__(535); +var merge = __webpack_require__(576); +var pascal = __webpack_require__(579); +var cu = __webpack_require__(580); /** * Optionally define a custom `cache` namespace to use. @@ -66377,7 +64008,7 @@ module.exports.namespace = namespace; /***/ }), -/* 568 */ +/* 557 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66390,7 +64021,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(558); +var isDescriptor = __webpack_require__(547); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -66415,21 +64046,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 569 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(544); -var Emitter = __webpack_require__(570); -var visit = __webpack_require__(571); -var toPath = __webpack_require__(574); -var union = __webpack_require__(575); -var del = __webpack_require__(579); -var get = __webpack_require__(577); -var has = __webpack_require__(584); -var set = __webpack_require__(578); +var isObject = __webpack_require__(535); +var Emitter = __webpack_require__(559); +var visit = __webpack_require__(560); +var toPath = __webpack_require__(563); +var union = __webpack_require__(564); +var del = __webpack_require__(568); +var get = __webpack_require__(566); +var has = __webpack_require__(573); +var set = __webpack_require__(567); /** * Create a `Cache` constructor that when instantiated will @@ -66683,7 +64314,7 @@ module.exports.namespace = namespace; /***/ }), -/* 570 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { @@ -66796,420 +64427,105 @@ Emitter.prototype.removeEventListener = function(event, fn){ cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); - break; - } - } - return this; -}; - -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks['$' + event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; -}; - -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks['$' + event] || []; -}; - -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; - - -/***/ }), -/* 571 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * collection-visit - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var visit = __webpack_require__(572); -var mapVisit = __webpack_require__(573); - -module.exports = function(collection, method, val) { - var result; - - if (typeof val === 'string' && (method in collection)) { - var args = [].slice.call(arguments, 2); - result = collection[method].apply(collection, args); - } else if (Array.isArray(val)) { - result = mapVisit.apply(null, arguments); - } else { - result = visit.apply(null, arguments); - } - - if (typeof result !== 'undefined') { - return result; - } - - return collection; -}; - - -/***/ }), -/* 572 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * object-visit - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var isObject = __webpack_require__(544); - -module.exports = function visit(thisArg, method, target, val) { - if (!isObject(thisArg) && typeof thisArg !== 'function') { - throw new Error('object-visit expects `thisArg` to be an object.'); - } - - if (typeof method !== 'string') { - throw new Error('object-visit expects `method` name to be a string'); - } - - if (typeof thisArg[method] !== 'function') { - return thisArg; - } - - var args = [].slice.call(arguments, 3); - target = target || {}; - - for (var key in target) { - var arr = [key, target[key]].concat(args); - thisArg[method].apply(thisArg, arr); - } - return thisArg; -}; - - -/***/ }), -/* 573 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(111); -var visit = __webpack_require__(572); - -/** - * Map `visit` over an array of objects. - * - * @param {Object} `collection` The context in which to invoke `method` - * @param {String} `method` Name of the method to call on `collection` - * @param {Object} `arr` Array of objects. - */ - -module.exports = function mapVisit(collection, method, val) { - if (isObject(val)) { - return visit.apply(null, arguments); - } - - if (!Array.isArray(val)) { - throw new TypeError('expected an array: ' + util.inspect(val)); - } - - var args = [].slice.call(arguments, 3); - - for (var i = 0; i < val.length; i++) { - var ele = val[i]; - if (isObject(ele)) { - visit.apply(null, [collection, method, ele].concat(args)); - } else { - collection[method].apply(collection, [ele].concat(args)); - } - } -}; - -function isObject(val) { - return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); -} - - -/***/ }), -/* 574 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * to-object-path - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var typeOf = __webpack_require__(549); - -module.exports = function toPath(args) { - if (typeOf(args) !== 'arguments') { - args = arguments; - } - return filter(args).join('.'); -}; - -function filter(arr) { - var len = arr.length; - var idx = -1; - var res = []; - - while (++idx < len) { - var ele = arr[idx]; - if (typeOf(ele) === 'arguments' || Array.isArray(ele)) { - res.push.apply(res, filter(ele)); - } else if (typeof ele === 'string') { - res.push(ele); - } - } - return res; -} - - -/***/ }), -/* 575 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isObject = __webpack_require__(536); -var union = __webpack_require__(576); -var get = __webpack_require__(577); -var set = __webpack_require__(578); - -module.exports = function unionValue(obj, prop, value) { - if (!isObject(obj)) { - throw new TypeError('union-value expects the first argument to be an object.'); - } - - if (typeof prop !== 'string') { - throw new TypeError('union-value expects `prop` to be a string.'); - } - - var arr = arrayify(get(obj, prop)); - set(obj, prop, union(arr, arrayify(value))); - return obj; -}; - -function arrayify(val) { - if (val === null || typeof val === 'undefined') { - return []; - } - if (Array.isArray(val)) { - return val; - } - return [val]; -} - - -/***/ }), -/* 576 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = function union(init) { - if (!Array.isArray(init)) { - throw new TypeError('arr-union expects the first argument to be an array.'); - } - - var len = arguments.length; - var i = 0; - - while (++i < len) { - var arg = arguments[i]; - if (!arg) continue; - - if (!Array.isArray(arg)) { - arg = [arg]; - } - - for (var j = 0; j < arg.length; j++) { - var ele = arg[j]; - - if (init.indexOf(ele) >= 0) { - continue; - } - init.push(ele); - } - } - return init; -}; - - -/***/ }), -/* 577 */ -/***/ (function(module, exports) { - -/*! - * get-value - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -module.exports = function(obj, prop, a, b, c) { - if (!isObject(obj) || !prop) { - return obj; - } - - prop = toString(prop); - - // allowing for multiple properties to be passed as - // a string or array, but much faster (3-4x) than doing - // `[].slice.call(arguments)` - if (a) prop += '.' + toString(a); - if (b) prop += '.' + toString(b); - if (c) prop += '.' + toString(c); - - if (prop in obj) { - return obj[prop]; - } - - var segs = prop.split('.'); - var len = segs.length; - var i = -1; - - while (obj && (++i < len)) { - var key = segs[i]; - while (key[key.length - 1] === '\\') { - key = key.slice(0, -1) + '.' + segs[++i]; - } - obj = obj[key]; - } - return obj; -}; - -function isObject(val) { - return val !== null && (typeof val === 'object' || typeof val === 'function'); -} - -function toString(val) { - if (!val) return ''; - if (Array.isArray(val)) { - return val.join('.'); - } - return val; -} + break; + } + } + return this; +}; + +/** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + +Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks['$' + event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +/** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + +Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; +}; + +/** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + +Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; +}; /***/ }), -/* 578 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * set-value + * collection-visit * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Copyright (c) 2015, 2017, Jon Schlinkert. * Released under the MIT License. */ -var split = __webpack_require__(540); -var extend = __webpack_require__(535); -var isPlainObject = __webpack_require__(543); -var isObject = __webpack_require__(536); - -module.exports = function(obj, prop, val) { - if (!isObject(obj)) { - return obj; - } +var visit = __webpack_require__(561); +var mapVisit = __webpack_require__(562); - if (Array.isArray(prop)) { - prop = [].concat.apply([], prop).join('.'); - } +module.exports = function(collection, method, val) { + var result; - if (typeof prop !== 'string') { - return obj; + if (typeof val === 'string' && (method in collection)) { + var args = [].slice.call(arguments, 2); + result = collection[method].apply(collection, args); + } else if (Array.isArray(val)) { + result = mapVisit.apply(null, arguments); + } else { + result = visit.apply(null, arguments); } - var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); - var len = keys.length; - var idx = -1; - var current = obj; - - while (++idx < len) { - var key = keys[idx]; - if (idx !== len - 1) { - if (!isObject(current[key])) { - current[key] = {}; - } - current = current[key]; - continue; - } - - if (isPlainObject(current[key]) && isPlainObject(val)) { - current[key] = extend({}, current[key], val); - } else { - current[key] = val; - } + if (typeof result !== 'undefined') { + return result; } - return obj; + return collection; }; -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; -} - /***/ }), -/* 579 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * unset-value + * object-visit * * Copyright (c) 2015, 2017, Jon Schlinkert. * Released under the MIT License. @@ -67217,960 +64533,542 @@ function isValidKey(key) { -var isObject = __webpack_require__(544); -var has = __webpack_require__(580); +var isObject = __webpack_require__(535); -module.exports = function unset(obj, prop) { - if (!isObject(obj)) { - throw new TypeError('expected an object.'); - } - if (obj.hasOwnProperty(prop)) { - delete obj[prop]; - return true; +module.exports = function visit(thisArg, method, target, val) { + if (!isObject(thisArg) && typeof thisArg !== 'function') { + throw new Error('object-visit expects `thisArg` to be an object.'); } - if (has(obj, prop)) { - var segs = prop.split('.'); - var last = segs.pop(); - while (segs.length && segs[segs.length - 1].slice(-1) === '\\') { - last = segs.pop().slice(0, -1) + '.' + last; - } - while (segs.length) obj = obj[prop = segs.shift()]; - return (delete obj[last]); + if (typeof method !== 'string') { + throw new Error('object-visit expects `method` name to be a string'); } - return true; -}; - - -/***/ }), -/* 580 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * has-value - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. - */ - + if (typeof thisArg[method] !== 'function') { + return thisArg; + } -var isObject = __webpack_require__(581); -var hasValues = __webpack_require__(583); -var get = __webpack_require__(577); + var args = [].slice.call(arguments, 3); + target = target || {}; -module.exports = function(obj, prop, noZero) { - if (isObject(obj)) { - return hasValues(get(obj, prop), noZero); + for (var key in target) { + var arr = [key, target[key]].concat(args); + thisArg[method].apply(thisArg, arr); } - return hasValues(obj, prop); + return thisArg; }; /***/ }), -/* 581 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * isobject - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var isArray = __webpack_require__(582); - -module.exports = function isObject(val) { - return val != null && typeof val === 'object' && isArray(val) === false; -}; - - -/***/ }), -/* 582 */ -/***/ (function(module, exports) { - -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; -/***/ }), -/* 583 */ -/***/ (function(module, exports, __webpack_require__) { +var util = __webpack_require__(111); +var visit = __webpack_require__(561); -"use strict"; -/*! - * has-values +/** + * Map `visit` over an array of objects. * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. + * @param {Object} `collection` The context in which to invoke `method` + * @param {String} `method` Name of the method to call on `collection` + * @param {Object} `arr` Array of objects. */ - - -module.exports = function hasValue(o, noZero) { - if (o === null || o === undefined) { - return false; - } - - if (typeof o === 'boolean') { - return true; +module.exports = function mapVisit(collection, method, val) { + if (isObject(val)) { + return visit.apply(null, arguments); } - if (typeof o === 'number') { - if (o === 0 && noZero === true) { - return false; - } - return true; + if (!Array.isArray(val)) { + throw new TypeError('expected an array: ' + util.inspect(val)); } - if (o.length !== undefined) { - return o.length !== 0; - } + var args = [].slice.call(arguments, 3); - for (var key in o) { - if (o.hasOwnProperty(key)) { - return true; + for (var i = 0; i < val.length; i++) { + var ele = val[i]; + if (isObject(ele)) { + visit.apply(null, [collection, method, ele].concat(args)); + } else { + collection[method].apply(collection, [ele].concat(args)); } } - return false; }; - -/***/ }), -/* 584 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * has-value - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var isObject = __webpack_require__(544); -var hasValues = __webpack_require__(585); -var get = __webpack_require__(577); - -module.exports = function(val, prop) { - return hasValues(isObject(val) && prop ? get(val, prop) : val); -}; +function isObject(val) { + return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); +} /***/ }), -/* 585 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * has-values - * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var typeOf = __webpack_require__(586); -var isNumber = __webpack_require__(548); - -module.exports = function hasValue(val) { - // is-number checks for NaN and other edge cases - if (isNumber(val)) { - return true; - } - - switch (typeOf(val)) { - case 'null': - case 'boolean': - case 'function': - return true; - case 'string': - case 'arguments': - return val.length !== 0; - case 'error': - return val.message !== ''; - case 'array': - var len = val.length; - if (len === 0) { - return false; - } - for (var i = 0; i < len; i++) { - if (hasValue(val[i])) { - return true; - } - } - return false; - case 'file': - case 'map': - case 'set': - return val.size !== 0; - case 'object': - var keys = Object.keys(val); - if (keys.length === 0) { - return false; - } - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (hasValue(val[key])) { - return true; - } - } - return false; - default: { - return false; - } - } -}; - - -/***/ }), -/* 586 */ -/***/ (function(module, exports, __webpack_require__) { - -var isBuffer = __webpack_require__(529); -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. + * to-object-path * - * @param {*} `val` - * @return {*} Native javascript type + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; - } - - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } - - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } - - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } - - // other objects - var type = toString.call(val); - - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } - if (type === '[object Promise]') { - return 'promise'; - } - // buffer - if (isBuffer(val)) { - return 'buffer'; - } - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; - } +var typeOf = __webpack_require__(540); - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; +module.exports = function toPath(args) { + if (typeOf(args) !== 'arguments') { + args = arguments; } - - // must be a plain object - return 'object'; + return filter(args).join('.'); }; +function filter(arr) { + var len = arr.length; + var idx = -1; + var res = []; -/***/ }), -/* 587 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isExtendable = __webpack_require__(588); -var forIn = __webpack_require__(589); - -function mixinDeep(target, objects) { - var len = arguments.length, i = 0; - while (++i < len) { - var obj = arguments[i]; - if (isObject(obj)) { - forIn(obj, copy, target); + while (++idx < len) { + var ele = arr[idx]; + if (typeOf(ele) === 'arguments' || Array.isArray(ele)) { + res.push.apply(res, filter(ele)); + } else if (typeof ele === 'string') { + res.push(ele); } - } - return target; -} - -/** - * Copy properties from the source object to the - * target object. - * - * @param {*} `val` - * @param {String} `key` - */ - -function copy(val, key) { - if (!isValidKey(key)) { - return; - } - - var obj = this[key]; - if (isObject(val) && isObject(obj)) { - mixinDeep(obj, val); - } else { - this[key] = val; - } -} - -/** - * Returns true if `val` is an object or function. - * - * @param {any} val - * @return {Boolean} - */ - -function isObject(val) { - return isExtendable(val) && !Array.isArray(val); -} - -/** - * Returns true if `key` is a valid key to use when extending objects. - * - * @param {String} `key` - * @return {Boolean} - */ - -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; -}; - -/** - * Expose `mixinDeep` - */ - -module.exports = mixinDeep; + } + return res; +} /***/ }), -/* 588 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ +var isObject = __webpack_require__(526); +var union = __webpack_require__(565); +var get = __webpack_require__(566); +var set = __webpack_require__(567); -var isPlainObject = __webpack_require__(543); +module.exports = function unionValue(obj, prop, value) { + if (!isObject(obj)) { + throw new TypeError('union-value expects the first argument to be an object.'); + } -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); + if (typeof prop !== 'string') { + throw new TypeError('union-value expects `prop` to be a string.'); + } + + var arr = arrayify(get(obj, prop)); + set(obj, prop, union(arr, arrayify(value))); + return obj; }; +function arrayify(val) { + if (val === null || typeof val === 'undefined') { + return []; + } + if (Array.isArray(val)) { + return val; + } + return [val]; +} + /***/ }), -/* 589 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * for-in - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ +module.exports = function union(init) { + if (!Array.isArray(init)) { + throw new TypeError('arr-union expects the first argument to be an array.'); + } -module.exports = function forIn(obj, fn, thisArg) { - for (var key in obj) { - if (fn.call(thisArg, obj[key], key, obj) === false) { - break; + var len = arguments.length; + var i = 0; + + while (++i < len) { + var arg = arguments[i]; + if (!arg) continue; + + if (!Array.isArray(arg)) { + arg = [arg]; + } + + for (var j = 0; j < arg.length; j++) { + var ele = arg[j]; + + if (init.indexOf(ele) >= 0) { + continue; + } + init.push(ele); } } + return init; }; /***/ }), -/* 590 */ +/* 566 */ /***/ (function(module, exports) { /*! - * pascalcase + * get-value * - * Copyright (c) 2015, Jon Schlinkert. + * Copyright (c) 2014-2015, Jon Schlinkert. * Licensed under the MIT License. */ -function pascalcase(str) { - if (typeof str !== 'string') { - throw new TypeError('expected a string.'); +module.exports = function(obj, prop, a, b, c) { + if (!isObject(obj) || !prop) { + return obj; } - str = str.replace(/([A-Z])/g, ' $1'); - if (str.length === 1) { return str.toUpperCase(); } - str = str.replace(/^[\W_]+|[\W_]+$/g, '').toLowerCase(); - str = str.charAt(0).toUpperCase() + str.slice(1); - return str.replace(/[\W_]+(\w|$)/g, function (_, ch) { - return ch.toUpperCase(); - }); -} - -module.exports = pascalcase; + prop = toString(prop); -/***/ }), -/* 591 */ -/***/ (function(module, exports, __webpack_require__) { + // allowing for multiple properties to be passed as + // a string or array, but much faster (3-4x) than doing + // `[].slice.call(arguments)` + if (a) prop += '.' + toString(a); + if (b) prop += '.' + toString(b); + if (c) prop += '.' + toString(c); -"use strict"; + if (prop in obj) { + return obj[prop]; + } + var segs = prop.split('.'); + var len = segs.length; + var i = -1; -var util = __webpack_require__(111); -var utils = __webpack_require__(592); + while (obj && (++i < len)) { + var key = segs[i]; + while (key[key.length - 1] === '\\') { + key = key.slice(0, -1) + '.' + segs[++i]; + } + obj = obj[key]; + } + return obj; +}; -/** - * Expose class utils - */ +function isObject(val) { + return val !== null && (typeof val === 'object' || typeof val === 'function'); +} -var cu = module.exports; +function toString(val) { + if (!val) return ''; + if (Array.isArray(val)) { + return val.join('.'); + } + return val; +} -/** - * Expose class utils: `cu` - */ -cu.isObject = function isObject(val) { - return utils.isObj(val) || typeof val === 'function'; -}; +/***/ }), +/* 567 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if an array has any of the given elements, or an - * object has any of the give keys. - * - * ```js - * cu.has(['a', 'b', 'c'], 'c'); - * //=> true - * - * cu.has(['a', 'b', 'c'], ['c', 'z']); - * //=> true +"use strict"; +/*! + * set-value * - * cu.has({a: 'b', c: 'd'}, ['c', 'z']); - * //=> true - * ``` - * @param {Object} `obj` - * @param {String|Array} `val` - * @return {Boolean} - * @api public + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -cu.has = function has(obj, val) { - val = cu.arrayify(val); - var len = val.length; - if (cu.isObject(obj)) { - for (var key in obj) { - if (val.indexOf(key) > -1) { - return true; - } - } - var keys = cu.nativeKeys(obj); - return cu.has(keys, val); +var split = __webpack_require__(531); +var extend = __webpack_require__(525); +var isPlainObject = __webpack_require__(534); +var isObject = __webpack_require__(526); + +module.exports = function(obj, prop, val) { + if (!isObject(obj)) { + return obj; } - if (Array.isArray(obj)) { - var arr = obj; - while (len--) { - if (arr.indexOf(val[len]) > -1) { - return true; - } - } - return false; + if (Array.isArray(prop)) { + prop = [].concat.apply([], prop).join('.'); } - throw new TypeError('expected an array or object.'); -}; + if (typeof prop !== 'string') { + return obj; + } -/** - * Returns true if an array or object has all of the given values. - * - * ```js - * cu.hasAll(['a', 'b', 'c'], 'c'); - * //=> true - * - * cu.hasAll(['a', 'b', 'c'], ['c', 'z']); - * //=> false - * - * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); - * //=> false - * ``` - * @param {Object|Array} `val` - * @param {String|Array} `values` - * @return {Boolean} - * @api public - */ + var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); + var len = keys.length; + var idx = -1; + var current = obj; -cu.hasAll = function hasAll(val, values) { - values = cu.arrayify(values); - var len = values.length; - while (len--) { - if (!cu.has(val, values[len])) { - return false; + while (++idx < len) { + var key = keys[idx]; + if (idx !== len - 1) { + if (!isObject(current[key])) { + current[key] = {}; + } + current = current[key]; + continue; } - } - return true; -}; -/** - * Cast the given value to an array. - * - * ```js - * cu.arrayify('foo'); - * //=> ['foo'] - * - * cu.arrayify(['foo']); - * //=> ['foo'] - * ``` - * - * @param {String|Array} `val` - * @return {Array} - * @api public - */ + if (isPlainObject(current[key]) && isPlainObject(val)) { + current[key] = extend({}, current[key], val); + } else { + current[key] = val; + } + } -cu.arrayify = function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; + return obj; }; -/** - * Noop - */ - -cu.noop = function noop() { - return; -}; +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +} -/** - * Returns the first argument passed to the function. - */ -cu.identity = function identity(val) { - return val; -}; +/***/ }), +/* 568 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if a value has a `contructor` - * - * ```js - * cu.hasConstructor({}); - * //=> true +"use strict"; +/*! + * unset-value * - * cu.hasConstructor(Object.create(null)); - * //=> false - * ``` - * @param {Object} `value` - * @return {Boolean} - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -cu.hasConstructor = function hasConstructor(val) { - return cu.isObject(val) && typeof val.constructor !== 'undefined'; -}; - -/** - * Get the native `ownPropertyNames` from the constructor of the - * given `object`. An empty array is returned if the object does - * not have a constructor. - * - * ```js - * cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) - * //=> ['a', 'b', 'c'] - * - * cu.nativeKeys(function(){}) - * //=> ['length', 'caller'] - * ``` - * - * @param {Object} `obj` Object that has a `constructor`. - * @return {Array} Array of keys. - * @api public - */ -cu.nativeKeys = function nativeKeys(val) { - if (!cu.hasConstructor(val)) return []; - return Object.getOwnPropertyNames(val); -}; -/** - * Returns property descriptor `key` if it's an "own" property - * of the given object. - * - * ```js - * function App() {} - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this).length; - * } - * }); - * cu.getDescriptor(App.prototype, 'count'); - * // returns: - * // { - * // get: [Function], - * // set: undefined, - * // enumerable: false, - * // configurable: false - * // } - * ``` - * - * @param {Object} `obj` - * @param {String} `key` - * @return {Object} Returns descriptor `key` - * @api public - */ +var isObject = __webpack_require__(535); +var has = __webpack_require__(569); -cu.getDescriptor = function getDescriptor(obj, key) { - if (!cu.isObject(obj)) { +module.exports = function unset(obj, prop) { + if (!isObject(obj)) { throw new TypeError('expected an object.'); } - if (typeof key !== 'string') { - throw new TypeError('expected key to be a string.'); - } - return Object.getOwnPropertyDescriptor(obj, key); -}; - -/** - * Copy a descriptor from one object to another. - * - * ```js - * function App() {} - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this).length; - * } - * }); - * var obj = {}; - * cu.copyDescriptor(obj, App.prototype, 'count'); - * ``` - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String} `name` - * @return {Object} - * @api public - */ - -cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } - if (typeof name !== 'string') { - throw new TypeError('expected name to be a string.'); - } - - var val = cu.getDescriptor(provider, name); - if (val) Object.defineProperty(receiver, name, val); -}; - -/** - * Copy static properties, prototype properties, and descriptors - * from one object to another. - * - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public - */ - -cu.copy = function copy(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); + if (obj.hasOwnProperty(prop)) { + delete obj[prop]; + return true; } - var props = Object.getOwnPropertyNames(provider); - var keys = Object.keys(provider); - var len = props.length, - key; - omit = cu.arrayify(omit); - - while (len--) { - key = props[len]; - if (cu.has(keys, key)) { - utils.define(receiver, key, provider[key]); - } else if (!(key in receiver) && !cu.has(omit, key)) { - cu.copyDescriptor(receiver, provider, key); + if (has(obj, prop)) { + var segs = prop.split('.'); + var last = segs.pop(); + while (segs.length && segs[segs.length - 1].slice(-1) === '\\') { + last = segs.pop().slice(0, -1) + '.' + last; } + while (segs.length) obj = obj[prop = segs.shift()]; + return (delete obj[last]); } + return true; }; -/** - * Inherit the static properties, prototype properties, and descriptors - * from of an object. - * - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public - */ - -cu.inherit = function inherit(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } - - var keys = []; - for (var key in provider) { - keys.push(key); - receiver[key] = provider[key]; - } - - keys = keys.concat(cu.arrayify(omit)); - - var a = provider.prototype || provider; - var b = receiver.prototype || receiver; - cu.copy(b, a, keys); -}; - -/** - * Returns a function for extending the static properties, - * prototype properties, and descriptors from the `Parent` - * constructor onto `Child` constructors. - * - * ```js - * var extend = cu.extend(Parent); - * Parent.extend(Child); - * - * // optional methods - * Parent.extend(Child, { - * foo: function() {}, - * bar: function() {} - * }); - * ``` - * @param {Function} `Parent` Parent ctor - * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. - * @param {Function} `Child` Child ctor - * @param {Object} `proto` Optionally pass additional prototype properties to inherit. - * @return {Object} - * @api public - */ - -cu.extend = function() { - // keep it lazy, instead of assigning to `cu.extend` - return utils.staticExtend.apply(null, arguments); -}; - -/** - * Bubble up events emitted from static methods on the Parent ctor. - * - * @param {Object} `Parent` - * @param {Array} `events` Event names to bubble up - * @api public - */ -cu.bubble = function(Parent, events) { - events = events || []; - Parent.bubble = function(Child, arr) { - if (Array.isArray(arr)) { - events = utils.union([], events, arr); - } - var len = events.length; - var idx = -1; - while (++idx < len) { - var name = events[idx]; - Parent.on(name, Child.emit.bind(Child, name)); - } - cu.bubble(Child, events); - }; +/***/ }), +/* 569 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * has-value + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + */ + + + +var isObject = __webpack_require__(570); +var hasValues = __webpack_require__(572); +var get = __webpack_require__(566); + +module.exports = function(obj, prop, noZero) { + if (isObject(obj)) { + return hasValues(get(obj, prop), noZero); + } + return hasValues(obj, prop); }; /***/ }), -/* 592 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * isobject + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ -var utils = {}; - +var isArray = __webpack_require__(571); -/** - * Lazily required module dependencies - */ +module.exports = function isObject(val) { + return val != null && typeof val === 'object' && isArray(val) === false; +}; -utils.union = __webpack_require__(576); -utils.define = __webpack_require__(593); -utils.isObj = __webpack_require__(544); -utils.staticExtend = __webpack_require__(600); +/***/ }), +/* 571 */ +/***/ (function(module, exports) { -/** - * Expose `utils` - */ +var toString = {}.toString; -module.exports = utils; +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; /***/ }), -/* 593 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * define-property + * has-values * - * Copyright (c) 2015, Jon Schlinkert. + * Copyright (c) 2014-2015, Jon Schlinkert. * Licensed under the MIT License. */ -var isDescriptor = __webpack_require__(594); +module.exports = function hasValue(o, noZero) { + if (o === null || o === undefined) { + return false; + } -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); + if (typeof o === 'boolean') { + return true; } - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); + if (typeof o === 'number') { + if (o === 0 && noZero === true) { + return false; + } + return true; } - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); + if (o.length !== undefined) { + return o.length !== 0; } - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); + for (var key in o) { + if (o.hasOwnProperty(key)) { + return true; + } + } + return false; }; /***/ }), -/* 594 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-descriptor + * has-value * - * Copyright (c) 2015-2017, Jon Schlinkert. + * Copyright (c) 2014-2017, Jon Schlinkert. + * Licensed under the MIT License. + */ + + + +var isObject = __webpack_require__(535); +var hasValues = __webpack_require__(574); +var get = __webpack_require__(566); + +module.exports = function(val, prop) { + return hasValues(isObject(val) && prop ? get(val, prop) : val); +}; + + +/***/ }), +/* 574 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * has-values + * + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. * Released under the MIT License. */ -var typeOf = __webpack_require__(595); -var isAccessor = __webpack_require__(596); -var isData = __webpack_require__(598); +var typeOf = __webpack_require__(575); +var isNumber = __webpack_require__(539); -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; +module.exports = function hasValue(val) { + // is-number checks for NaN and other edge cases + if (isNumber(val)) { + return true; } - if ('get' in obj) { - return isAccessor(obj, key); + + switch (typeOf(val)) { + case 'null': + case 'boolean': + case 'function': + return true; + case 'string': + case 'arguments': + return val.length !== 0; + case 'error': + return val.message !== ''; + case 'array': + var len = val.length; + if (len === 0) { + return false; + } + for (var i = 0; i < len; i++) { + if (hasValue(val[i])) { + return true; + } + } + return false; + case 'file': + case 'map': + case 'set': + return val.size !== 0; + case 'object': + var keys = Object.keys(val); + if (keys.length === 0) { + return false; + } + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (hasValue(val[key])) { + return true; + } + } + return false; + default: { + return false; + } } - return isData(obj, key); }; /***/ }), -/* 595 */ -/***/ (function(module, exports) { +/* 575 */ +/***/ (function(module, exports, __webpack_require__) { +var isBuffer = __webpack_require__(522); var toString = Object.prototype.toString; /** @@ -68181,10 +65079,8 @@ var toString = Object.prototype.toString; */ module.exports = function kindOf(val) { - var type = typeof val; - // primitivies - if (type === 'undefined') { + if (typeof val === 'undefined') { return 'undefined'; } if (val === null) { @@ -68193,18 +65089,15 @@ module.exports = function kindOf(val) { if (val === true || val === false || val instanceof Boolean) { return 'boolean'; } - if (type === 'string' || val instanceof String) { + if (typeof val === 'string' || val instanceof String) { return 'string'; } - if (type === 'number' || val instanceof Number) { + if (typeof val === 'number' || val instanceof Number) { return 'number'; } // functions - if (type === 'function' || val instanceof Function) { - if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { - return 'generatorfunction'; - } + if (typeof val === 'function' || val instanceof Function) { return 'function'; } @@ -68222,7 +65115,7 @@ module.exports = function kindOf(val) { } // other objects - type = toString.call(val); + var type = toString.call(val); if (type === '[object RegExp]') { return 'regexp'; @@ -68261,20 +65154,7 @@ module.exports = function kindOf(val) { if (type === '[object Symbol]') { return 'symbol'; } - - if (type === '[object Map Iterator]') { - return 'mapiterator'; - } - if (type === '[object Set Iterator]') { - return 'setiterator'; - } - if (type === '[object String Iterator]') { - return 'stringiterator'; - } - if (type === '[object Array Iterator]') { - return 'arrayiterator'; - } - + // typed arrays if (type === '[object Int8Array]') { return 'int8array'; @@ -68308,402 +65188,551 @@ module.exports = function kindOf(val) { return 'object'; }; + +/***/ }), +/* 576 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isExtendable = __webpack_require__(577); +var forIn = __webpack_require__(578); + +function mixinDeep(target, objects) { + var len = arguments.length, i = 0; + while (++i < len) { + var obj = arguments[i]; + if (isObject(obj)) { + forIn(obj, copy, target); + } + } + return target; +} + /** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer + * Copy properties from the source object to the + * target object. + * + * @param {*} `val` + * @param {String} `key` */ -function isBuffer(val) { - return val.constructor - && typeof val.constructor.isBuffer === 'function' - && val.constructor.isBuffer(val); +function copy(val, key) { + if (!isValidKey(key)) { + return; + } + + var obj = this[key]; + if (isObject(val) && isObject(obj)) { + mixinDeep(obj, val); + } else { + this[key] = val; + } +} + +/** + * Returns true if `val` is an object or function. + * + * @param {any} val + * @return {Boolean} + */ + +function isObject(val) { + return isExtendable(val) && !Array.isArray(val); } +/** + * Returns true if `key` is a valid key to use when extending objects. + * + * @param {String} `key` + * @return {Boolean} + */ + +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +}; + +/** + * Expose `mixinDeep` + */ + +module.exports = mixinDeep; + /***/ }), -/* 596 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-accessor-descriptor + * is-extendable * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var typeOf = __webpack_require__(597); +var isPlainObject = __webpack_require__(534); -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -function isAccessorDescriptor(obj, prop) { - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } - if (typeOf(obj) !== 'object') { - return false; +/***/ }), +/* 578 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * for-in + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + + + +module.exports = function forIn(obj, fn, thisArg) { + for (var key in obj) { + if (fn.call(thisArg, obj[key], key, obj) === false) { + break; + } } +}; - if (has(obj, 'value') || has(obj, 'writable')) { - return false; + +/***/ }), +/* 579 */ +/***/ (function(module, exports) { + +/*! + * pascalcase + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +function pascalcase(str) { + if (typeof str !== 'string') { + throw new TypeError('expected a string.'); } + str = str.replace(/([A-Z])/g, ' $1'); + if (str.length === 1) { return str.toUpperCase(); } + str = str.replace(/^[\W_]+|[\W_]+$/g, '').toLowerCase(); + str = str.charAt(0).toUpperCase() + str.slice(1); + return str.replace(/[\W_]+(\w|$)/g, function (_, ch) { + return ch.toUpperCase(); + }); +} - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; +module.exports = pascalcase; + + +/***/ }), +/* 580 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var util = __webpack_require__(111); +var utils = __webpack_require__(581); + +/** + * Expose class utils + */ + +var cu = module.exports; + +/** + * Expose class utils: `cu` + */ + +cu.isObject = function isObject(val) { + return utils.isObj(val) || typeof val === 'function'; +}; + +/** + * Returns true if an array has any of the given elements, or an + * object has any of the give keys. + * + * ```js + * cu.has(['a', 'b', 'c'], 'c'); + * //=> true + * + * cu.has(['a', 'b', 'c'], ['c', 'z']); + * //=> true + * + * cu.has({a: 'b', c: 'd'}, ['c', 'z']); + * //=> true + * ``` + * @param {Object} `obj` + * @param {String|Array} `val` + * @return {Boolean} + * @api public + */ + +cu.has = function has(obj, val) { + val = cu.arrayify(val); + var len = val.length; + + if (cu.isObject(obj)) { + for (var key in obj) { + if (val.indexOf(key) > -1) { + return true; + } + } + + var keys = cu.nativeKeys(obj); + return cu.has(keys, val); } - // 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') { + if (Array.isArray(obj)) { + var arr = obj; + while (len--) { + if (arr.indexOf(val[len]) > -1) { + return true; + } + } return false; } - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } + throw new TypeError('expected an array or object.'); +}; - if (typeOf(obj[key]) === accessor[key]) { - continue; - } +/** + * Returns true if an array or object has all of the given values. + * + * ```js + * cu.hasAll(['a', 'b', 'c'], 'c'); + * //=> true + * + * cu.hasAll(['a', 'b', 'c'], ['c', 'z']); + * //=> false + * + * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); + * //=> false + * ``` + * @param {Object|Array} `val` + * @param {String|Array} `values` + * @return {Boolean} + * @api public + */ - if (typeof obj[key] !== 'undefined') { +cu.hasAll = function hasAll(val, values) { + values = cu.arrayify(values); + var len = values.length; + while (len--) { + if (!cu.has(val, values[len])) { return false; } } return true; -} +}; -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} +/** + * Cast the given value to an array. + * + * ```js + * cu.arrayify('foo'); + * //=> ['foo'] + * + * cu.arrayify(['foo']); + * //=> ['foo'] + * ``` + * + * @param {String|Array} `val` + * @return {Array} + * @api public + */ + +cu.arrayify = function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +}; /** - * Expose `isAccessorDescriptor` + * Noop */ -module.exports = isAccessorDescriptor; +cu.noop = function noop() { + return; +}; +/** + * Returns the first argument passed to the function. + */ -/***/ }), -/* 597 */ -/***/ (function(module, exports, __webpack_require__) { +cu.identity = function identity(val) { + return val; +}; + +/** + * Returns true if a value has a `contructor` + * + * ```js + * cu.hasConstructor({}); + * //=> true + * + * cu.hasConstructor(Object.create(null)); + * //=> false + * ``` + * @param {Object} `value` + * @return {Boolean} + * @api public + */ + +cu.hasConstructor = function hasConstructor(val) { + return cu.isObject(val) && typeof val.constructor !== 'undefined'; +}; + +/** + * Get the native `ownPropertyNames` from the constructor of the + * given `object`. An empty array is returned if the object does + * not have a constructor. + * + * ```js + * cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) + * //=> ['a', 'b', 'c'] + * + * cu.nativeKeys(function(){}) + * //=> ['length', 'caller'] + * ``` + * + * @param {Object} `obj` Object that has a `constructor`. + * @return {Array} Array of keys. + * @api public + */ -var isBuffer = __webpack_require__(529); -var toString = Object.prototype.toString; +cu.nativeKeys = function nativeKeys(val) { + if (!cu.hasConstructor(val)) return []; + return Object.getOwnPropertyNames(val); +}; /** - * Get the native `typeof` a value. + * Returns property descriptor `key` if it's an "own" property + * of the given object. * - * @param {*} `val` - * @return {*} Native javascript type + * ```js + * function App() {} + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this).length; + * } + * }); + * cu.getDescriptor(App.prototype, 'count'); + * // returns: + * // { + * // get: [Function], + * // set: undefined, + * // enumerable: false, + * // configurable: false + * // } + * ``` + * + * @param {Object} `obj` + * @param {String} `key` + * @return {Object} Returns descriptor `key` + * @api public */ -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; +cu.getDescriptor = function getDescriptor(obj, key) { + if (!cu.isObject(obj)) { + throw new TypeError('expected an object.'); } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; + if (typeof key !== 'string') { + throw new TypeError('expected key to be a string.'); } + return Object.getOwnPropertyDescriptor(obj, key); +}; - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } +/** + * Copy a descriptor from one object to another. + * + * ```js + * function App() {} + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this).length; + * } + * }); + * var obj = {}; + * cu.copyDescriptor(obj, App.prototype, 'count'); + * ``` + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String} `name` + * @return {Object} + * @api public + */ - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; +cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); } - - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); } - if (val instanceof Date) { - return 'date'; + if (typeof name !== 'string') { + throw new TypeError('expected name to be a string.'); } - // other objects - var type = toString.call(val); - - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } + var val = cu.getDescriptor(provider, name); + if (val) Object.defineProperty(receiver, name, val); +}; - // buffer - if (isBuffer(val)) { - return 'buffer'; - } +/** + * Copy static properties, prototype properties, and descriptors + * from one object to another. + * + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public + */ - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; +cu.copy = function copy(receiver, provider, omit) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); } - if (type === '[object Symbol]') { - return 'symbol'; + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); } + var props = Object.getOwnPropertyNames(provider); + var keys = Object.keys(provider); + var len = props.length, + key; + omit = cu.arrayify(omit); - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; - } + while (len--) { + key = props[len]; - // must be a plain object - return 'object'; + if (cu.has(keys, key)) { + utils.define(receiver, key, provider[key]); + } else if (!(key in receiver) && !cu.has(omit, key)) { + cu.copyDescriptor(receiver, provider, key); + } + } }; - -/***/ }), -/* 598 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-data-descriptor +/** + * Inherit the static properties, prototype properties, and descriptors + * from of an object. * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public */ - - -var typeOf = __webpack_require__(599); - -// data descriptor properties -var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' -}; - -function isDataDescriptor(obj, prop) { - if (typeOf(obj) !== 'object') { - return false; +cu.inherit = function inherit(receiver, provider, omit) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); } - - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); } - if (!('value' in obj) && !('writable' in obj)) { - return false; + var keys = []; + for (var key in provider) { + keys.push(key); + receiver[key] = provider[key]; } - for (var key in obj) { - if (key === 'value') continue; + keys = keys.concat(cu.arrayify(omit)); - if (!data.hasOwnProperty(key)) { - continue; - } + var a = provider.prototype || provider; + var b = receiver.prototype || receiver; + cu.copy(b, a, keys); +}; - if (typeOf(obj[key]) === data[key]) { - continue; - } +/** + * Returns a function for extending the static properties, + * prototype properties, and descriptors from the `Parent` + * constructor onto `Child` constructors. + * + * ```js + * var extend = cu.extend(Parent); + * Parent.extend(Child); + * + * // optional methods + * Parent.extend(Child, { + * foo: function() {}, + * bar: function() {} + * }); + * ``` + * @param {Function} `Parent` Parent ctor + * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. + * @param {Function} `Child` Child ctor + * @param {Object} `proto` Optionally pass additional prototype properties to inherit. + * @return {Object} + * @api public + */ - if (typeof obj[key] !== 'undefined') { - return false; - } - } - return true; -} +cu.extend = function() { + // keep it lazy, instead of assigning to `cu.extend` + return utils.staticExtend.apply(null, arguments); +}; /** - * Expose `isDataDescriptor` + * Bubble up events emitted from static methods on the Parent ctor. + * + * @param {Object} `Parent` + * @param {Array} `events` Event names to bubble up + * @api public */ -module.exports = isDataDescriptor; +cu.bubble = function(Parent, events) { + events = events || []; + Parent.bubble = function(Child, arr) { + if (Array.isArray(arr)) { + events = utils.union([], events, arr); + } + var len = events.length; + var idx = -1; + while (++idx < len) { + var name = events[idx]; + Parent.on(name, Child.emit.bind(Child, name)); + } + cu.bubble(Child, events); + }; +}; /***/ }), -/* 599 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(529); -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. - * - * @param {*} `val` - * @return {*} Native javascript type - */ - -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; - } +"use strict"; - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } +var utils = {}; - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } - // other objects - var type = toString.call(val); - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } +/** + * Lazily required module dependencies + */ - // buffer - if (isBuffer(val)) { - return 'buffer'; - } +utils.union = __webpack_require__(565); +utils.define = __webpack_require__(517); +utils.isObj = __webpack_require__(535); +utils.staticExtend = __webpack_require__(582); - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; - } - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; - } +/** + * Expose `utils` + */ - // must be a plain object - return 'object'; -}; +module.exports = utils; /***/ }), -/* 600 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68716,8 +65745,8 @@ module.exports = function kindOf(val) { -var copy = __webpack_require__(601); -var define = __webpack_require__(593); +var copy = __webpack_require__(583); +var define = __webpack_require__(517); var util = __webpack_require__(111); /** @@ -68800,15 +65829,15 @@ module.exports = extend; /***/ }), -/* 601 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(549); -var copyDescriptor = __webpack_require__(602); -var define = __webpack_require__(593); +var typeOf = __webpack_require__(540); +var copyDescriptor = __webpack_require__(584); +var define = __webpack_require__(517); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -68981,7 +66010,7 @@ module.exports.has = has; /***/ }), -/* 602 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69069,16 +66098,16 @@ function isObject(val) { /***/ }), -/* 603 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(604); -var define = __webpack_require__(593); -var debug = __webpack_require__(606)('snapdragon:compiler'); -var utils = __webpack_require__(612); +var use = __webpack_require__(586); +var define = __webpack_require__(517); +var debug = __webpack_require__(588)('snapdragon:compiler'); +var utils = __webpack_require__(594); /** * Create a new `Compiler` with the given `options`. @@ -69232,7 +66261,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(631); + var sourcemaps = __webpack_require__(613); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -69253,7 +66282,7 @@ module.exports = Compiler; /***/ }), -/* 604 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69266,7 +66295,7 @@ module.exports = Compiler; -var utils = __webpack_require__(605); +var utils = __webpack_require__(587); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -69381,7 +66410,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 605 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69395,8 +66424,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(593); -utils.isObject = __webpack_require__(544); +utils.define = __webpack_require__(517); +utils.isObject = __webpack_require__(535); utils.isString = function(val) { @@ -69411,7 +66440,7 @@ module.exports = utils; /***/ }), -/* 606 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -69420,14 +66449,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(607); + module.exports = __webpack_require__(589); } else { - module.exports = __webpack_require__(610); + module.exports = __webpack_require__(592); } /***/ }), -/* 607 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -69436,7 +66465,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(608); +exports = module.exports = __webpack_require__(590); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -69618,7 +66647,7 @@ function localstorage() { /***/ }), -/* 608 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { @@ -69634,7 +66663,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(609); +exports.humanize = __webpack_require__(591); /** * The currently active debug mode names, and names to skip. @@ -69826,7 +66855,7 @@ function coerce(val) { /***/ }), -/* 609 */ +/* 591 */ /***/ (function(module, exports) { /** @@ -69984,7 +67013,7 @@ function plural(ms, n, name) { /***/ }), -/* 610 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -70000,7 +67029,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(608); +exports = module.exports = __webpack_require__(590); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -70179,7 +67208,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(611); + var net = __webpack_require__(593); stream = new net.Socket({ fd: fd, readable: false, @@ -70238,13 +67267,13 @@ exports.enable(load()); /***/ }), -/* 611 */ +/* 593 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 612 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70254,9 +67283,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(535); -exports.SourceMap = __webpack_require__(613); -exports.sourceMapResolve = __webpack_require__(624); +exports.extend = __webpack_require__(525); +exports.SourceMap = __webpack_require__(595); +exports.sourceMapResolve = __webpack_require__(606); /** * Convert backslash in the given string to forward slashes @@ -70299,7 +67328,7 @@ exports.last = function(arr, n) { /***/ }), -/* 613 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -70307,13 +67336,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__(614).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(620).SourceMapConsumer; -exports.SourceNode = __webpack_require__(623).SourceNode; +exports.SourceMapGenerator = __webpack_require__(596).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(602).SourceMapConsumer; +exports.SourceNode = __webpack_require__(605).SourceNode; /***/ }), -/* 614 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70323,10 +67352,10 @@ exports.SourceNode = __webpack_require__(623).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(615); -var util = __webpack_require__(617); -var ArraySet = __webpack_require__(618).ArraySet; -var MappingList = __webpack_require__(619).MappingList; +var base64VLQ = __webpack_require__(597); +var util = __webpack_require__(599); +var ArraySet = __webpack_require__(600).ArraySet; +var MappingList = __webpack_require__(601).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -70735,7 +67764,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 615 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70775,7 +67804,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(616); +var base64 = __webpack_require__(598); // 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, @@ -70881,7 +67910,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 616 */ +/* 598 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -70954,7 +67983,7 @@ exports.decode = function (charCode) { /***/ }), -/* 617 */ +/* 599 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71377,7 +68406,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 618 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71387,7 +68416,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(617); +var util = __webpack_require__(599); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -71504,7 +68533,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 619 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71514,7 +68543,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(617); +var util = __webpack_require__(599); /** * Determine whether mappingB is after mappingA with respect to generated @@ -71589,7 +68618,7 @@ exports.MappingList = MappingList; /***/ }), -/* 620 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -71599,11 +68628,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(617); -var binarySearch = __webpack_require__(621); -var ArraySet = __webpack_require__(618).ArraySet; -var base64VLQ = __webpack_require__(615); -var quickSort = __webpack_require__(622).quickSort; +var util = __webpack_require__(599); +var binarySearch = __webpack_require__(603); +var ArraySet = __webpack_require__(600).ArraySet; +var base64VLQ = __webpack_require__(597); +var quickSort = __webpack_require__(604).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -72677,7 +69706,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 621 */ +/* 603 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72794,7 +69823,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 622 */ +/* 604 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72914,7 +69943,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 623 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72924,8 +69953,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(614).SourceMapGenerator; -var util = __webpack_require__(617); +var SourceMapGenerator = __webpack_require__(596).SourceMapGenerator; +var util = __webpack_require__(599); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -73333,17 +70362,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 624 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(625) -var resolveUrl = __webpack_require__(626) -var decodeUriComponent = __webpack_require__(627) -var urix = __webpack_require__(629) -var atob = __webpack_require__(630) +var sourceMappingURL = __webpack_require__(607) +var resolveUrl = __webpack_require__(608) +var decodeUriComponent = __webpack_require__(609) +var urix = __webpack_require__(611) +var atob = __webpack_require__(612) @@ -73641,7 +70670,7 @@ module.exports = { /***/ }), -/* 625 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -73704,7 +70733,7 @@ void (function(root, factory) { /***/ }), -/* 626 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -73722,13 +70751,13 @@ module.exports = resolveUrl /***/ }), -/* 627 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(628) +var decodeUriComponent = __webpack_require__(610) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -73739,7 +70768,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 628 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73840,7 +70869,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 629 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -73863,7 +70892,7 @@ module.exports = urix /***/ }), -/* 630 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73877,7 +70906,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 631 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73885,8 +70914,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(133); var path = __webpack_require__(4); -var define = __webpack_require__(593); -var utils = __webpack_require__(612); +var define = __webpack_require__(517); +var utils = __webpack_require__(594); /** * Expose `mixin()`. @@ -74029,19 +71058,19 @@ exports.comment = function(node) { /***/ }), -/* 632 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(604); +var use = __webpack_require__(586); var util = __webpack_require__(111); -var Cache = __webpack_require__(633); -var define = __webpack_require__(593); -var debug = __webpack_require__(606)('snapdragon:parser'); -var Position = __webpack_require__(634); -var utils = __webpack_require__(612); +var Cache = __webpack_require__(615); +var define = __webpack_require__(517); +var debug = __webpack_require__(588)('snapdragon:parser'); +var Position = __webpack_require__(616); +var utils = __webpack_require__(594); /** * Create a new `Parser` with the given `input` and `options`. @@ -74569,7 +71598,7 @@ module.exports = Parser; /***/ }), -/* 633 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74676,13 +71705,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 634 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(593); +var define = __webpack_require__(517); /** * Store position for a node @@ -74697,16 +71726,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 635 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(636); -var define = __webpack_require__(642); -var extend = __webpack_require__(643); -var not = __webpack_require__(645); +var safe = __webpack_require__(618); +var define = __webpack_require__(624); +var extend = __webpack_require__(625); +var not = __webpack_require__(627); var MAX_LENGTH = 1024 * 64; /** @@ -74859,10 +71888,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 636 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(637); +var parse = __webpack_require__(619); var types = parse.types; module.exports = function (re, opts) { @@ -74908,13 +71937,13 @@ function isRegExp (x) { /***/ }), -/* 637 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(638); -var types = __webpack_require__(639); -var sets = __webpack_require__(640); -var positions = __webpack_require__(641); +var util = __webpack_require__(620); +var types = __webpack_require__(621); +var sets = __webpack_require__(622); +var positions = __webpack_require__(623); module.exports = function(regexpStr) { @@ -75196,11 +72225,11 @@ module.exports.types = types; /***/ }), -/* 638 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(639); -var sets = __webpack_require__(640); +var types = __webpack_require__(621); +var sets = __webpack_require__(622); // All of these are private and only used by randexp. @@ -75313,7 +72342,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 639 */ +/* 621 */ /***/ (function(module, exports) { module.exports = { @@ -75329,10 +72358,10 @@ module.exports = { /***/ }), -/* 640 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(639); +var types = __webpack_require__(621); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -75417,10 +72446,10 @@ exports.anyChar = function() { /***/ }), -/* 641 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(639); +var types = __webpack_require__(621); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -75440,7 +72469,7 @@ exports.end = function() { /***/ }), -/* 642 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75453,8 +72482,8 @@ exports.end = function() { -var isobject = __webpack_require__(544); -var isDescriptor = __webpack_require__(558); +var isobject = __webpack_require__(535); +var isDescriptor = __webpack_require__(547); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -75485,14 +72514,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 643 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(644); -var assignSymbols = __webpack_require__(545); +var isExtendable = __webpack_require__(626); +var assignSymbols = __webpack_require__(536); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -75552,7 +72581,7 @@ function isEnum(obj, key) { /***/ }), -/* 644 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75565,7 +72594,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(543); +var isPlainObject = __webpack_require__(534); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -75573,14 +72602,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 645 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(643); -var safe = __webpack_require__(636); +var extend = __webpack_require__(625); +var safe = __webpack_require__(618); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -75652,14 +72681,14 @@ module.exports = toRegex; /***/ }), -/* 646 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(647); -var extglob = __webpack_require__(663); +var nanomatch = __webpack_require__(629); +var extglob = __webpack_require__(644); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -75736,7 +72765,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 647 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75747,17 +72776,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(111); -var toRegex = __webpack_require__(648); -var extend = __webpack_require__(649); +var toRegex = __webpack_require__(516); +var extend = __webpack_require__(630); /** * Local dependencies */ -var compilers = __webpack_require__(651); -var parsers = __webpack_require__(652); -var cache = __webpack_require__(655); -var utils = __webpack_require__(657); +var compilers = __webpack_require__(632); +var parsers = __webpack_require__(633); +var cache = __webpack_require__(636); +var utils = __webpack_require__(638); var MAX_LENGTH = 1024 * 64; /** @@ -76581,169 +73610,14 @@ module.exports = nanomatch; /***/ }), -/* 648 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var define = __webpack_require__(593); -var extend = __webpack_require__(535); -var not = __webpack_require__(534); -var MAX_LENGTH = 1024 * 64; - -/** - * Session cache - */ - -var cache = {}; - -/** - * Create a regular expression from the given `pattern` string. - * - * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ - -module.exports = function(patterns, options) { - if (!Array.isArray(patterns)) { - return makeRe(patterns, options); - } - return makeRe(patterns.join('|'), options); -}; - -/** - * Create a regular expression from the given `pattern` string. - * - * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ - -function makeRe(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; - } - - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } - - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); - } - - var key = pattern; - // do this before shallow cloning options, it's a lot faster - if (!options || (options && options.cache !== false)) { - key = createKey(pattern, options); - - if (cache.hasOwnProperty(key)) { - return cache[key]; - } - } - - var opts = extend({}, options); - if (opts.contains === true) { - if (opts.negate === true) { - opts.strictNegate = false; - } else { - opts.strict = false; - } - } - - if (opts.strict === false) { - opts.strictOpen = false; - opts.strictClose = false; - } - - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var flags = opts.flags || ''; - var regex; - - if (opts.nocase === true && !/i/.test(flags)) { - flags += 'i'; - } - - try { - if (opts.negate || typeof opts.strictNegate === 'boolean') { - pattern = not.create(pattern, opts); - } - var str = open + '(?:' + pattern + ')' + close; - regex = new RegExp(str, flags); - } catch (err) { - if (opts.strictErrors === true) { - err.key = key; - err.pattern = pattern; - err.originalOptions = options; - err.createdOptions = opts; - throw err; - } - - try { - regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); - } catch (err) { - regex = /.^/; //<= match nothing - } - } - - if (opts.cache !== false) { - cacheRegex(regex, key, pattern, opts); - } - return regex; -} - -/** - * Cache generated regex. This can result in dramatic speed improvements - * and simplify debugging by adding options and pattern to the regex. It can be - * disabled by passing setting `options.cache` to false. - */ - -function cacheRegex(regex, key, pattern, options) { - define(regex, 'cached', true); - define(regex, 'pattern', pattern); - define(regex, 'options', options); - define(regex, 'key', key); - cache[key] = regex; -} - -/** - * Create the key to use for memoization. The key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. - */ - -function createKey(pattern, options) { - if (!options) return pattern; - var key = pattern; - for (var prop in options) { - if (options.hasOwnProperty(prop)) { - key += ';' + prop + '=' + String(options[prop]); - } - } - return key; -} - -/** - * Expose `makeRe` - */ - -module.exports.makeRe = makeRe; - - -/***/ }), -/* 649 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(650); -var assignSymbols = __webpack_require__(545); +var isExtendable = __webpack_require__(631); +var assignSymbols = __webpack_require__(536); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -76803,7 +73677,7 @@ function isEnum(obj, key) { /***/ }), -/* 650 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76816,7 +73690,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(543); +var isPlainObject = __webpack_require__(534); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -76824,7 +73698,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 651 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77170,15 +74044,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 652 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(534); -var toRegex = __webpack_require__(648); -var isOdd = __webpack_require__(653); +var regexNot = __webpack_require__(527); +var toRegex = __webpack_require__(516); +var isOdd = __webpack_require__(634); /** * Characters to use in negation regex (we want to "not" match @@ -77564,7 +74438,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 653 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77577,7 +74451,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(654); +var isNumber = __webpack_require__(635); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -77591,7 +74465,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 654 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77619,14 +74493,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 655 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(656))(); +module.exports = new (__webpack_require__(637))(); /***/ }), -/* 656 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77639,7 +74513,7 @@ module.exports = new (__webpack_require__(656))(); -var MapCache = __webpack_require__(633); +var MapCache = __webpack_require__(615); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -77761,7 +74635,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 657 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77774,14 +74648,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(658)(); -var Snapdragon = __webpack_require__(566); -utils.define = __webpack_require__(659); -utils.diff = __webpack_require__(660); -utils.extend = __webpack_require__(649); -utils.pick = __webpack_require__(661); -utils.typeOf = __webpack_require__(662); -utils.unique = __webpack_require__(537); +var isWindows = __webpack_require__(639)(); +var Snapdragon = __webpack_require__(555); +utils.define = __webpack_require__(640); +utils.diff = __webpack_require__(641); +utils.extend = __webpack_require__(630); +utils.pick = __webpack_require__(642); +utils.typeOf = __webpack_require__(643); +utils.unique = __webpack_require__(528); /** * Returns true if the given value is effectively an empty string @@ -78147,7 +75021,7 @@ utils.unixify = function(options) { /***/ }), -/* 658 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -78175,7 +75049,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 659 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78188,8 +75062,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(544); -var isDescriptor = __webpack_require__(558); +var isobject = __webpack_require__(535); +var isDescriptor = __webpack_require__(547); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -78220,7 +75094,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 660 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78274,7 +75148,7 @@ function diffArray(one, two) { /***/ }), -/* 661 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78287,7 +75161,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(544); +var isObject = __webpack_require__(535); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -78316,7 +75190,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 662 */ +/* 643 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -78451,7 +75325,7 @@ function isBuffer(val) { /***/ }), -/* 663 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78461,18 +75335,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(535); -var unique = __webpack_require__(537); -var toRegex = __webpack_require__(648); +var extend = __webpack_require__(525); +var unique = __webpack_require__(528); +var toRegex = __webpack_require__(516); /** * Local dependencies */ -var compilers = __webpack_require__(664); -var parsers = __webpack_require__(675); -var Extglob = __webpack_require__(678); -var utils = __webpack_require__(677); +var compilers = __webpack_require__(645); +var parsers = __webpack_require__(656); +var Extglob = __webpack_require__(659); +var utils = __webpack_require__(658); var MAX_LENGTH = 1024 * 64; /** @@ -78789,13 +75663,13 @@ module.exports = extglob; /***/ }), -/* 664 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(665); +var brackets = __webpack_require__(646); /** * Extglob compilers @@ -78965,7 +75839,7 @@ module.exports = function(extglob) { /***/ }), -/* 665 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78975,17 +75849,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(666); -var parsers = __webpack_require__(668); +var compilers = __webpack_require__(647); +var parsers = __webpack_require__(649); /** * Module dependencies */ -var debug = __webpack_require__(670)('expand-brackets'); -var extend = __webpack_require__(535); -var Snapdragon = __webpack_require__(566); -var toRegex = __webpack_require__(648); +var debug = __webpack_require__(651)('expand-brackets'); +var extend = __webpack_require__(525); +var Snapdragon = __webpack_require__(555); +var toRegex = __webpack_require__(516); /** * Parses the given POSIX character class `pattern` and returns a @@ -79183,13 +76057,13 @@ module.exports = brackets; /***/ }), -/* 666 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(667); +var posix = __webpack_require__(648); module.exports = function(brackets) { brackets.compiler @@ -79277,7 +76151,7 @@ module.exports = function(brackets) { /***/ }), -/* 667 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79306,14 +76180,14 @@ module.exports = { /***/ }), -/* 668 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(669); -var define = __webpack_require__(593); +var utils = __webpack_require__(650); +var define = __webpack_require__(517); /** * Text regex @@ -79532,14 +76406,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 669 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(648); -var regexNot = __webpack_require__(534); +var toRegex = __webpack_require__(516); +var regexNot = __webpack_require__(527); var cached; /** @@ -79573,7 +76447,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 670 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -79582,14 +76456,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(671); + module.exports = __webpack_require__(652); } else { - module.exports = __webpack_require__(674); + module.exports = __webpack_require__(655); } /***/ }), -/* 671 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -79598,7 +76472,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(672); +exports = module.exports = __webpack_require__(653); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -79780,7 +76654,7 @@ function localstorage() { /***/ }), -/* 672 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { @@ -79796,7 +76670,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(673); +exports.humanize = __webpack_require__(654); /** * The currently active debug mode names, and names to skip. @@ -79988,7 +76862,7 @@ function coerce(val) { /***/ }), -/* 673 */ +/* 654 */ /***/ (function(module, exports) { /** @@ -80146,7 +77020,7 @@ function plural(ms, n, name) { /***/ }), -/* 674 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -80162,7 +77036,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(672); +exports = module.exports = __webpack_require__(653); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -80341,7 +77215,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(611); + var net = __webpack_require__(593); stream = new net.Socket({ fd: fd, readable: false, @@ -80400,15 +77274,15 @@ exports.enable(load()); /***/ }), -/* 675 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(665); -var define = __webpack_require__(676); -var utils = __webpack_require__(677); +var brackets = __webpack_require__(646); +var define = __webpack_require__(657); +var utils = __webpack_require__(658); /** * Characters to use in text regex (we want to "not" match @@ -80563,7 +77437,7 @@ module.exports = parsers; /***/ }), -/* 676 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80576,7 +77450,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(558); +var isDescriptor = __webpack_require__(547); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -80601,14 +77475,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 677 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(534); -var Cache = __webpack_require__(656); +var regex = __webpack_require__(527); +var Cache = __webpack_require__(637); /** * Utils @@ -80677,7 +77551,7 @@ utils.createRegex = function(str) { /***/ }), -/* 678 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80687,16 +77561,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(566); -var define = __webpack_require__(676); -var extend = __webpack_require__(535); +var Snapdragon = __webpack_require__(555); +var define = __webpack_require__(657); +var extend = __webpack_require__(525); /** * Local dependencies */ -var compilers = __webpack_require__(664); -var parsers = __webpack_require__(675); +var compilers = __webpack_require__(645); +var parsers = __webpack_require__(656); /** * Customize Snapdragon parser and renderer @@ -80762,16 +77636,16 @@ module.exports = Extglob; /***/ }), -/* 679 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(663); -var nanomatch = __webpack_require__(647); -var regexNot = __webpack_require__(534); -var toRegex = __webpack_require__(635); +var extglob = __webpack_require__(644); +var nanomatch = __webpack_require__(629); +var regexNot = __webpack_require__(527); +var toRegex = __webpack_require__(617); var not; /** @@ -80852,14 +77726,14 @@ function textRegex(pattern) { /***/ }), -/* 680 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(656))(); +module.exports = new (__webpack_require__(637))(); /***/ }), -/* 681 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80872,13 +77746,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(566); -utils.define = __webpack_require__(642); -utils.diff = __webpack_require__(660); -utils.extend = __webpack_require__(643); -utils.pick = __webpack_require__(661); -utils.typeOf = __webpack_require__(682); -utils.unique = __webpack_require__(537); +var Snapdragon = __webpack_require__(555); +utils.define = __webpack_require__(624); +utils.diff = __webpack_require__(641); +utils.extend = __webpack_require__(625); +utils.pick = __webpack_require__(642); +utils.typeOf = __webpack_require__(663); +utils.unique = __webpack_require__(528); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -81175,7 +78049,7 @@ utils.unixify = function(options) { /***/ }), -/* 682 */ +/* 663 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -81310,7 +78184,7 @@ function isBuffer(val) { /***/ }), -/* 683 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81329,9 +78203,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(684); -var reader_1 = __webpack_require__(697); -var fs_stream_1 = __webpack_require__(701); +var readdir = __webpack_require__(665); +var reader_1 = __webpack_require__(678); +var fs_stream_1 = __webpack_require__(682); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -81392,15 +78266,15 @@ exports.default = ReaderAsync; /***/ }), -/* 684 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(685); -const readdirAsync = __webpack_require__(693); -const readdirStream = __webpack_require__(696); +const readdirSync = __webpack_require__(666); +const readdirAsync = __webpack_require__(674); +const readdirStream = __webpack_require__(677); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -81484,7 +78358,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 685 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81492,11 +78366,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(686); +const DirectoryReader = __webpack_require__(667); let syncFacade = { - fs: __webpack_require__(691), - forEach: __webpack_require__(692), + fs: __webpack_require__(672), + forEach: __webpack_require__(673), sync: true }; @@ -81525,7 +78399,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 686 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81534,9 +78408,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(137).Readable; const EventEmitter = __webpack_require__(155).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(687); -const stat = __webpack_require__(689); -const call = __webpack_require__(690); +const normalizeOptions = __webpack_require__(668); +const stat = __webpack_require__(670); +const call = __webpack_require__(671); /** * Asynchronously reads the contents of a directory and streams the results @@ -81912,14 +78786,14 @@ module.exports = DirectoryReader; /***/ }), -/* 687 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(688); +const globToRegExp = __webpack_require__(669); module.exports = normalizeOptions; @@ -82096,7 +78970,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 688 */ +/* 669 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -82233,13 +79107,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 689 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(690); +const call = __webpack_require__(671); module.exports = stat; @@ -82314,7 +79188,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 690 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82375,14 +79249,14 @@ function callOnce (fn) { /***/ }), -/* 691 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const call = __webpack_require__(690); +const call = __webpack_require__(671); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -82446,7 +79320,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 692 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82475,7 +79349,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 693 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82483,12 +79357,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(694); -const DirectoryReader = __webpack_require__(686); +const maybe = __webpack_require__(675); +const DirectoryReader = __webpack_require__(667); let asyncFacade = { fs: __webpack_require__(133), - forEach: __webpack_require__(695), + forEach: __webpack_require__(676), async: true }; @@ -82530,7 +79404,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 694 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82557,7 +79431,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 695 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82593,7 +79467,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 696 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82601,11 +79475,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(686); +const DirectoryReader = __webpack_require__(667); let streamFacade = { fs: __webpack_require__(133), - forEach: __webpack_require__(695), + forEach: __webpack_require__(676), async: true }; @@ -82625,16 +79499,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 697 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(698); -var entry_1 = __webpack_require__(700); -var pathUtil = __webpack_require__(699); +var deep_1 = __webpack_require__(679); +var entry_1 = __webpack_require__(681); +var pathUtil = __webpack_require__(680); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -82700,14 +79574,14 @@ exports.default = Reader; /***/ }), -/* 698 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(699); -var patternUtils = __webpack_require__(516); +var pathUtils = __webpack_require__(680); +var patternUtils = __webpack_require__(509); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -82790,7 +79664,7 @@ exports.default = DeepFilter; /***/ }), -/* 699 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82821,14 +79695,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 700 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(699); -var patternUtils = __webpack_require__(516); +var pathUtils = __webpack_require__(680); +var patternUtils = __webpack_require__(509); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -82913,7 +79787,7 @@ exports.default = EntryFilter; /***/ }), -/* 701 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82933,8 +79807,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(137); -var fsStat = __webpack_require__(702); -var fs_1 = __webpack_require__(706); +var fsStat = __webpack_require__(683); +var fs_1 = __webpack_require__(687); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -82984,14 +79858,14 @@ exports.default = FileSystemStream; /***/ }), -/* 702 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(703); -const statProvider = __webpack_require__(705); +const optionsManager = __webpack_require__(684); +const statProvider = __webpack_require__(686); /** * Asynchronous API. */ @@ -83022,13 +79896,13 @@ exports.statSync = statSync; /***/ }), -/* 703 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(704); +const fsAdapter = __webpack_require__(685); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -83041,7 +79915,7 @@ exports.prepare = prepare; /***/ }), -/* 704 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83064,7 +79938,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 705 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83116,7 +79990,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 706 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83147,7 +80021,7 @@ exports.default = FileSystem; /***/ }), -/* 707 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83167,9 +80041,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(137); -var readdir = __webpack_require__(684); -var reader_1 = __webpack_require__(697); -var fs_stream_1 = __webpack_require__(701); +var readdir = __webpack_require__(665); +var reader_1 = __webpack_require__(678); +var fs_stream_1 = __webpack_require__(682); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -83237,7 +80111,7 @@ exports.default = ReaderStream; /***/ }), -/* 708 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83256,9 +80130,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(684); -var reader_1 = __webpack_require__(697); -var fs_sync_1 = __webpack_require__(709); +var readdir = __webpack_require__(665); +var reader_1 = __webpack_require__(678); +var fs_sync_1 = __webpack_require__(690); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -83318,7 +80192,7 @@ exports.default = ReaderSync; /***/ }), -/* 709 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83337,8 +80211,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(702); -var fs_1 = __webpack_require__(706); +var fsStat = __webpack_require__(683); +var fs_1 = __webpack_require__(687); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -83384,7 +80258,7 @@ exports.default = FileSystemSync; /***/ }), -/* 710 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83400,13 +80274,13 @@ exports.flatten = flatten; /***/ }), -/* 711 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(291); +var merge2 = __webpack_require__(288); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -83421,13 +80295,13 @@ exports.merge = merge; /***/ }), -/* 712 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(713); +const pathType = __webpack_require__(694); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -83493,13 +80367,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 713 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const pify = __webpack_require__(714); +const pify = __webpack_require__(695); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -83542,7 +80416,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 714 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83633,17 +80507,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 715 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(512); -const gitIgnore = __webpack_require__(716); -const pify = __webpack_require__(717); -const slash = __webpack_require__(718); +const fastGlob = __webpack_require__(505); +const gitIgnore = __webpack_require__(697); +const pify = __webpack_require__(698); +const slash = __webpack_require__(699); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -83741,7 +80615,7 @@ module.exports.sync = options => { /***/ }), -/* 716 */ +/* 697 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -84210,7 +81084,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 717 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84285,7 +81159,7 @@ module.exports = (input, options) => { /***/ }), -/* 718 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84303,7 +81177,7 @@ module.exports = input => { /***/ }), -/* 719 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -84313,7 +81187,7 @@ module.exports = input => { * Released under the MIT License. */ -var isExtglob = __webpack_require__(302); +var isExtglob = __webpack_require__(299); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -84357,17 +81231,17 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 720 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(133); -const pEvent = __webpack_require__(721); -const CpFileError = __webpack_require__(724); -const fs = __webpack_require__(728); -const ProgressEmitter = __webpack_require__(731); +const pEvent = __webpack_require__(702); +const CpFileError = __webpack_require__(705); +const fs = __webpack_require__(709); +const ProgressEmitter = __webpack_require__(712); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -84481,12 +81355,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 721 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(722); +const pTimeout = __webpack_require__(703); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -84777,12 +81651,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 722 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(723); +const pFinally = __webpack_require__(704); class TimeoutError extends Error { constructor(message) { @@ -84828,7 +81702,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 723 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84850,12 +81724,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 724 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(725); +const NestedError = __webpack_require__(706); class CpFileError extends NestedError { constructor(message, nested) { @@ -84869,10 +81743,10 @@ module.exports = CpFileError; /***/ }), -/* 725 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(726); +var inherits = __webpack_require__(707); var NestedError = function (message, nested) { this.nested = nested; @@ -84923,7 +81797,7 @@ module.exports = NestedError; /***/ }), -/* 726 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -84931,12 +81805,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(727); + module.exports = __webpack_require__(708); } /***/ }), -/* 727 */ +/* 708 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -84965,16 +81839,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 728 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(111); const fs = __webpack_require__(132); -const makeDir = __webpack_require__(729); -const pEvent = __webpack_require__(721); -const CpFileError = __webpack_require__(724); +const makeDir = __webpack_require__(710); +const pEvent = __webpack_require__(702); +const CpFileError = __webpack_require__(705); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -85071,7 +81945,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 729 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85079,7 +81953,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(133); const path = __webpack_require__(4); const {promisify} = __webpack_require__(111); -const semver = __webpack_require__(730); +const semver = __webpack_require__(711); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -85234,7 +82108,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 730 */ +/* 711 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -86836,7 +83710,7 @@ function coerce (version, options) { /***/ }), -/* 731 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86877,7 +83751,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 732 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86923,12 +83797,12 @@ exports.default = module.exports; /***/ }), -/* 733 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(734); +const NestedError = __webpack_require__(715); class CpyError extends NestedError { constructor(message, nested) { @@ -86942,7 +83816,7 @@ module.exports = CpyError; /***/ }), -/* 734 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(111).inherits; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 1fb94e4c92ce1..c2f9236d9e798 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -19,8 +19,8 @@ "@types/cpy": "^5.1.0", "@types/dedent": "^0.7.0", "@types/getopts": "^2.0.1", - "@types/glob": "^5.0.35", - "@types/globby": "^6.1.0", + "@types/glob": "^7.1.2", + "@types/globby": "^8.0.0", "@types/has-ansi": "^3.0.0", "@types/lodash": "^4.14.159", "@types/log-symbols": "^2.0.0", @@ -41,12 +41,12 @@ "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^4.0.2", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "glob": "^7.1.2", "globby": "^8.0.1", "has-ansi": "^3.0.0", "is-path-inside": "^3.0.2", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "log-symbols": "^2.2.0", "multimatch": "^4.0.0", "ncp": "^2.0.0", diff --git a/packages/kbn-pm/src/commands/bootstrap.test.ts b/packages/kbn-pm/src/commands/bootstrap.test.ts index 97505a66a1fff..956c4e72933ba 100644 --- a/packages/kbn-pm/src/commands/bootstrap.test.ts +++ b/packages/kbn-pm/src/commands/bootstrap.test.ts @@ -19,6 +19,7 @@ jest.mock('../utils/scripts'); jest.mock('../utils/link_project_executables'); +jest.mock('../utils/validate_yarn_lock'); import { resolve } from 'path'; diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index a559f9a20432a..7cf89c5f08f96 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -25,6 +25,8 @@ import { Project } from '../utils/project'; import { ICommand } from './'; import { getAllChecksums } from '../utils/project_checksums'; import { BootstrapCacheFile } from '../utils/bootstrap_cache_file'; +import { readYarnLock } from '../utils/yarn_lock'; +import { validateYarnLock } from '../utils/validate_yarn_lock'; export const BootstrapCommand: ICommand = { description: 'Install dependencies and crosslink projects', @@ -54,6 +56,10 @@ export const BootstrapCommand: ICommand = { } } + const yarnLock = await readYarnLock(kbn); + + await validateYarnLock(kbn, yarnLock); + await linkProjectExecutables(projects, projectGraph); /** @@ -63,7 +69,7 @@ export const BootstrapCommand: ICommand = { * have to, as it will slow down the bootstrapping process. */ - const checksums = await getAllChecksums(kbn, log); + const checksums = await getAllChecksums(kbn, log, yarnLock); const caches = new Map(); let cachedProjectCount = 0; diff --git a/packages/kbn-pm/src/utils/__snapshots__/link_project_executables.test.ts.snap b/packages/kbn-pm/src/utils/__snapshots__/link_project_executables.test.ts.snap index 5bda7b544e201..311e350f6e865 100644 --- a/packages/kbn-pm/src/utils/__snapshots__/link_project_executables.test.ts.snap +++ b/packages/kbn-pm/src/utils/__snapshots__/link_project_executables.test.ts.snap @@ -18,6 +18,7 @@ Object { "mkdirp": Array [], "readFile": Array [], "unlink": Array [], + "writeFile": Array [], } `; @@ -66,5 +67,6 @@ Object { ], "readFile": Array [], "unlink": Array [], + "writeFile": Array [], } `; diff --git a/packages/kbn-pm/src/utils/fs.ts b/packages/kbn-pm/src/utils/fs.ts index 44fc59bdeba96..21fd2c32b9c7b 100644 --- a/packages/kbn-pm/src/utils/fs.ts +++ b/packages/kbn-pm/src/utils/fs.ts @@ -25,6 +25,7 @@ import { promisify } from 'util'; const lstat = promisify(fs.lstat); export const readFile = promisify(fs.readFile); +export const writeFile = promisify(fs.writeFile); const symlink = promisify(fs.symlink); export const chmod = promisify(fs.chmod); const cmdShim = promisify(cmdShimCb); diff --git a/packages/kbn-pm/src/utils/kibana.ts b/packages/kbn-pm/src/utils/kibana.ts index 7fca4bd01822b..e48b61611d63f 100644 --- a/packages/kbn-pm/src/utils/kibana.ts +++ b/packages/kbn-pm/src/utils/kibana.ts @@ -22,6 +22,8 @@ import Path from 'path'; import multimatch from 'multimatch'; import isPathInside from 'is-path-inside'; +import { resolveDepsForProject, YarnLock } from './yarn_lock'; +import { Log } from './log'; import { ProjectMap, getProjects, includeTransitiveProjects } from './projects'; import { Project } from './project'; import { getProjectPaths } from '../config'; @@ -133,4 +135,26 @@ export class Kibana { isOutsideRepo(project: Project) { return !this.isPartOfRepo(project); } + + resolveAllProductionDependencies(yarnLock: YarnLock, log: Log) { + const kibanaDeps = resolveDepsForProject({ + project: this.kibanaProject, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log, + })!; + + const xpackDeps = resolveDepsForProject({ + project: this.getProject('x-pack')!, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log, + })!; + + return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + } } diff --git a/packages/kbn-pm/src/utils/project_checksums.ts b/packages/kbn-pm/src/utils/project_checksums.ts index 839c44f4f18c4..c13788c89bfaa 100644 --- a/packages/kbn-pm/src/utils/project_checksums.ts +++ b/packages/kbn-pm/src/utils/project_checksums.ts @@ -24,7 +24,7 @@ import { promisify } from 'util'; import execa from 'execa'; -import { readYarnLock, YarnLock } from './yarn_lock'; +import { YarnLock, resolveDepsForProject } from './yarn_lock'; import { ProjectMap } from '../utils/projects'; import { Project } from '../utils/project'; import { Kibana } from '../utils/kibana'; @@ -145,51 +145,6 @@ async function getLatestSha(project: Project, kbn: Kibana) { return stdout.trim() || undefined; } -/** - * Get a list of the absolute dependencies of this project, as resolved - * in the yarn.lock file, does not include other projects in the workspace - * or their dependencies - */ -function resolveDepsForProject(project: Project, yarnLock: YarnLock, kbn: Kibana, log: Log) { - /** map of [name@range, name@resolved] */ - const resolved = new Map(); - - const queue: Array<[string, string]> = Object.entries(project.allDependencies); - - while (queue.length) { - const [name, versionRange] = queue.shift()!; - const req = `${name}@${versionRange}`; - - if (resolved.has(req)) { - continue; - } - - if (!kbn.hasProject(name)) { - const pkg = yarnLock[req]; - if (!pkg) { - log.warning( - 'yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching' - ); - return; - } - - const res = `${name}@${pkg.version}`; - resolved.set(req, res); - - const allDepsEntries = [ - ...Object.entries(pkg.dependencies || {}), - ...Object.entries(pkg.optionalDependencies || {}), - ]; - - for (const [childName, childVersionRange] of allDepsEntries) { - queue.push([childName, childVersionRange]); - } - } - } - - return Array.from(resolved.values()).sort((a, b) => a.localeCompare(b)); -} - /** * Get the checksum for a specific project in the workspace */ @@ -224,11 +179,22 @@ async function getChecksum( }) ); - const deps = await resolveDepsForProject(project, yarnLock, kbn, log); - if (!deps) { + const depMap = resolveDepsForProject({ + project, + yarnLock, + kbn, + log, + includeDependentProject: false, + productionDepsOnly: false, + }); + if (!depMap) { return; } + const deps = Array.from(depMap.values()) + .map(({ name, version }) => `${name}@${version}`) + .sort((a, b) => a.localeCompare(b)); + log.verbose(`[${project.name}] resolved %d deps`, deps.length); const checksum = JSON.stringify( @@ -256,10 +222,9 @@ async function getChecksum( * - un-committed changes * - resolved dependencies from yarn.lock referenced by project package.json */ -export async function getAllChecksums(kbn: Kibana, log: Log) { +export async function getAllChecksums(kbn: Kibana, log: Log, yarnLock: YarnLock) { const projects = kbn.getAllProjects(); const changesByProject = await getChangesForProjects(projects, kbn, log); - const yarnLock = await readYarnLock(kbn); /** map of [project.name, cacheKey] */ const cacheKeys: ChecksumMap = new Map(); diff --git a/packages/kbn-pm/src/utils/validate_yarn_lock.ts b/packages/kbn-pm/src/utils/validate_yarn_lock.ts new file mode 100644 index 0000000000000..e110dc4d921cf --- /dev/null +++ b/packages/kbn-pm/src/utils/validate_yarn_lock.ts @@ -0,0 +1,99 @@ +/* + * 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. + */ + +// @ts-expect-error published types are useless +import { stringify as stringifyLockfile } from '@yarnpkg/lockfile'; +import dedent from 'dedent'; + +import { writeFile } from './fs'; +import { Kibana } from './kibana'; +import { YarnLock } from './yarn_lock'; +import { log } from './log'; + +export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { + // look through all of the packages in the yarn.lock file to see if + // we have accidentally installed multiple lodash v4 versions + const lodash4Versions = new Set(); + const lodash4Reqs = new Set(); + for (const [req, dep] of Object.entries(yarnLock)) { + if (req.startsWith('lodash@') && dep.version.startsWith('4.')) { + lodash4Reqs.add(req); + lodash4Versions.add(dep.version); + } + } + + // if we find more than one lodash v4 version installed then delete + // lodash v4 requests from the yarn.lock file and prompt the user to + // retry bootstrap so that a single v4 version will be installed + if (lodash4Versions.size > 1) { + for (const req of lodash4Reqs) { + delete yarnLock[req]; + } + + await writeFile(kbn.getAbsolute('yarn.lock'), stringifyLockfile(yarnLock), 'utf8'); + + log.error(dedent` + + Multiple version of lodash v4 were detected, so they have been removed + from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the + lodash versions installed. + + If you still see this error when you re-bootstrap then you might need + to force a new dependency to use the latest version of lodash via the + "resolutions" field in package.json. + + If you have questions about this please reach out to the operations team. + + `); + + process.exit(1); + } + + // look through all the dependencies of production packages and production + // dependencies of those packages to determine if we're shipping any versions + // of lodash v3 in the distributable + const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, log); + const lodash3Versions = new Set(); + for (const dep of prodDependencies.values()) { + if (dep.name === 'lodash' && dep.version.startsWith('3.')) { + lodash3Versions.add(dep.version); + } + } + + // if any lodash v3 packages were found we abort and tell the user to fix things + if (lodash3Versions.size) { + log.error(dedent` + + Due to changes in the yarn.lock file and/or package.json files a version of + lodash 3 is now included in the production dependencies. To reduce the size of + our distributable and especially our front-end bundles we have decided to + prevent adding any new instances of lodash 3. + + Please inspect the changes to yarn.lock or package.json files to identify where + the lodash 3 version is coming from and remove it. + + If you have questions about this please reack out to the operations team. + + `); + + process.exit(1); + } + + log.success('yarn.lock analysis completed without any issues'); +} diff --git a/packages/kbn-pm/src/utils/yarn_lock.ts b/packages/kbn-pm/src/utils/yarn_lock.ts index f46240c0e1d2e..953341915e232 100644 --- a/packages/kbn-pm/src/utils/yarn_lock.ts +++ b/packages/kbn-pm/src/utils/yarn_lock.ts @@ -17,14 +17,16 @@ * under the License. */ -// @ts-ignore published types are worthless +// @ts-expect-error published types are worthless import { parse as parseLockfile } from '@yarnpkg/lockfile'; import { readFile } from '../utils/fs'; import { Kibana } from '../utils/kibana'; +import { Project } from '../utils/project'; +import { Log } from '../utils/log'; export interface YarnLock { - /** a simple map of version@versionrange tags to metadata about a package */ + /** a simple map of name@versionrange tags to metadata about a package */ [key: string]: { /** resolved version installed for this pacakge */ version: string; @@ -61,3 +63,82 @@ export async function readYarnLock(kbn: Kibana): Promise { return {}; } + +/** + * Get a list of the absolute dependencies of this project, as resolved + * in the yarn.lock file, does not include other projects in the workspace + * or their dependencies + */ +export function resolveDepsForProject({ + project: rootProject, + yarnLock, + kbn, + log, + productionDepsOnly, + includeDependentProject, +}: { + project: Project; + yarnLock: YarnLock; + kbn: Kibana; + log: Log; + productionDepsOnly: boolean; + includeDependentProject: boolean; +}) { + /** map of [name@range, { name, version }] */ + const resolved = new Map(); + + const seenProjects = new Set(); + const projectQueue: Project[] = [rootProject]; + const depQueue: Array<[string, string]> = []; + + while (projectQueue.length) { + const project = projectQueue.shift()!; + if (seenProjects.has(project)) { + continue; + } + seenProjects.add(project); + + const projectDeps = Object.entries( + productionDepsOnly ? project.productionDependencies : project.allDependencies + ); + for (const [name, versionRange] of projectDeps) { + depQueue.push([name, versionRange]); + } + + while (depQueue.length) { + const [name, versionRange] = depQueue.shift()!; + const req = `${name}@${versionRange}`; + + if (resolved.has(req)) { + continue; + } + + if (includeDependentProject && kbn.hasProject(name)) { + projectQueue.push(kbn.getProject(name)!); + } + + if (!kbn.hasProject(name)) { + const pkg = yarnLock[req]; + if (!pkg) { + log.warning( + 'yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching' + ); + return; + } + + resolved.set(req, { name, version: pkg.version }); + + const allDepsEntries = [ + ...Object.entries(pkg.dependencies || {}), + ...Object.entries(pkg.optionalDependencies || {}), + ]; + + for (const [childName, childVersionRange] of allDepsEntries) { + depQueue.push([childName, childVersionRange]); + } + } + } + } + + return resolved; +} diff --git a/packages/kbn-spec-to-console/package.json b/packages/kbn-spec-to-console/package.json index 63ce93ac54f46..0c80c949c1d11 100644 --- a/packages/kbn-spec-to-console/package.json +++ b/packages/kbn-spec-to-console/package.json @@ -21,7 +21,7 @@ "prettier": "^2.1.1" }, "dependencies": { - "commander": "^3.0.0", + "commander": "^3.0.2", "glob": "^7.1.2" } -} \ No newline at end of file +} diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json index 2cc9fd72082be..a931dd3f3154d 100644 --- a/packages/kbn-std/package.json +++ b/packages/kbn-std/package.json @@ -15,6 +15,6 @@ }, "dependencies": { "@kbn/utility-types": "1.0.0", - "lodash": "^4.17.15" + "lodash": "^4.17.20" } } diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index 8cffcd43d7537..7cf70a0e28e2c 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -24,6 +24,6 @@ export { mapToObject } from './map_to_object'; export { merge } from './merge'; export { pick } from './pick'; export { withTimeout } from './promise'; -export { isRelativeUrl, modifyUrl, URLMeaningfulParts, ParsedQuery } from './url'; +export { isRelativeUrl, modifyUrl, URLMeaningfulParts } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; diff --git a/packages/kbn-std/src/merge.ts b/packages/kbn-std/src/merge.ts index c0de50544a34e..43878c27b1e19 100644 --- a/packages/kbn-std/src/merge.ts +++ b/packages/kbn-std/src/merge.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import isPlainObject from 'lodash/isPlainObject'; +import { isPlainObject } from 'lodash'; /** * Deeply merges two objects, omitting undefined values, and not deeply merging Arrays. * diff --git a/packages/kbn-std/src/url.ts b/packages/kbn-std/src/url.ts index 7a0f08130816d..edcdebbd2bc81 100644 --- a/packages/kbn-std/src/url.ts +++ b/packages/kbn-std/src/url.ts @@ -18,11 +18,7 @@ */ import { format as formatUrl, parse as parseUrl, UrlObject } from 'url'; - -// duplicate type from 'query-string' to avoid adding the d.ts file to all packages depending on kbn-std -export interface ParsedQuery { - [key: string]: T | T[] | null | undefined; -} +import type { ParsedQuery } from 'query-string'; /** * We define our own typings because the current version of @types/node diff --git a/packages/kbn-storybook/README.md b/packages/kbn-storybook/README.md index c9195f41ebf26..eea8912c8715e 100644 --- a/packages/kbn-storybook/README.md +++ b/packages/kbn-storybook/README.md @@ -2,32 +2,40 @@ This package provides ability to add [Storybook](https://storybook.js.org/) to any Kibana plugin. -- [Setup Instructions](#setup-instructions) - +- [Kibana Storybook](#kibana-storybook) + - [Setup Instructions](#setup-instructions) + - [Customizing configuration](#customizing-configuration) ## Setup Instructions -1. Add `storybook.js` launcher file to your plugin. For example, create a file at - `src/plugins//scripts/storybook.js`, with the following contents: - - ```js - import { join } from 'path'; - - // eslint-disable-next-line - require('@kbn/storybook').runStorybookCli({ - name: '', - storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')], - }); - ``` -2. Add your plugin alias to `src/dev/storybook/aliases.ts` config. -3. Create sample Storybook stories. For example, in your plugin create create a file at - `src/plugins//public/components/hello_world/__examples__/hello_world.examples.tsx` with - the following contents: - - ```jsx - import * as React from 'react'; - import { storiesOf } from '@storybook/react'; - - storiesOf('Hello world', module).add('default', () =>
Hello world!
); - ``` -4. Launch Storybook with `yarn storybook `. +- Add a `.storybook/main.js` configuration file to your plugin. For example, create a file at + `src/plugins//.storybook/main.js`, with the following contents: + + ```js + module.exports = require('@kbn/storybook').defaultConfig; + ``` + +- Add your plugin alias to `src/dev/storybook/aliases.ts` config. +- Create sample Storybook stories. For example, in your plugin create a file at + `src/plugins//public/components/hello_world/hello_world.stories.tsx` with + the following [Component Story Format](https://storybook.js.org/docs/react/api/csf) contents: + + ```jsx + import { MyComponent } from './my_component'; + + export default { + component: MyComponent, + title: 'Path/In/Side/Navigation/ToComponent', + }; + + export function Example() { + return ; + } + ``` + +- Launch Storybook with `yarn storybook `, or build a static site with `yarn storybook --site `. + +## Customizing configuration + +The `defaultConfig` object provided by the @kbn/storybook package should be all you need to get running, but you can +override this in your .storybook/main.js. Using [Storybook's configuration options](https://storybook.js.org/docs/react/configure/overview). diff --git a/packages/kbn-storybook/index.js b/packages/kbn-storybook/index.js deleted file mode 100644 index 77d457f2bb3c0..0000000000000 --- a/packages/kbn-storybook/index.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const fs = require('fs'); -const { join } = require('path'); -const Rx = require('rxjs'); -const { first } = require('rxjs/operators'); -const storybook = require('@storybook/react/standalone'); -const { run } = require('@kbn/dev-utils'); -const { generateStorybookEntry } = require('./lib/storybook_entry'); -const { ASSET_DIR, CURRENT_CONFIG } = require('./lib/constants'); -const { buildDll } = require('./lib/dll'); - -exports.runStorybookCli = (config) => { - const { name, storyGlobs } = config; - run( - async ({ flags, log, procRunner }) => { - log.debug('Global config:\n', require('./lib/constants')); - - const currentConfig = JSON.stringify(config, null, 2); - const currentConfigDir = join(CURRENT_CONFIG, '..'); - await fs.promises.mkdir(currentConfigDir, { recursive: true }); - log.debug('Writing currentConfig:\n', CURRENT_CONFIG + '\n', currentConfig); - await fs.promises.writeFile(CURRENT_CONFIG, `exports.currentConfig = ${currentConfig};`); - - await buildDll({ - rebuildDll: flags.rebuildDll, - log, - procRunner, - }); - - const subj = new Rx.ReplaySubject(1); - generateStorybookEntry({ log, storyGlobs }).subscribe(subj); - - await subj.pipe(first()).toPromise(); - - await Promise.all([ - // route errors - subj.toPromise(), - - new Promise(async () => { - // storybook never completes, so neither will this promise - const configDir = join(__dirname, 'storybook_config'); - log.debug('Config dir:', configDir); - - const config = { - mode: flags.site ? 'static' : 'dev', - port: 9001, - configDir, - }; - if (flags.site) { - config.outputDir = join(ASSET_DIR, name); - } - - await storybook(config); - - // Line is only reached when building the static version - if (flags.site) process.exit(); - }), - ]); - }, - { - flags: { - boolean: ['rebuildDll', 'site'], - }, - description: ` - Run the storybook examples for ${name} - `, - } - ); -}; diff --git a/packages/kbn-storybook/index.ts b/packages/kbn-storybook/index.ts new file mode 100644 index 0000000000000..a0c944f9a6e28 --- /dev/null +++ b/packages/kbn-storybook/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 { defaultConfig } from './lib/default_config'; +export { runStorybookCli } from './lib/run_storybook_cli'; diff --git a/packages/kbn-storybook/lib/constants.js b/packages/kbn-storybook/lib/constants.js deleted file mode 100644 index 4d8ca0adbfe17..0000000000000 --- a/packages/kbn-storybook/lib/constants.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const { resolve } = require('path'); -const { REPO_ROOT } = require('@kbn/utils'); - -exports.ASSET_DIR = resolve(REPO_ROOT, 'built_assets/storybook'); -exports.CURRENT_CONFIG = resolve(exports.ASSET_DIR, 'current.config.js'); -exports.STORY_ENTRY_PATH = resolve(exports.ASSET_DIR, 'stories.entry.js'); -exports.DLL_DIST_DIR = resolve(exports.ASSET_DIR, 'dll'); -exports.DLL_NAME = 'storybook_dll'; diff --git a/packages/kbn-storybook/lib/constants.ts b/packages/kbn-storybook/lib/constants.ts new file mode 100644 index 0000000000000..7ca0ff349af97 --- /dev/null +++ b/packages/kbn-storybook/lib/constants.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { resolve } from 'path'; +import { REPO_ROOT as KIBANA_ROOT } from '@kbn/dev-utils'; + +export const REPO_ROOT = KIBANA_ROOT; +export const ASSET_DIR = resolve(KIBANA_ROOT, 'built_assets/storybook'); diff --git a/packages/kbn-storybook/lib/default_config.ts b/packages/kbn-storybook/lib/default_config.ts new file mode 100644 index 0000000000000..1fad9e2a3e087 --- /dev/null +++ b/packages/kbn-storybook/lib/default_config.ts @@ -0,0 +1,28 @@ +/* + * 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 { StorybookConfig } from '@storybook/core/types'; + +export const defaultConfig: StorybookConfig = { + addons: ['@kbn/storybook/preset', '@storybook/addon-knobs', '@storybook/addon-essentials'], + stories: ['../**/*.stories.tsx'], + typescript: { + reactDocgen: false, + }, +}; diff --git a/packages/kbn-storybook/lib/dll.js b/packages/kbn-storybook/lib/dll.js deleted file mode 100644 index 55bc8e43a02ec..0000000000000 --- a/packages/kbn-storybook/lib/dll.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const { resolve } = require('path'); -const { existsSync } = require('fs'); - -const { REPO_ROOT } = require('@kbn/utils'); -const { DLL_DIST_DIR } = require('./constants'); - -exports.buildDll = async ({ rebuildDll, log, procRunner }) => { - if (rebuildDll) { - log.info('rebuilding dll'); - } else if (!existsSync(resolve(DLL_DIST_DIR, 'dll.js'))) { - log.info('dll missing, rebuilding'); - } else { - log.info('dll exists'); - return; - } - - await procRunner.run('build dll ', { - cmd: require.resolve('webpack/bin/webpack'), - args: ['--config', require.resolve('./webpack.dll.config.js')], - cwd: REPO_ROOT, - wait: true, - }); -}; diff --git a/packages/kbn-storybook/lib/register.ts b/packages/kbn-storybook/lib/register.ts new file mode 100644 index 0000000000000..5121b6f614902 --- /dev/null +++ b/packages/kbn-storybook/lib/register.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 { addons } from '@storybook/addons'; +import { create } from '@storybook/theming'; + +// This configures the "Manager", or main outer view of Storybook. It is an +// addon that's loaded by the `managerEntries` part of the preset in ../preset.js. +addons.setConfig({ + theme: create({ + base: 'light', + brandTitle: 'Kibana Storybook', + brandUrl: 'https://github.com/elastic/kibana/tree/master/packages/kbn-storybook', + }), + showPanel: false, + isFullscreen: false, + panelPosition: 'bottom', + isToolshown: true, +}); diff --git a/packages/kbn-storybook/lib/run_storybook_cli.ts b/packages/kbn-storybook/lib/run_storybook_cli.ts new file mode 100644 index 0000000000000..3c4cdbf3dcf84 --- /dev/null +++ b/packages/kbn-storybook/lib/run_storybook_cli.ts @@ -0,0 +1,75 @@ +/* + * 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 { join } from 'path'; +import { logger } from '@storybook/node-logger'; +import buildStandalone from '@storybook/react/standalone'; +import { Flags, run } from '@kbn/dev-utils'; +import { distDir } from '@kbn/ui-shared-deps'; +import * as constants from './constants'; + +// Convert the flags to a Storybook loglevel +function getLogLevelFromFlags(flags: Flags) { + if (flags.debug) { + return 'silly'; + } + if (flags.verbose) { + return 'verbose'; + } + if (flags.quiet) { + return 'warn'; + } + if (flags.silent) { + return 'silent'; + } + return 'info'; +} + +export function runStorybookCli({ configDir, name }: { configDir: string; name: string }) { + run( + async ({ flags, log }) => { + log.debug('Global config:\n', constants); + + const staticDir = [distDir]; + const config: Record = { + configDir, + mode: flags.site ? 'static' : 'dev', + port: 9001, + staticDir, + }; + if (flags.site) { + config.outputDir = join(constants.ASSET_DIR, name); + } + + logger.setLevel(getLogLevelFromFlags(flags)); + await buildStandalone(config); + + // Line is only reached when building the static version + if (flags.site) process.exit(); + }, + { + flags: { + boolean: ['site'], + }, + description: ` + Run the storybook examples for ${name} + `, + } + ); +} diff --git a/packages/kbn-storybook/lib/storybook_entry.js b/packages/kbn-storybook/lib/storybook_entry.js deleted file mode 100644 index fc970b1ff9d2a..0000000000000 --- a/packages/kbn-storybook/lib/storybook_entry.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const { resolve, relative, dirname } = require('path'); -const Fs = require('fs'); -const Rx = require('rxjs'); -const { mergeMap, map, debounceTime } = require('rxjs/operators'); -const normalize = require('normalize-path'); -const { promisify } = require('util'); - -const watch = require('glob-watcher'); -const mkdirp = require('mkdirp'); // eslint-disable-line -const glob = require('fast-glob'); -const { REPO_ROOT } = require('@kbn/utils'); - -const mkdirpAsync = promisify(mkdirp); -const writeFileAsync = promisify(Fs.writeFile); - -const { STORY_ENTRY_PATH } = require('./constants'); -const STORE_ENTRY_DIR = dirname(STORY_ENTRY_PATH); - -exports.generateStorybookEntry = ({ log, storyGlobs }) => { - const globs = [...storyGlobs]; - log.info('Storybook globs:\n', globs); - const norm = (p) => normalize(relative(STORE_ENTRY_DIR, p)); - - return Rx.defer(() => - glob(globs, { - absolute: true, - cwd: REPO_ROOT, - onlyFiles: true, - }) - ).pipe( - map((paths) => { - log.info('Discovered Storybook entry points:\n', paths); - return new Set(paths.map(norm)); - }), - mergeMap( - (paths) => - new Rx.Observable((observer) => { - observer.next(paths); - - const chokidar = watch(globs, { cwd: REPO_ROOT }) - .on('add', (path) => { - observer.next(paths.add(norm(resolve(REPO_ROOT, path)))); - }) - .on('unlink', (path) => { - observer.next(paths.delete(norm(resolve(REPO_ROOT, path)))); - }); - - return () => { - chokidar.close(); - }; - }) - ), - debounceTime(200), - mergeMap(async (paths, i) => { - await mkdirpAsync(STORE_ENTRY_DIR); - - let content = ''; - for (const path of paths) { - content += `require('${path}');\n`; - } - - await writeFileAsync(STORY_ENTRY_PATH, content); - - if (i === 0) { - log.info('%d paths written to entry file', paths.size); - } else { - log.info('entry file updated'); - } - }) - ); -}; diff --git a/packages/kbn-storybook/lib/templates/index.ejs b/packages/kbn-storybook/lib/templates/index.ejs new file mode 100644 index 0000000000000..a4f8204c95d7a --- /dev/null +++ b/packages/kbn-storybook/lib/templates/index.ejs @@ -0,0 +1,59 @@ + + + + + + <%= options.title || 'Storybook'%> + + <% if (files.favicon) { %> + + <% } %> + + + + + + + + + + + + <% if (typeof headHtmlSnippet !== 'undefined') { %> <%= headHtmlSnippet %> <% } %> <% + files.css.forEach(file => { %> + + <% }); %> + + + + + <% if (typeof bodyHtmlSnippet !== 'undefined') { %> <%= bodyHtmlSnippet %> <% } %> + +
+
+ + <% if (typeof globals !== 'undefined' && Object.keys(globals).length) { %> + + <% } %> <% dlls.forEach(file => { %> + + <% }); %> <% files.js.forEach(file => { %> + + <% }); %> + + diff --git a/packages/kbn-storybook/lib/webpack.dll.config.js b/packages/kbn-storybook/lib/webpack.dll.config.js deleted file mode 100644 index 6e3b4d41bd7f0..0000000000000 --- a/packages/kbn-storybook/lib/webpack.dll.config.js +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const webpack = require('webpack'); -const path = require('path'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const { REPO_ROOT } = require('@kbn/utils'); - -const { DLL_NAME, DLL_DIST_DIR } = require('./constants'); - -// This is the Webpack config for the DLL of CSS and JS assets that are -// not expected to change during development. This saves compile and run -// times considerably. -module.exports = { - context: REPO_ROOT, - mode: 'development', - - // This is a (potentially growing) list of modules that can be safely - // included in the DLL. Only add to this list modules or other code - // which Storybook stories and their components would require, but don't - // change during development. - entry: [ - '@elastic/eui/dist/eui_theme_light.css', - '@kbn/ui-framework/dist/kui_light.css', - '@storybook/addon-info', - '@storybook/addon-knobs', - '@storybook/addon-knobs/react', - '@storybook/addon-knobs/register', - '@storybook/addon-options', - '@storybook/addon-options/register', - '@storybook/core', - '@storybook/core/dist/server/common/polyfills.js', - '@storybook/react', - '@storybook/theming', - 'angular-mocks', - 'angular', - 'brace', - 'chroma-js', - 'highlight.js', - 'html-entities', - 'jquery', - 'lodash', - 'markdown-it', - 'mocha', - 'prop-types', - 'react-ace', - 'react-beautiful-dnd', - 'react-dom', - 'react-focus-lock', - 'react-markdown', - 'react-resize-detector', - 'react-virtualized', - 'react', - 'recompose', - 'redux-actions', - 'remark-parse', - 'rxjs', - 'sinon', - 'tinycolor2', - ], - plugins: [ - // Produce the DLL and its manifest - new webpack.DllPlugin({ - name: DLL_NAME, - path: path.resolve(DLL_DIST_DIR, 'manifest.json'), - }), - // Produce the DLL CSS file - new MiniCssExtractPlugin({ - filename: 'dll.css', - }), - ], - // Output the DLL JS file - output: { - path: DLL_DIST_DIR, - filename: 'dll.js', - library: DLL_NAME, - }, - // Include a require alias for legacy UI code and styles - resolve: { - alias: { - ui: path.resolve(REPO_ROOT, 'src/legacy/ui/public'), - }, - mainFields: ['browser', 'main'], - }, - module: { - rules: [ - { - test: /\.css$/, - use: [ - { - loader: MiniCssExtractPlugin.loader, - options: {}, - }, - { loader: 'css-loader' }, - { - loader: 'string-replace-loader', - options: { - search: '__REPLACE_WITH_PUBLIC_PATH__', - replace: '/', - flags: 'g', - }, - }, - ], - }, - { - test: /\.(woff|woff2|ttf|eot|svg|ico)(\?|$)/, - loader: 'file-loader', - }, - ], - }, - node: { - fs: 'empty', - child_process: 'empty', - }, -}; diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 5271ddb96d842..05fdb8489a1c3 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -3,18 +3,21 @@ "version": "1.0.0", "private": true, "license": "Apache-2.0", + "main": "./target/index.js", "dependencies": { - "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@storybook/addon-actions": "^6.0.16", + "@storybook/addon-essentials": "^6.0.16", + "@storybook/addon-knobs": "^6.0.16", + "@storybook/addon-storyshots": "^6.0.16", + "@storybook/core": "^6.0.16", + "@storybook/react": "^6.0.16", + "@storybook/theming": "^6.0.16", + "@types/loader-utils": "^2.0.1", + "@types/webpack": "^4.41.5", + "@types/webpack-env": "^1.15.2", + "@types/webpack-merge": "^4.1.5", "@kbn/utils": "1.0.0", - "@storybook/addon-actions": "^5.3.19", - "@storybook/addon-console": "^1.2.1", - "@storybook/addon-info": "^5.3.19", - "@storybook/addon-knobs": "^5.3.19", - "@storybook/addon-options": "^5.3.19", - "@storybook/addon-storyshots": "^5.3.19", - "@storybook/react": "^5.3.19", - "@storybook/theming": "^5.3.19", "babel-loader": "^8.0.6", "copy-webpack-plugin": "^6.0.2", "fast-glob": "2.2.7", @@ -22,12 +25,17 @@ "jest-specific-snapshot": "2.0.0", "jest-styled-components": "^7.0.2", "mkdirp": "0.5.1", - "mini-css-extract-plugin": "0.7.0", - "normalize-path": "3.0.0", - "react-docgen-typescript-loader": "3.1.0", - "rxjs": "6.5.5", + "mini-css-extract-plugin": "0.8.0", + "normalize-path": "^3.0.0", + "react-docgen-typescript-loader": "^3.1.1", + "rxjs": "^6.5.5", "serve-static": "1.14.1", "styled-components": "^5.1.0", "webpack": "^4.41.5" + }, + "scripts": { + "build": "tsc", + "kbn:bootstrap": "yarn build", + "watch": "yarn build --watch" } -} \ No newline at end of file +} diff --git a/packages/kbn-storybook/preset.js b/packages/kbn-storybook/preset.js new file mode 100644 index 0000000000000..8c17f78e208d8 --- /dev/null +++ b/packages/kbn-storybook/preset.js @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const webpackConfig = require('./target/webpack.config').default; + +module.exports = { + managerEntries: (entry = []) => { + return [...entry, require.resolve('./target/lib/register')]; + }, + webpackFinal: (config) => { + return webpackConfig({ config }); + }, +}; diff --git a/packages/kbn-storybook/storybook_config/addons.js b/packages/kbn-storybook/storybook_config/addons.js deleted file mode 100644 index f439d1d8892f8..0000000000000 --- a/packages/kbn-storybook/storybook_config/addons.js +++ /dev/null @@ -1,23 +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 '@storybook/addon-options/register'; -import '@storybook/addon-actions/register'; -import '@storybook/addon-knobs/register'; -import '@storybook/addon-console'; diff --git a/packages/kbn-storybook/storybook_config/config.js b/packages/kbn-storybook/storybook_config/config.js deleted file mode 100644 index d97bd3f7c2dcc..0000000000000 --- a/packages/kbn-storybook/storybook_config/config.js +++ /dev/null @@ -1,68 +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 { configure, addDecorator, addParameters } from '@storybook/react'; -import { withKnobs } from '@storybook/addon-knobs/react'; -import { withInfo } from '@storybook/addon-info'; -import { create } from '@storybook/theming'; - -// If we're running Storyshots, be sure to register the require context hook. -// Otherwise, add the other decorators. -if (process.env.NODE_ENV === 'test') { - // eslint-disable-next-line - require('babel-plugin-require-context-hook/register')(); -} else { - // Customize the info for each story. - addDecorator( - withInfo({ - inline: true, - styles: { - infoBody: { - margin: 20, - }, - infoStory: { - margin: '40px 60px', - }, - }, - }) - ); - - // Add optional knobs to customize each story. - addDecorator(withKnobs); -} - -// Set up the Storybook environment with custom settings. -addParameters({ - options: { - theme: create({ - base: 'light', - brandTitle: 'Kibana Storybook', - brandUrl: 'https://github.com/elastic/kibana/tree/master/packages/kbn-storybook', - }), - showPanel: false, - isFullscreen: false, - panelPosition: 'bottom', - isToolshown: true, - }, -}); - -configure(() => { - // eslint-disable-next-line - require('../../../built_assets/storybook/stories.entry.js'); -}, module); diff --git a/packages/kbn-storybook/storybook_config/middleware.js b/packages/kbn-storybook/storybook_config/middleware.js deleted file mode 100644 index 9410bb66030d9..0000000000000 --- a/packages/kbn-storybook/storybook_config/middleware.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const serve = require('serve-static'); -const path = require('path'); - -// Extend the Storybook Middleware to include a route to access Legacy UI assets -module.exports = function (router) { - router.get('/ui', serve(path.resolve(__dirname, '../../../src/core/server/core_app/assets'))); -}; diff --git a/packages/kbn-storybook/storybook_config/mocks/absolute_to_parsed_url.js b/packages/kbn-storybook/storybook_config/mocks/absolute_to_parsed_url.js deleted file mode 100644 index 65a27b095f84e..0000000000000 --- a/packages/kbn-storybook/storybook_config/mocks/absolute_to_parsed_url.js +++ /dev/null @@ -1,23 +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 const absoluteToParsedUrl = () => { - getAbsoluteUrl: () => - 'http://localhost:5601/kbp/app/canvas#/workpad/workpad-24d56dad-ae70-42b8-9ef1-c5350ecd426c/page/1'; -}; // noop diff --git a/packages/kbn-storybook/storybook_config/mocks/noop.js b/packages/kbn-storybook/storybook_config/mocks/noop.js deleted file mode 100755 index e78d222eaa560..0000000000000 --- a/packages/kbn-storybook/storybook_config/mocks/noop.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export default function () {} diff --git a/packages/kbn-storybook/storybook_config/mocks/state_store.js b/packages/kbn-storybook/storybook_config/mocks/state_store.js deleted file mode 100644 index 11bdf6632321d..0000000000000 --- a/packages/kbn-storybook/storybook_config/mocks/state_store.js +++ /dev/null @@ -1,26 +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 function getState() { - return { - assets: { - yay: { value: 'here is your image' }, - }, - }; -} diff --git a/packages/kbn-storybook/storybook_config/mocks/ui_storage.js b/packages/kbn-storybook/storybook_config/mocks/ui_storage.js deleted file mode 100644 index 4bd8cdeddfc22..0000000000000 --- a/packages/kbn-storybook/storybook_config/mocks/ui_storage.js +++ /dev/null @@ -1,28 +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 class Storage { - get(key) { - return this[key]; - } - - set(key, value) { - this[key] = value; - } -} diff --git a/packages/kbn-storybook/storybook_config/preview-head.html b/packages/kbn-storybook/storybook_config/preview-head.html deleted file mode 100644 index 16754ad550da0..0000000000000 --- a/packages/kbn-storybook/storybook_config/preview-head.html +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/packages/kbn-storybook/storybook_config/webpack.config.js b/packages/kbn-storybook/storybook_config/webpack.config.js deleted file mode 100644 index 60b6b6add66d1..0000000000000 --- a/packages/kbn-storybook/storybook_config/webpack.config.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const { resolve } = require('path'); -const webpack = require('webpack'); -const webpackMerge = require('webpack-merge'); -const { stringifyRequest } = require('loader-utils'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const { REPO_ROOT } = require('@kbn/utils'); -const { DLL_DIST_DIR } = require('../lib/constants'); -// eslint-disable-next-line import/no-unresolved -const { currentConfig } = require('../../../built_assets/storybook/current.config'); - -// Extend the Storybook Webpack config with some customizations -module.exports = async ({ config: storybookConfig }) => { - let config = { - module: { - rules: [ - // Include the React preset from Kibana for JS(X) and TS(X) - { - test: /\.(j|t)sx?$/, - exclude: /node_modules/, - loaders: 'babel-loader', - options: { - presets: [require.resolve('@kbn/babel-preset/webpack_preset')], - }, - }, - { - test: /\.(html|md|txt|tmpl)$/, - use: { - loader: 'raw-loader', - }, - }, - // Parse props data for .tsx files - // This is notoriously slow, and is making Storybook unusable. Disabling for now. - // See: https://github.com/storybookjs/storybook/issues/7998 - // - // { - // test: /\.tsx$/, - // // Exclude example files, as we don't display props info for them - // exclude: /\.stories.tsx$/, - // use: [ - // // Parse TS comments to create Props tables in the UI - // require.resolve('react-docgen-typescript-loader'), - // ], - // }, - { - test: /\.scss$/, - exclude: /\.module.(s(a|c)ss)$/, - use: [ - { loader: 'style-loader' }, - { loader: 'css-loader', options: { importLoaders: 2 } }, - { - loader: 'postcss-loader', - options: { - config: { - path: require.resolve('@kbn/optimizer/postcss.config.js'), - }, - }, - }, - { - loader: 'sass-loader', - options: { - prependData(loaderContext) { - return `@import ${stringifyRequest( - loaderContext, - resolve(REPO_ROOT, 'src/core/public/core_app/styles/_globals_v7light.scss') - )};\n`; - }, - sassOptions: { - includePaths: [resolve(REPO_ROOT, 'node_modules')], - }, - }, - }, - ], - }, - ], - }, - plugins: [ - // Reference the built DLL file of static(ish) dependencies, which are removed - // during kbn:bootstrap and rebuilt if missing. - new webpack.DllReferencePlugin({ - manifest: resolve(DLL_DIST_DIR, 'manifest.json'), - context: REPO_ROOT, - }), - // Copy the DLL files to the Webpack build for use in the Storybook UI - - new CopyWebpackPlugin({ - patterns: [ - { - from: resolve(DLL_DIST_DIR, 'dll.js'), - to: 'dll.js', - }, - { - from: resolve(DLL_DIST_DIR, 'dll.css'), - to: 'dll.css', - }, - ], - }), - ], - resolve: { - // Tell Webpack about the ts/x extensions - extensions: ['.ts', '.tsx', '.scss'], - alias: { - core_app_image_assets: resolve(REPO_ROOT, 'src/core/public/core_app/images'), - }, - }, - }; - - // Find and alter the CSS rule to replace the Kibana public path string with a path - // to the route we've added in middleware.js - const cssRule = storybookConfig.module.rules.find((rule) => rule.test.source.includes('.css$')); - cssRule.use.push({ - loader: 'string-replace-loader', - options: { - search: '__REPLACE_WITH_PUBLIC_PATH__', - replace: '/', - flags: 'g', - }, - }); - - config = webpackMerge(storybookConfig, config); - - // Load custom Webpack config specified by a plugin. - if (currentConfig.webpackHook) { - // eslint-disable-next-line import/no-dynamic-require - return await require(currentConfig.webpackHook)({ config }); - } - - return config; -}; diff --git a/packages/kbn-storybook/tsconfig.json b/packages/kbn-storybook/tsconfig.json new file mode 100644 index 0000000000000..814a3963c9f49 --- /dev/null +++ b/packages/kbn-storybook/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "declaration": true, + "outDir": "target", + "skipLibCheck": true + }, + "include": ["*.ts", "lib/*.ts"] +} diff --git a/packages/kbn-storybook/typings.d.ts b/packages/kbn-storybook/typings.d.ts new file mode 100644 index 0000000000000..a20af34a0eb06 --- /dev/null +++ b/packages/kbn-storybook/typings.d.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. + */ + +// Storybook react doesn't declare this in its typings, but it's there. +declare module '@storybook/react/standalone'; diff --git a/packages/kbn-storybook/webpack.config.ts b/packages/kbn-storybook/webpack.config.ts new file mode 100644 index 0000000000000..98fca597ffd78 --- /dev/null +++ b/packages/kbn-storybook/webpack.config.ts @@ -0,0 +1,108 @@ +/* + * 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 { resolve } from 'path'; +import { stringifyRequest } from 'loader-utils'; +import { Configuration, Stats } from 'webpack'; +import webpackMerge from 'webpack-merge'; +import { externals } from '@kbn/ui-shared-deps'; +import { REPO_ROOT } from './lib/constants'; + +const stats = { + ...Stats.presetToOptions('minimal'), + colors: true, + errorDetails: true, + errors: true, + moduleTrace: true, + warningsFilter: /(export .* was not found in)|(entrypoint size limit)/, +}; + +// Extend the Storybook Webpack config with some customizations +/* eslint-disable import/no-default-export */ +export default function ({ config: storybookConfig }: { config: Configuration }) { + const config = { + devServer: { + stats, + }, + externals, + module: { + rules: [ + { + test: /\.(html|md|txt|tmpl)$/, + use: { + loader: 'raw-loader', + }, + }, + { + test: /\.scss$/, + exclude: /\.module.(s(a|c)ss)$/, + use: [ + { loader: 'style-loader' }, + { loader: 'css-loader', options: { importLoaders: 2 } }, + { + loader: 'postcss-loader', + options: { + config: { + path: require.resolve('@kbn/optimizer/postcss.config.js'), + }, + }, + }, + { + loader: 'sass-loader', + options: { + prependData(loaderContext: any) { + return `@import ${stringifyRequest( + loaderContext, + resolve(REPO_ROOT, 'src/core/public/core_app/styles/_globals_v7light.scss') + )};\n`; + }, + sassOptions: { + includePaths: [resolve(REPO_ROOT, 'node_modules')], + }, + }, + }, + ], + }, + ], + }, + resolve: { + // Tell Webpack about the scss extension + extensions: ['.scss'], + alias: { + core_app_image_assets: resolve(REPO_ROOT, 'src/core/public/core_app/images'), + }, + }, + stats, + }; + + // This is the hacky part. We find something that looks like the + // HtmlWebpackPlugin and mutate its `options.template` to point at our + // revised template. + const htmlWebpackPlugin: any = (storybookConfig.plugins || []).find((plugin: any) => { + return plugin.options && typeof plugin.options.template === 'string'; + }); + if (htmlWebpackPlugin) { + htmlWebpackPlugin.options.template = require.resolve('../lib/templates/index.ejs'); + } + + // @ts-expect-error There's a long error here about the types of the + // incompatibility of Configuration, but it looks like it just may be Webpack + // type definition related. + return webpackMerge(storybookConfig, config); +} diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json b/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json index 2e69d3625d7ff..51e5df9bf7dc0 100644 --- a/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/mock_schema.json @@ -35,16 +35,19 @@ } }, "my_array": { - "properties": { - "total": { - "type": "number" - }, - "type": { - "type": "boolean" + "type": "array", + "items": { + "properties": { + "total": { + "type": "number" + }, + "type": { + "type": "boolean" + } } } }, - "my_str_array": { "type": "keyword" } + "my_str_array": { "type": "array", "items": { "type": "keyword" } } } } } diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_schema_defined_with_spreads_collector.ts b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_schema_defined_with_spreads_collector.ts new file mode 100644 index 0000000000000..833344fa368b0 --- /dev/null +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_schema_defined_with_spreads_collector.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. + */ + +import { SyntaxKind } from 'typescript'; +import { ParsedUsageCollection } from '../ts_parser'; + +export const parsedSchemaDefinedWithSpreadsCollector: ParsedUsageCollection = [ + 'src/fixtures/telemetry_collectors/schema_defined_with_spreads_collector.ts', + { + collectorName: 'schema_defined_with_spreads', + schema: { + value: { + flat: { + type: 'keyword', + }, + my_str: { + type: 'text', + }, + my_objects: { + total: { + type: 'number', + }, + type: { + type: 'boolean', + }, + }, + }, + }, + fetch: { + typeName: 'Usage', + typeDescriptor: { + flat: { + kind: SyntaxKind.StringKeyword, + type: 'StringKeyword', + }, + my_str: { + kind: SyntaxKind.StringKeyword, + type: 'StringKeyword', + }, + my_objects: { + total: { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', + }, + type: { + kind: SyntaxKind.BooleanKeyword, + type: 'BooleanKeyword', + }, + }, + }, + }, + }, +]; diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts index b238c5aa346ad..acf984b7d10ee 100644 --- a/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/parsed_working_collector.ts @@ -55,12 +55,15 @@ export const parsedWorkingCollector: ParsedUsageCollection = [ }, }, my_array: { - total: { - type: 'number', + type: 'array', + items: { + total: { + type: 'number', + }, + type: { type: 'boolean' }, }, - type: { type: 'boolean' }, }, - my_str_array: { type: 'keyword' }, + my_str_array: { type: 'array', items: { type: 'keyword' } }, }, }, fetch: { @@ -75,11 +78,9 @@ export const parsedWorkingCollector: ParsedUsageCollection = [ type: 'StringKeyword', }, my_index_signature_prop: { - '': { - '@@INDEX@@': { - kind: SyntaxKind.NumberKeyword, - type: 'NumberKeyword', - }, + '@@INDEX@@': { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', }, }, my_objects: { @@ -93,18 +94,22 @@ export const parsedWorkingCollector: ParsedUsageCollection = [ }, }, my_array: { - total: { - kind: SyntaxKind.NumberKeyword, - type: 'NumberKeyword', - }, - type: { - kind: SyntaxKind.BooleanKeyword, - type: 'BooleanKeyword', + items: { + total: { + kind: SyntaxKind.NumberKeyword, + type: 'NumberKeyword', + }, + type: { + kind: SyntaxKind.BooleanKeyword, + type: 'BooleanKeyword', + }, }, }, my_str_array: { - kind: SyntaxKind.StringKeyword, - type: 'StringKeyword', + items: { + kind: SyntaxKind.StringKeyword, + type: 'StringKeyword', + }, }, }, }, diff --git a/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap b/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap index 68b068b0cfe06..4725be77533af 100644 --- a/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap +++ b/packages/kbn-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap @@ -96,16 +96,14 @@ Array [ "collectorName": "indexed_interface_with_not_matching_schema", "fetch": Object { "typeDescriptor": Object { - "": Object { - "@@INDEX@@": Object { - "count_1": Object { - "kind": 143, - "type": "NumberKeyword", - }, - "count_2": Object { - "kind": 143, - "type": "NumberKeyword", - }, + "@@INDEX@@": Object { + "count_1": Object { + "kind": 143, + "type": "NumberKeyword", + }, + "count_2": Object { + "kind": 143, + "type": "NumberKeyword", }, }, }, @@ -145,16 +143,16 @@ Array [ }, ], Array [ - "src/fixtures/telemetry_collectors/working_collector.ts", + "src/fixtures/telemetry_collectors/schema_defined_with_spreads_collector.ts", Object { - "collectorName": "my_working_collector", + "collectorName": "schema_defined_with_spreads", "fetch": Object { "typeDescriptor": Object { "flat": Object { "kind": 146, "type": "StringKeyword", }, - "my_array": Object { + "my_objects": Object { "total": Object { "kind": 143, "type": "NumberKeyword", @@ -164,12 +162,59 @@ Array [ "type": "BooleanKeyword", }, }, - "my_index_signature_prop": Object { - "": Object { - "@@INDEX@@": Object { + "my_str": Object { + "kind": 146, + "type": "StringKeyword", + }, + }, + "typeName": "Usage", + }, + "schema": Object { + "value": Object { + "flat": Object { + "type": "keyword", + }, + "my_objects": Object { + "total": Object { + "type": "number", + }, + "type": Object { + "type": "boolean", + }, + }, + "my_str": Object { + "type": "text", + }, + }, + }, + }, + ], + Array [ + "src/fixtures/telemetry_collectors/working_collector.ts", + Object { + "collectorName": "my_working_collector", + "fetch": Object { + "typeDescriptor": Object { + "flat": Object { + "kind": 146, + "type": "StringKeyword", + }, + "my_array": Object { + "items": Object { + "total": Object { "kind": 143, "type": "NumberKeyword", }, + "type": Object { + "kind": 131, + "type": "BooleanKeyword", + }, + }, + }, + "my_index_signature_prop": Object { + "@@INDEX@@": Object { + "kind": 143, + "type": "NumberKeyword", }, }, "my_objects": Object { @@ -187,8 +232,10 @@ Array [ "type": "StringKeyword", }, "my_str_array": Object { - "kind": 146, - "type": "StringKeyword", + "items": Object { + "kind": 146, + "type": "StringKeyword", + }, }, }, "typeName": "Usage", @@ -199,12 +246,15 @@ Array [ "type": "keyword", }, "my_array": Object { - "total": Object { - "type": "number", - }, - "type": Object { - "type": "boolean", + "items": Object { + "total": Object { + "type": "number", + }, + "type": Object { + "type": "boolean", + }, }, + "type": "array", }, "my_index_signature_prop": Object { "avg": Object { @@ -232,7 +282,10 @@ Array [ "type": "text", }, "my_str_array": Object { - "type": "keyword", + "items": Object { + "type": "keyword", + }, + "type": "array", }, }, }, diff --git a/packages/kbn-telemetry-tools/src/tools/check_collector_integrity.ts b/packages/kbn-telemetry-tools/src/tools/check_collector_integrity.ts index 3205edb87aa29..8a5752f77d7fc 100644 --- a/packages/kbn-telemetry-tools/src/tools/check_collector_integrity.ts +++ b/packages/kbn-telemetry-tools/src/tools/check_collector_integrity.ts @@ -47,6 +47,7 @@ export function checkCompatibleTypeDescriptor( const typeDescriptorKinds = reduce( typeDescriptorTypes, (acc: any, type: number, key: string) => { + key = key.replace(/'/g, ''); try { acc[key] = kindToDescriptorName(type); } catch (err) { @@ -61,6 +62,7 @@ export function checkCompatibleTypeDescriptor( const transformedMappingKinds = reduce( schemaTypes, (acc: any, type: string, key: string) => { + key = key.replace(/'/g, ''); try { acc[key.replace(/.type$/, '.kind')] = compatibleSchemaTypes(type as any); } catch (err) { diff --git a/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts b/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts index 0517cb9034d0a..b03db75b219f6 100644 --- a/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/extract_collectors.test.ts @@ -34,7 +34,7 @@ describe('extractCollectors', () => { const programPaths = await getProgramPaths(configs[0]); const results = [...extractCollectors(programPaths, tsConfig)]; - expect(results).toHaveLength(7); + expect(results).toHaveLength(8); expect(results).toMatchSnapshot(); }); }); diff --git a/packages/kbn-telemetry-tools/src/tools/manage_schema.ts b/packages/kbn-telemetry-tools/src/tools/manage_schema.ts index d422837140d80..7721492fdb691 100644 --- a/packages/kbn-telemetry-tools/src/tools/manage_schema.ts +++ b/packages/kbn-telemetry-tools/src/tools/manage_schema.ts @@ -28,7 +28,7 @@ export type AllowedSchemaTypes = | 'date' | 'float'; -export function compatibleSchemaTypes(type: AllowedSchemaTypes) { +export function compatibleSchemaTypes(type: AllowedSchemaTypes | 'array') { switch (type) { case 'keyword': case 'text': @@ -40,6 +40,8 @@ export function compatibleSchemaTypes(type: AllowedSchemaTypes) { case 'float': case 'long': return 'number'; + case 'array': + return 'array'; default: throw new Error(`Unknown schema type ${type}`); } @@ -66,10 +68,22 @@ export function isObjectMapping(entity: any) { return false; } +function isArrayMapping(entity: any): entity is { type: 'array'; items: object } { + return typeof entity === 'object' && entity.type === 'array' && typeof entity.items === 'object'; +} + +function getValueMapping(value: any) { + return isObjectMapping(value) ? transformToEsMapping(value) : value; +} + function transformToEsMapping(usageMappingValue: any) { const fieldMapping: any = { properties: {} }; for (const [key, value] of Object.entries(usageMappingValue)) { - fieldMapping.properties[key] = isObjectMapping(value) ? transformToEsMapping(value) : value; + if (isArrayMapping(value)) { + fieldMapping.properties[key] = { ...value, items: getValueMapping(value.items) }; + } else { + fieldMapping.properties[key] = getValueMapping(value); + } } return fieldMapping; } diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts index 9475574a44219..652b26c8edb23 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts @@ -44,13 +44,13 @@ export function loadFixtureProgram(fixtureName: string) { } describe('getDescriptor', () => { - const usageInterfaces = new Map(); + const usageInterfaces = new Map(); let tsProgram: ts.Program; beforeAll(() => { const { program, sourceFile } = loadFixtureProgram('constants'); tsProgram = program; for (const node of traverseNodes(sourceFile)) { - if (ts.isInterfaceDeclaration(node)) { + if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) { const interfaceName = node.name.getText(); usageInterfaces.set(interfaceName, node); } @@ -84,8 +84,8 @@ describe('getDescriptor', () => { expect(descriptor).toEqual({ prop1: { kind: TelemetryKinds.MomentDate, type: 'MomentDate' }, prop2: { kind: TelemetryKinds.MomentDate, type: 'MomentDate' }, - prop3: { kind: TelemetryKinds.MomentDate, type: 'MomentDate' }, - prop4: { kind: TelemetryKinds.Date, type: 'Date' }, + prop3: { items: { kind: TelemetryKinds.MomentDate, type: 'MomentDate' } }, + prop4: { items: { kind: TelemetryKinds.Date, type: 'Date' } }, }); }); @@ -102,4 +102,40 @@ describe('getDescriptor', () => { 'Mapping does not support conflicting union types.' ); }); + + it('serializes TypeAliasDeclaration', () => { + const usageInterface = usageInterfaces.get('TypeAliasWithUnion')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + locale: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + prop1: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + prop2: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + prop3: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + prop4: { kind: ts.SyntaxKind.StringLiteral, type: 'StringLiteral' }, + prop5: { kind: ts.SyntaxKind.FirstLiteralToken, type: 'FirstLiteralToken' }, + }); + }); + + it('serializes Record entries', () => { + const usageInterface = usageInterfaces.get('TypeAliasWithRecord')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + locale: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + '@@INDEX@@': { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }); + }); + + it('serializes MappedTypes', () => { + const usageInterface = usageInterfaces.get('MappedTypes')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + mappedTypeWithExternallyDefinedProps: { + prop1: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop2: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }, + mappedTypeWithOneInlineProp: { + prop3: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }, + }); + }); }); diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index 7afe828298b4b..cd845a680ad06 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -71,6 +71,33 @@ export function kindToDescriptorName(kind: number) { } } +export function getConstraints(node: ts.Node, program: ts.Program): any { + if (ts.isTypeReferenceNode(node)) { + const typeChecker = program.getTypeChecker(); + const symbol = typeChecker.getSymbolAtLocation(node.typeName); + const declaration = (symbol?.getDeclarations() || [])[0]; + if (declaration) { + return getConstraints(declaration, program); + } + return getConstraints(node.typeName, program); + } + + if (ts.isTypeAliasDeclaration(node)) { + return getConstraints(node.type, program); + } + + if (ts.isUnionTypeNode(node)) { + const types = node.types.filter(discardNullOrUndefined); + return types.map((typeNode) => getConstraints(typeNode, program)); + } + + if (ts.isLiteralTypeNode(node) && ts.isLiteralExpression(node.literal)) { + return node.literal.text; + } + + throw Error(`Unsupported constraint`); +} + export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | DescriptorValue { if (ts.isMethodSignature(node) || ts.isPropertySignature(node)) { if (node.type) { @@ -79,14 +106,29 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | } if (ts.isTypeLiteralNode(node) || ts.isInterfaceDeclaration(node)) { return node.members.reduce((acc, m) => { - acc[m.name?.getText() || ''] = getDescriptor(m, program); - return acc; - }, {} as any); + const key = m.name?.getText(); + if (key) { + return { ...acc, [key]: getDescriptor(m, program) }; + } else { + return { ...acc, ...getDescriptor(m, program) }; + } + }, {}); } // If it's defined as signature { [key: string]: OtherInterface } - if (ts.isIndexSignatureDeclaration(node) && node.type) { - return { '@@INDEX@@': getDescriptor(node.type, program) }; + if ((ts.isIndexSignatureDeclaration(node) || ts.isMappedTypeNode(node)) && node.type) { + const descriptor = getDescriptor(node.type, program); + + // If we know the constraints of `string` ({ [key in 'prop1' | 'prop2']: value }) + const constraint = (node as ts.MappedTypeNode).typeParameter?.constraint; + if (constraint) { + const constraints = getConstraints(constraint, program); + const constraintsArray = Array.isArray(constraints) ? constraints : [constraints]; + if (typeof constraintsArray[0] === 'string') { + return constraintsArray.reduce((acc, c) => ({ ...acc, [c]: descriptor }), {}); + } + } + return { '@@INDEX@@': descriptor }; } if (ts.SyntaxKind.FirstNode === node.kind) { @@ -114,6 +156,10 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | if (symbolName === 'Date') { return { kind: TelemetryKinds.Date, type: 'Date' }; } + // Support `Record` + if (symbolName === 'Record' && node.typeArguments![0].kind === ts.SyntaxKind.StringKeyword) { + return { '@@INDEX@@': getDescriptor(node.typeArguments![1], program) }; + } const declaration = (symbol?.getDeclarations() || [])[0]; if (declaration) { return getDescriptor(declaration, program); @@ -131,7 +177,7 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | } if (ts.isArrayTypeNode(node)) { - return getDescriptor(node.elementType, program); + return { items: getDescriptor(node.elementType, program) }; } if (ts.isLiteralTypeNode(node)) { @@ -157,6 +203,19 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | return uniqueKinds[0]; } + // Support `type MyUsageType = SomethingElse` + if (ts.isTypeAliasDeclaration(node)) { + return getDescriptor(node.type, program); + } + + // Support `&` unions + if (ts.isIntersectionTypeNode(node)) { + return node.types.reduce( + (acc, unionNode) => ({ ...acc, ...getDescriptor(unionNode, program) }), + {} + ); + } + switch (node.kind) { case ts.SyntaxKind.NumberKeyword: case ts.SyntaxKind.BooleanKeyword: diff --git a/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts b/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts index b7ca33a7bcd74..d036b93a7bbf9 100644 --- a/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/ts_parser.test.ts @@ -25,6 +25,7 @@ import { parsedNestedCollector } from './__fixture__/parsed_nested_collector'; import { parsedExternallyDefinedCollector } from './__fixture__/parsed_externally_defined_collector'; import { parsedImportedUsageInterface } from './__fixture__/parsed_imported_usage_interface'; import { parsedImportedSchemaCollector } from './__fixture__/parsed_imported_schema'; +import { parsedSchemaDefinedWithSpreadsCollector } from './__fixture__/parsed_schema_defined_with_spreads_collector'; export function loadFixtureProgram(fixtureName: string) { const fixturePath = path.resolve( @@ -62,6 +63,12 @@ describe('parseUsageCollection', () => { expect(result).toEqual([parsedWorkingCollector]); }); + it('parses collector with schema defined as union of spreads', () => { + const { program, sourceFile } = loadFixtureProgram('schema_defined_with_spreads_collector'); + const result = [...parseUsageCollection(sourceFile, program)]; + expect(result).toEqual([parsedSchemaDefinedWithSpreadsCollector]); + }); + it('parses nested collectors', () => { const { program, sourceFile } = loadFixtureProgram('nested_collector'); const result = [...parseUsageCollection(sourceFile, program)]; diff --git a/packages/kbn-telemetry-tools/src/tools/utils.ts b/packages/kbn-telemetry-tools/src/tools/utils.ts index 3d6764117374c..947a4f66908f6 100644 --- a/packages/kbn-telemetry-tools/src/tools/utils.ts +++ b/packages/kbn-telemetry-tools/src/tools/utils.ts @@ -100,42 +100,57 @@ export function getIdentifierDeclaration(node: ts.Node) { return getIdentifierDeclarationFromSource(node, source); } -export function getVariableValue(node: ts.Node): string | Record { +export function getVariableValue(node: ts.Node, program: ts.Program): string | Record { if (ts.isStringLiteral(node) || ts.isNumericLiteral(node)) { return node.text; } if (ts.isObjectLiteralExpression(node)) { - return serializeObject(node); + return serializeObject(node, program); } if (ts.isIdentifier(node)) { const declaration = getIdentifierDeclaration(node); if (ts.isVariableDeclaration(declaration) && declaration.initializer) { - return getVariableValue(declaration.initializer); + return getVariableValue(declaration.initializer, program); + } else { + // Go fetch it in another file + return getIdentifierValue(node, node, program, { chaseImport: true }); } - // TODO: If this is another imported value from another file, we'll need to go fetch it like in getPropertyValue } - throw Error(`Unsuppored Node: cannot get value of node (${node.getText()}) of kind ${node.kind}`); + if (ts.isSpreadAssignment(node)) { + return getVariableValue(node.expression, program); + } + + throw Error( + `Unsupported Node: cannot get value of node (${node.getText()}) of kind ${node.kind} [${ + ts.SyntaxKind[node.kind] + }]` + ); } -export function serializeObject(node: ts.Node) { +export function serializeObject(node: ts.Node, program: ts.Program) { if (!ts.isObjectLiteralExpression(node)) { throw new Error(`Expecting Object literal Expression got ${node.getText()}`); } - const value: Record = {}; + let value: Record = {}; for (const property of node.properties) { const propertyName = property.name?.getText(); + const val = ts.isPropertyAssignment(property) + ? getVariableValue(property.initializer, program) + : getVariableValue(property, program); + if (typeof propertyName === 'undefined') { - throw new Error(`Unable to get property name ${property.getText()}`); - } - const cleanPropertyName = propertyName.replace(/["']/g, ''); - if (ts.isPropertyAssignment(property)) { - value[cleanPropertyName] = getVariableValue(property.initializer); + if (typeof val === 'object') { + value = { ...value, ...val }; + } else { + throw new Error(`Unable to get property name ${property.getText()}`); + } } else { - value[cleanPropertyName] = getVariableValue(property); + const cleanPropertyName = propertyName.replace(/["']/g, ''); + value[cleanPropertyName] = val; } } @@ -155,45 +170,53 @@ export function getResolvedModuleSourceFile( return resolvedModuleSourceFile; } -export function getPropertyValue( +export function getIdentifierValue( node: ts.Node, + initializer: ts.Identifier, program: ts.Program, config: Optional<{ chaseImport: boolean }> = {} ) { const { chaseImport = false } = config; + const identifierName = initializer.getText(); + const declaration = getIdentifierDeclaration(initializer); + if (ts.isImportSpecifier(declaration)) { + if (!chaseImport) { + throw new Error( + `Value of node ${identifierName} is imported from another file. Chasing imports is not allowed.` + ); + } - if (ts.isPropertyAssignment(node)) { - const { initializer } = node; + const importedModuleName = getModuleSpecifier(declaration); - if (ts.isIdentifier(initializer)) { - const identifierName = initializer.getText(); - const declaration = getIdentifierDeclaration(initializer); - if (ts.isImportSpecifier(declaration)) { - if (!chaseImport) { - throw new Error( - `Value of node ${identifierName} is imported from another file. Chasing imports is not allowed.` - ); - } + const source = node.getSourceFile(); + const declarationSource = getResolvedModuleSourceFile(source, program, importedModuleName); + const declarationNode = getIdentifierDeclarationFromSource(initializer, declarationSource); + if (!ts.isVariableDeclaration(declarationNode)) { + throw new Error(`Expected ${identifierName} to be variable declaration.`); + } + if (!declarationNode.initializer) { + throw new Error(`Expected ${identifierName} to be initialized.`); + } + const serializedObject = serializeObject(declarationNode.initializer, program); + return serializedObject; + } - const importedModuleName = getModuleSpecifier(declaration); + return getVariableValue(declaration, program); +} - const source = node.getSourceFile(); - const declarationSource = getResolvedModuleSourceFile(source, program, importedModuleName); - const declarationNode = getIdentifierDeclarationFromSource(initializer, declarationSource); - if (!ts.isVariableDeclaration(declarationNode)) { - throw new Error(`Expected ${identifierName} to be variable declaration.`); - } - if (!declarationNode.initializer) { - throw new Error(`Expected ${identifierName} to be initialized.`); - } - const serializedObject = serializeObject(declarationNode.initializer); - return serializedObject; - } +export function getPropertyValue( + node: ts.Node, + program: ts.Program, + config: Optional<{ chaseImport: boolean }> = {} +) { + if (ts.isPropertyAssignment(node)) { + const { initializer } = node; - return getVariableValue(declaration); + if (ts.isIdentifier(initializer)) { + return getIdentifierValue(node, initializer, program, config); } - return getVariableValue(initializer); + return getVariableValue(initializer, program); } } @@ -249,7 +272,7 @@ export function difference(actual: any, expected: any) { function (result, value, key) { if (key && /@@INDEX@@/.test(`${key}`)) { // The type definition is an Index Signature, fuzzy searching for similar keys - const regexp = new RegExp(`${key}`.replace(/@@INDEX@@/g, '(.+)?')); + const regexp = new RegExp(`^${key}`.replace(/@@INDEX@@/g, '(.+)?')); const keysInBase = Object.keys(base) .map((k) => { const match = k.match(regexp); diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 8a4ff55dcf68f..c616c836d5ff4 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -26,15 +26,14 @@ "dedent": "^0.7.0", "del": "^5.1.0", "exit-hook": "^2.2.0", - "getopts": "^2.2.4", + "getopts": "^2.2.5", "glob": "^7.1.2", "joi": "^13.5.2", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "parse-link-header": "^1.0.1", "rxjs": "^6.5.5", "strip-ansi": "^5.2.0", "tar-fs": "^2.1.0", - "tmp": "^0.1.0", "xml2js": "^0.4.22", "zlib": "^1.0.5" } diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 363f97522a901..be18b7cfc0d01 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -17,7 +17,7 @@ "dependencies": { "classnames": "2.2.6", "focus-trap-react": "^3.1.1", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "prop-types": "^15.7.2", "react": "^16.12.0", "react-ace": "^5.9.0", @@ -31,13 +31,13 @@ }, "devDependencies": { "@babel/core": "^7.11.1", - "@elastic/eui": "0.0.55", + "@elastic/eui": "29.0.0", "@kbn/babel-preset": "1.0.0", "@kbn/optimizer": "1.0.0", "babel-loader": "^8.0.6", "brace": "0.11.1", "chalk": "^4.1.0", - "chokidar": "3.2.1", + "chokidar": "^3.4.2", "core-js": "^3.6.4", "css-loader": "^3.4.2", "expose-loader": "^0.7.5", @@ -62,7 +62,7 @@ "react-router": "^3.2.0", "react-router-redux": "^4.0.8", "redux": "3.7.2", - "redux-thunk": "2.2.0", + "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.3", "sass-loader": "^8.0.2", "sinon": "^7.4.2", diff --git a/packages/kbn-ui-shared-deps/entry.js b/packages/kbn-ui-shared-deps/entry.js index 365b84b83bd5f..69344174a2dc6 100644 --- a/packages/kbn-ui-shared-deps/entry.js +++ b/packages/kbn-ui-shared-deps/entry.js @@ -51,6 +51,8 @@ export const ElasticEui = require('@elastic/eui'); export const ElasticEuiLibServices = require('@elastic/eui/lib/services'); export const ElasticEuiLibServicesFormat = require('@elastic/eui/lib/services/format'); export const ElasticEuiChartsTheme = require('@elastic/eui/dist/eui_charts_theme'); +export const Lodash = require('lodash'); +export const LodashFp = require('lodash/fp'); import * as Theme from './theme.ts'; export { Theme }; diff --git a/packages/kbn-ui-shared-deps/index.js b/packages/kbn-ui-shared-deps/index.js index 84ca3435e02bc..a5d6954fd5cc0 100644 --- a/packages/kbn-ui-shared-deps/index.js +++ b/packages/kbn-ui-shared-deps/index.js @@ -62,5 +62,7 @@ exports.externals = { '@elastic/eui/dist/eui_charts_theme': '__kbnSharedDeps__.ElasticEuiChartsTheme', '@elastic/eui/dist/eui_theme_light.json': '__kbnSharedDeps__.Theme.euiLightVars', '@elastic/eui/dist/eui_theme_dark.json': '__kbnSharedDeps__.Theme.euiDarkVars', + lodash: '__kbnSharedDeps__.Lodash', + 'lodash/fp': '__kbnSharedDeps__.LodashFp', }; exports.publicPathLoader = require.resolve('./public_path_loader'); diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 372126c4418f5..278e8efd2d29e 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -20,6 +20,7 @@ "core-js": "^3.6.4", "custom-event-polyfill": "^0.3.0", "jquery": "^3.5.0", + "lodash": "^4.17.20", "mini-css-extract-plugin": "0.8.0", "moment": "^2.24.0", "moment-timezone": "^0.5.27", diff --git a/packages/kbn-ui-shared-deps/scripts/build.js b/packages/kbn-ui-shared-deps/scripts/build.js index af4e3481e624d..86251536b8876 100644 --- a/packages/kbn-ui-shared-deps/scripts/build.js +++ b/packages/kbn-ui-shared-deps/scripts/build.js @@ -18,18 +18,21 @@ */ const Path = require('path'); +const Fs = require('fs'); -const { run, createFailError } = require('@kbn/dev-utils'); +const { run, createFailError, CiStatsReporter } = require('@kbn/dev-utils'); const webpack = require('webpack'); const Stats = require('webpack/lib/Stats'); const del = require('del'); const { getWebpackConfig } = require('../webpack.config'); +const DIST_DIR = Path.resolve(__dirname, '../target'); + run( async ({ log, flags }) => { log.info('cleaning previous build output'); - await del(Path.resolve(__dirname, '../target')); + await del(DIST_DIR); const compiler = webpack( getWebpackConfig({ @@ -38,10 +41,38 @@ run( ); /** @param {webpack.Stats} stats */ - const onCompilationComplete = (stats) => { + const onCompilationComplete = async (stats) => { const took = Math.round((stats.endTime - stats.startTime) / 1000); if (!stats.hasErrors() && !stats.hasWarnings()) { + if (!flags.dev) { + const reporter = CiStatsReporter.fromEnv(log); + + const metrics = [ + { + group: '@kbn/ui-shared-deps asset size', + id: 'kbn-ui-shared-deps.js', + value: Fs.statSync(Path.resolve(DIST_DIR, 'kbn-ui-shared-deps.js')).size, + }, + { + group: '@kbn/ui-shared-deps asset size', + id: 'kbn-ui-shared-deps.@elastic.js', + value: Fs.statSync(Path.resolve(DIST_DIR, 'kbn-ui-shared-deps.@elastic.js')).size, + }, + { + group: '@kbn/ui-shared-deps asset size', + id: 'css', + value: + Fs.statSync(Path.resolve(DIST_DIR, 'kbn-ui-shared-deps.css')).size + + Fs.statSync(Path.resolve(DIST_DIR, 'kbn-ui-shared-deps.v7.light.css')).size, + }, + ]; + + log.debug('metrics:', metrics); + + await reporter.metrics(metrics); + } + log.success(`webpack completed in about ${took} seconds`); return; } @@ -56,11 +87,9 @@ run( if (flags.watch) { compiler.hooks.done.tap('report on stats', (stats) => { - try { - onCompilationComplete(stats); - } catch (error) { + onCompilationComplete(stats).catch((error) => { log.error(error.message); - } + }); }); compiler.hooks.watchRun.tap('report on start', () => { @@ -83,7 +112,7 @@ run( return; } - onCompilationComplete( + await onCompilationComplete( await new Promise((resolve, reject) => { compiler.run((error, stats) => { if (error) { diff --git a/renovate.json5 b/renovate.json5 index 57d0fcb9f8ce2..17391c2f83827 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -27,31 +27,36 @@ ], }, separateMajorMinor: false, - masterIssue: false, + masterIssue: true, rangeStrategy: 'bump', + semanticCommits: false, + vulnerabilityAlerts: { + enabled: false, + }, npm: { lockFileMaintenance: { enabled: false, }, packageRules: [ - { - groupSlug: '@elastic/charts', - packageNames: ['@elastic/charts'], - reviewers: ['markov00'], - }, { packagePatterns: [ '.*', ], enabled: false, }, + { + groupName: '@elastic/charts', + packageNames: ['@elastic/charts'], + reviewers: ['markov00'], + enabled: true, + }, + { + groupName: 'vega related modules', + packageNames: ['vega', 'vega-lite', 'vega-schema-url-parser', 'vega-tooltip'], + reviewers: ['team:kibana-app'], + labels: ['Feature:Lens', 'Team:KibanaApp'], + enabled: true, + }, ], }, - prConcurrentLimit: 0, - vulnerabilityAlerts: { - enabled: false, - }, - rebaseStalePrs: false, - rebaseConflictedPrs: false, - semanticCommits: false, } diff --git a/rfcs/text/0013_saved_object_migrations.md b/rfcs/text/0013_saved_object_migrations.md new file mode 100644 index 0000000000000..c5069625cb8a6 --- /dev/null +++ b/rfcs/text/0013_saved_object_migrations.md @@ -0,0 +1,745 @@ +- Start Date: 2020-05-11 +- RFC PR: (leave this empty) +- Kibana Issue: (leave this empty) + +--- +- [1. Summary](#1-summary) +- [2. Motivation](#2-motivation) +- [3. Saved Object Migration Errors](#3-saved-object-migration-errors) +- [4. Design](#4-design) + - [4.0 Assumptions and tradeoffs](#40-assumptions-and-tradeoffs) + - [4.1 Discover and remedy potential failures before any downtime](#41-discover-and-remedy-potential-failures-before-any-downtime) + - [4.2 Automatically retry failed migrations until they succeed](#42-automatically-retry-failed-migrations-until-they-succeed) + - [4.2.1 Idempotent migrations performed without coordination](#421-idempotent-migrations-performed-without-coordination) + - [4.2.1.1 Restrictions](#4211-restrictions) + - [4.2.1.2 Migration algorithm: Cloned index per version](#4212-migration-algorithm-cloned-index-per-version) + - [4.2.1.3 Upgrade and rollback procedure](#4213-upgrade-and-rollback-procedure) + - [4.2.1.4 Handling documents that belong to a disabled plugin](#4214-handling-documents-that-belong-to-a-disabled-plugin) +- [5. Alternatives](#5-alternatives) + - [5.1 Rolling upgrades](#51-rolling-upgrades) + - [5.2 Single node migrations coordinated through a lease/lock](#52-single-node-migrations-coordinated-through-a-leaselock) + - [5.2.1 Migration algorithm](#521-migration-algorithm) + - [5.2.2 Document lock algorithm](#522-document-lock-algorithm) + - [5.2.3 Checking for "weak lease" expiry](#523-checking-for-weak-lease-expiry) + - [5.3 Minimize data loss with mixed Kibana versions during 7.x](#53-minimize-data-loss-with-mixed-kibana-versions-during-7x) + - [5.4 In-place migrations that re-use the same index (8.0)](#54-in-place-migrations-that-re-use-the-same-index-80) + - [5.4.1 Migration algorithm (8.0):](#541-migration-algorithm-80) + - [5.4.2 Minimizing data loss with unsupported upgrade configurations (8.0)](#542-minimizing-data-loss-with-unsupported-upgrade-configurations-80) + - [5.5 Tag objects as “invalid” if their transformation fails](#55-tag-objects-as-invalid-if-their-transformation-fails) +- [6. How we teach this](#6-how-we-teach-this) +- [7. Unresolved questions](#7-unresolved-questions) + +# 1. Summary + +Improve the Saved Object migration algorithm to ensure a smooth Kibana upgrade +procedure. + +# 2. Motivation + +Kibana version upgrades should have a minimal operational impact. To achieve +this, users should be able to rely on: + +1. A predictable downtime window. +2. A small downtime window. + 1. (future) provide a small downtime window on indices with 10k or even + a 100k documents. +3. The ability to discover and remedy potential failures before initiating the + downtime window. +4. Quick roll-back in case of failure. +5. Detailed documentation about the impact of downtime on the features they + are using (e.g. actions, task manager, fleet, reporting). +6. Mixed Kibana versions shouldn’t cause data loss. +7. (stretch goal) Maintain read-only functionality during the downtime window. + +The biggest hurdle to achieving the above is Kibana’s Saved Object migrations. +Migrations aren’t resilient and require manual intervention anytime an error +occurs (see [3. Saved Object Migration +Errors](#3-saved-object-migration-errors)). + +It is impossible to discover these failures before initiating downtime. Errors +often force users to roll-back to a previous version of Kibana or cause hours +of downtime. To retry the migration, users are asked to manually delete a +`.kibana_x` index. If done incorrectly this can lead to data loss, making it a +terrifying experience (restoring from a pre-upgrade snapshot is a safer +alternative but not mentioned in the docs or logs). + +Cloud users don’t have access to Kibana logs to be able to identify and remedy +the cause of the migration failure. Apart from blindly retrying migrations by +restoring a previous snapshot, cloud users are unable to remedy a failed +migration and have to escalate to support which can further delay resolution. + +Taken together, version upgrades are a major operational risk and discourage +users from adopting the latest features. + +# 3. Saved Object Migration Errors + +Any of the following classes of errors could result in a Saved Object +migration failure which requires manual intervention to resolve: + +1. A bug in a plugin’s registered document transformation function causes it + to throw an exception on _valid_ data. +2. _Invalid_ data stored in Elasticsearch causes a plugin’s registered + document transformation function to throw an exception . +3. Failures resulting from an unhealthy Elasticsearch cluster: + 1. Maximum shards open + 2. Too many scroll contexts + 3. `circuit_breaking_exception` (insufficient heap memory) + 4. `process_cluster_event_timeout_exception` for index-aliases, create-index, put-mappings + 5. Read-only indices due to low disk space (hitting the flood_stage watermark) + 6. Re-index failed: search rejected due to missing shards + 7. `TooManyRequests` while doing a `count` of documents requiring a migration + 8. Bulk write failed: primary shard is not active +4. The Kibana process is killed while migrations are in progress. + +# 4. Design +## 4.0 Assumptions and tradeoffs +The proposed design makes several important assumptions and tradeoffs. + +**Background:** + +The 7.x upgrade documentation lists taking an Elasticsearch snapshot as a +required step, but we instruct users to retry migrations and perform rollbacks +by deleting the failed `.kibana_n` index and pointing the `.kibana` alias to +`.kibana_n-1`: + - [Handling errors during saved object +migrations.](https://github.com/elastic/kibana/blob/75444a9f1879c5702f9f2b8ad4a70a3a0e75871d/docs/setup/upgrade/upgrade-migrations.asciidoc#handling-errors-during-saved-object-migrations) + - [Rolling back to a previous version of Kibana.](https://github.com/elastic/kibana/blob/75444a9f1879c5702f9f2b8ad4a70a3a0e75871d/docs/setup/upgrade/upgrade-migrations.asciidoc#rolling-back-to-a-previous-version-of-kib) + - Server logs from failed migrations. + +**Assumptions and tradeoffs:** +1. It is critical to maintain a backup index during 7.x to ensure that anyone + following the existing upgrade / rollback procedures don't end up in a + position where they no longer can recover their data. + 1. This excludes us from introducing in-place migrations to support huge + indices during 7.x. +2. The simplicity of idempotent, coordination-free migrations outweighs the + restrictions this will impose on the kinds of migrations we're able to + support in the future. See (4.2.1) +3. A saved object type (and it's associated migrations) will only ever be + owned by one plugin. If pluginA registers saved object type `plugin_a_type` + then pluginB must never register that same type, even if pluginA is + disabled. Although we cannot enforce it on third-party plugins, breaking + this assumption may lead to data loss. + +## 4.1 Discover and remedy potential failures before any downtime + +> Achieves goals: (2.3) +> Mitigates errors: (3.1), (3.2) + +1. Introduce a CLI option to perform a dry run migration to allow + administrators to locate and fix potential migration failures without + taking their existing Kibana node(s) offline. +2. To have the highest chance of surfacing potential failures such as low disk + space, dry run migrations should not be mere simulations. A dry run should + perform a real migration in a way that doesn’t impact the existing Kibana + cluster. +3. The CLI should generate a migration report to make it easy to create a + support request from a failed migration dry run. + 1. The report would be an NDJSON export of all failed objects. + 2. If support receives such a report, we could modify all the objects to + ensure the migration would pass and send this back to the client. + 3. The client can then import the updated objects using the standard Saved + Objects NDJSON import and run another dry run to verify all problems + have been fixed. +4. Make running dry run migrations a required step in the upgrade procedure + documentation. +5. (Optional) Add dry run migrations to the standard cloud upgrade procedure? + +## 4.2 Automatically retry failed migrations until they succeed + +> Achieves goals: (2.2), (2.6) +> Mitigates errors (3.3) and (3.4) + +External conditions such as failures from an unhealthy Elasticsearch cluster +(3.3) can cause the migration to fail. The Kibana cluster should be able to +recover automatically once these external conditions are resolved. There are +two broad approaches to solving this problem based on whether or not +migrations are idempotent: + +| Idempotent migrations |Description | +| --------------------- | --------------------------------------------------------- | +| Yes | Idempotent migrations performed without coordination | +| No | Single node migrations coordinated through a lease / lock | + +Idempotent migrations don't require coordination making the algorithm +significantly less complex and will never require manual intervention to +retry. We, therefore, prefer this solution, even though it introduces +restrictions on migrations (4.2.1.1). For other alternatives that were +considered see section [(5)](#5-alternatives). + +## 4.2.1 Idempotent migrations performed without coordination + +The migration system can be said to be idempotent if the same results are +produced whether the migration was run once or multiple times. This property +should hold even if new (up to date) writes occur in between migration runs +which introduces the following restrictions: + +### 4.2.1.1 Restrictions + +1. All document transforms need to be deterministic, that is a document + transform will always return the same result for the same set of inputs. +2. It should always be possible to construct the exact set of inputs required + for (1) at any point during the migration process (before, during, after). + +Although these restrictions require significant changes, it does not prevent +known upcoming migrations such as [sharing saved-objects in multiple spaces](https://github.com/elastic/kibana/issues/27004) or [splitting a saved +object into multiple child +documents](https://github.com/elastic/kibana/issues/26602). To ensure that +these migrations are idempotent, they will have to generate new saved object +id's deterministically with e.g. UUIDv5. + + +### 4.2.1.2 Migration algorithm: Cloned index per version +Note: +- The description below assumes the migration algorithm is released in 7.10.0. + So < 7.10.0 will use `.kibana` and >= 7.10.0 will use `.kibana_current`. +- We refer to the alias and index that outdated nodes use as the source alias + and source index. +- Every version performs a migration even if mappings or documents aren't outdated. + +1. Locate the source index by fetching aliases (including `.kibana` for + versions prior to v7.10.0) + + ``` + GET '/_alias/.kibana_current,.kibana_7.10.0,.kibana' + ``` + + The source index is: + 1. the index the `.kibana_current` alias points to, or if it doesn’t exist, + 2. the index the `.kibana` alias points to, or if it doesn't exist, + 3. the v6.x `.kibana` index + + If none of the aliases exists, this is a new Elasticsearch cluster and no + migrations are necessary. Create the `.kibana_7.10.0_001` index with the + following aliases: `.kibana_current` and `.kibana_7.10.0`. +2. If `.kibana_current` and `.kibana_7.10.0` both exists and are pointing to the same index this version's migration has already been completed. + 1. Because the same version can have plugins enabled at any point in time, + perform the mappings update in step (6) and migrate outdated documents + with step (7). + 2. Skip to step (9) to start serving traffic. +3. Fail the migration if: + 1. `.kibana_current` is pointing to an index that belongs to a later version of Kibana .e.g. `.kibana_7.12.0_001` + 2. (Only in 8.x) The source index contains documents that belong to an unknown Saved Object type (from a disabled plugin). Log an error explaining that the plugin that created these documents needs to be enabled again or that these objects should be deleted. See section (4.2.1.4). +4. Mark the source index as read-only and wait for all in-flight operations to drain (requires https://github.com/elastic/elasticsearch/pull/58094). This prevents any further writes from outdated nodes. Assuming this API is similar to the existing `//_close` API, we expect to receive `"acknowledged" : true` and `"shards_acknowledged" : true`. If all shards don’t acknowledge within the timeout, retry the operation until it succeeds. +5. Clone the source index into a new target index which has writes enabled. All nodes on the same version will use the same fixed index name e.g. `.kibana_7.10.0_001`. The `001` postfix isn't used by Kibana, but allows for re-indexing an index should this be required by an Elasticsearch upgrade. E.g. re-index `.kibana_7.10.0_001` into `.kibana_7.10.0_002` and point the `.kibana_7.10.0` alias to `.kibana_7.10.0_002`. + 1. `POST /.kibana_n/_clone/.kibana_7.10.0_001?wait_for_active_shards=all {"settings": {"index.blocks.write": false}}`. Ignore errors if the clone already exists. + 2. Wait for the cloning to complete `GET /_cluster/health/.kibana_7.10.0_001?wait_for_status=green&timeout=60s` If cloning doesn’t complete within the 60s timeout, log a warning for visibility and poll again. +6. Update the mappings of the target index + 1. Retrieve the existing mappings including the `migrationMappingPropertyHashes` metadata. + 2. Update the mappings with `PUT /.kibana_7.10.0_001/_mapping`. The API deeply merges any updates so this won't remove the mappings of any plugins that were enabled in a previous version but are now disabled. + 3. Ensure that fields are correctly indexed using the target index's latest mappings `POST /.kibana_7.10.0_001/_update_by_query?conflicts=proceed`. In the future we could optimize this query by only targeting documents: + 1. That belong to a known saved object type. + 2. Which don't have outdated migrationVersion numbers since these will be transformed anyway. + 3. That belong to a type whose mappings were changed by comparing the `migrationMappingPropertyHashes`. (Metadata, unlike the mappings isn't commutative, so there is a small chance that the metadata hashes do not accurately reflect the latest mappings, however, this will just result in an less efficient query). +7. Transform documents by reading batches of outdated documents from the target index then transforming and updating them with optimistic concurrency control. + 1. Ignore any version conflict errors. + 2. If a document transform throws an exception, add the document to a failure list and continue trying to transform all other documents. If any failures occured, log the complete list of documents that failed to transform. Fail the migration. +8. Mark the migration as complete by doing a single atomic operation (requires https://github.com/elastic/elasticsearch/pull/58100) that: + 1. Checks that `.kibana-current` alias is still pointing to the source index + 2. Points the `.kibana-7.10.0` and `.kibana_current` aliases to the target index. + 3. If this fails with a "required alias [.kibana_current] does not exist" error fetch `.kibana_current` again: + 1. If `.kibana_current` is _not_ pointing to our target index fail the migration. + 2. If `.kibana_current` is pointing to our target index the migration has succeeded and we can proceed to step (9). +9. Start serving traffic. + +Together with the limitations, this algorithm ensures that migrations are +idempotent. If two nodes are started simultaneously, both of them will start +transforming documents in that version's target index, but because migrations are idempotent, it doesn’t matter which node’s writes win. + +
+ In the future, this algorithm could enable (2.6) "read-only functionality during the downtime window" but this is outside of the scope of this RFC. + + Although the migration algorithm guarantees there's no data loss while providing read-only access to outdated nodes, this could cause plugins to behave in unexpected ways. If we wish to persue it in the future, enabling read-only functionality during the downtime window will be it's own project and must include an audit of all plugins' behaviours. +
+ +### 4.2.1.3 Upgrade and rollback procedure +When a newer Kibana starts an upgrade, it blocks all writes to the outdated index to prevent data loss. Since Kibana is not designed to gracefully handle a read-only index this could have unintended consequences such as a task executing multiple times but never being able to write that the task was completed successfully. To prevent unintended consequences, the following procedure should be followed when upgrading Kibana: + +1. Gracefully shutdown outdated nodes by sending a `SIGTERM` signal + 1. Node starts returning `503` from it's healthcheck endpoint to signal to + the load balancer that it's no longer accepting new traffic (requires https://github.com/elastic/kibana/issues/46984). + 2. Allows ungoing HTTP requests to complete with a configurable timeout + before forcefully terminating any open connections. + 3. Closes any keep-alive sockets by sending a `connection: close` header. + 4. Shutdown all plugins and Core services. +2. (recommended) Take a snapshot of all Kibana's Saved Objects indices. This simplifies doing a rollback to a simple snapshot restore, but is not required in order to do a rollback if a migration fails. +3. Start the upgraded Kibana nodes. All running Kibana nodes should be on the same version, have the same plugins enabled and use the same configuration. + +To rollback to a previous version of Kibana with a snapshot +1. Shutdown all Kibana nodes. +2. Restore the Saved Object indices and aliases from the snapshot +3. Start the rollback Kibana nodes. All running Kibana nodes should be on the same rollback version, have the same plugins enabled and use the same configuration. + +To rollback to a previous version of Kibana without a snapshot: +(Assumes the migration to 7.11.0 failed) +1. Shutdown all Kibana nodes. +2. Remove the index created by the failed Kibana migration by using the version-specific alias e.g. `DELETE /.kibana_7.11.0` +3. Identify the rollback index: + 1. If rolling back to a Kibana version < 7.10.0 use `.kibana` + 2. If rolling back to a Kibana version >= 7.10.0 use the version alias of the Kibana version you wish to rollback to e.g. `.kibana_7.10.0` +4. Point the `.kibana_current` alias to the rollback index. +5. Remove the write block from the rollback index. +6. Start the rollback Kibana nodes. All running Kibana nodes should be on the same rollback version, have the same plugins enabled and use the same configuration. + +### 4.2.1.4 Handling documents that belong to a disabled plugin +It is possible for a plugin to create documents in one version of Kibana, but then when upgrading Kibana to a newer version, that plugin is disabled. Because the plugin is disabled it cannot register it's Saved Objects type including the mappings or any migration transformation functions. These "orphan" documents could cause future problems: + - A major version introduces breaking mapping changes that cannot be applied to the data in these documents. + - Two majors later migrations will no longer be able to migrate this old schema and could fail unexpectadly when the plugin is suddenly enabled. + +As a concrete example of the above, consider a user taking the following steps: +1. Installs Kibana 7.6.0 with spaces=enabled. The spaces plugin creates a default space saved object. +2. User upgrades to 7.10.0 but uses the OSS download which has spaces=disabled. Although the 7.10.0 spaces plugin includes a migration for space documents, the OSS release cannot migrate the documents or update it's mappings. +3. User realizes they made a mistake and use Kibana 7.10.0 with x-pack and the spaces plugin enabled. At this point we have a completed migration for 7.10.0 but there's outdated spaces documents with migrationVersion=7.6.0 instead of 7.10.0. + +There are several approaches we could take to dealing with these orphan documents: + +1. Start up but refuse to query on types with outdated documents until a user manually triggers a re-migration + + Advantages: + - The impact is limited to a single plugin + + Disadvantages: + - It might be less obvious that a plugin is in a degraded state unless you read the logs (not possible on Cloud) or view the `/status` endpoint. + - If a user doesn't care that the plugin is degraded, orphan documents are carried forward indefinitely. + - Since Kibana has started receiving traffic, users can no longer + downgrade without losing data. They have to re-migrate, but if that + fails they're stuck. + - Introduces a breaking change in the upgrade behaviour + + To perform a re-migration: + - Remove the `.kibana_7.10.0` alias + - Take a snapshot OR set the configuration option `migrations.target_index_postfix: '002'` to create a new target index `.kibana_7.10.0_002` and keep the `.kibana_7.10.0_001` index to be able to perform a rollback. + - Start up Kibana + +2. Refuse to start Kibana until the plugin is enabled or it's data deleted + + Advantages: + - Admin’s are forced to deal with the problem as soon as they disable a plugin + + Disadvantages: + - Cannot temporarily disable a plugin to aid in debugging or to reduce the load a Kibana plugin places on an ES cluster. + - Introduces a breaking change + +3. Refuse to start a migration until the plugin is enabled or it's data deleted + + Advantages: + - We force users to enable a plugin or delete the documents which prevents these documents from creating future problems like a mapping update not being compatible because there are fields which are assumed to have been migrated. + - We keep the index “clean”. + + Disadvantages: + - Since users have to take down outdated nodes before they can start the upgrade, they have to enter the downtime window before they know about this problem. This prolongs the downtime window and in many cases might cause an operations team to have to reschedule their downtime window to give them time to investigate the documents that need to be deleted. Logging an error on every startup could warn users ahead of time to mitigate this. + - We don’t expose Kibana logs on Cloud so this will have to be escalated to support and could take 48hrs to resolve (users can safely rollback, but without visibility into the logs they might not know this). Exposing Kibana logs is on the cloud team’s roadmap. + - It might not be obvious just from the saved object type, which plugin created these objects. + - Introduces a breaking change in the upgrade behaviour + +4. Use a hash of enabled plugins as part of the target index name + Using a migration target index name like + `.kibana_7.10.0_${hash(enabled_plugins)}_001` we can migrate all documents + every time a plugin is enabled / disabled. + + Advantages: + - Outdated documents belonging to disabled plugins will be upgraded as soon + as the plugin is enabled again. + + Disadvantages: + - Disabling / enabling a plugin will cause downtime (breaking change). + - When a plugin is enabled, disabled and enabled again our target index + will be an existing outdated index which needs to be deleted and + re-cloned. Without a way to check if the index is outdated, we cannot + deterministically perform the delete and re-clone operation without + coordination. + +5. Transform outdated documents (step 7) on every startup + Advantages: + - Outdated documents belonging to disabled plugins will be upgraded as soon + as the plugin is enabled again. + + Disadvantages: + - Orphan documents are retained indefinitely so there's still a potential + for future problems. + - Slightly slower startup time since we have to query for outdated + documents every time. + +We prefer option (3) since it provides flexibility for disabling plugins in +the same version while also protecting users' data in all cases during an +upgrade migration. However, because this is a breaking change we will +implement (5) during 7.x and only implement (3) during 8.x. + +# 5. Alternatives +## 5.1 Rolling upgrades +We considered implementing rolling upgrades to provide zero downtime +migrations. However, this would introduce significant complexity for plugins: +they will need to maintain up and down migration transformations and ensure +that queries match both current and outdated documents across all +versions. Although we can afford the once-off complexity of implementing +rolling upgrades, the complexity burden of maintaining plugins that support +rolling-upgrades will slow down all development in Kibana. Since a predictable +downtime window is sufficient for our users, we decided against trying to +achieve zero downtime with rolling upgrades. See "Rolling upgrades" in +https://github.com/elastic/kibana/issues/52202 for more information. + +## 5.2 Single node migrations coordinated through a lease/lock +This alternative is a proposed algorithm for coordinating migrations so that +these only happen on a single node and therefore don't have the restrictions +found in [(4.2.1.1)](#4311-restrictions). We decided against this algorithm +primarily because it is a lot more complex, but also because it could still +require manual intervention to retry from certain unlikely edge cases. + +
+ It's impossible to guarantee that a single node performs the + migration and automatically retry failed migrations. + +Coordination should ensure that only one Kibana node performs the migration at +a given time which can be achived with a distributed lock built on top of +Elasticsearch. For the Kibana cluster to be able to retry a failed migration, +requires a specialized lock which expires after a given amount of inactivity. +We will refer to such expiring locks as a "lease". + +If a Kibana process stalls, it is possible that the process' lease has expired +but the process doesn't yet recognize this and continues the migration. To +prevent this from causing data loss each lease should be accompanied by a +"guard" that prevents all writes after the lease has expired. See +[how to do distributed +locking](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) +for an in-depth discussion. + +Elasticsearch doesn't provide any building blocks for constructing such a guard. +
+ +However, we can implement a lock (that never expires) with strong +data-consistency guarantees. Because there’s no expiration, a failure between +obtaining the lock and releasing it will require manual intervention. Instead +of trying to accomplish the entire migration after obtaining a lock, we can +only perform the last step of the migration process, moving the aliases, with +a lock. A permanent failure in only this last step is not impossible, but very +unlikely. + +### 5.2.1 Migration algorithm +1. Obtain a document lock (see [5.2.2 Document lock + algorithm](#522-document-lock-algorithm)). Convert the lock into a "weak + lease" by expiring locks for nodes which aren't active (see [4.2.2.4 + Checking for lease expiry](#4324-checking-for-lease-expiry)). This "weak + lease" doesn't require strict guarantees since it's only used to prevent + multiple Kibana nodes from performing a migration in parallel to reduce the + load on Elasticsearch. +2. Migrate data into a new process specific index (we could use the process + UUID that’s used in the lease document like + `.kibana_3ef25ff1-090a-4335-83a0-307a47712b4e`). +3. Obtain a document lock (see [5.2.2 Document lock + algorithm](#522-document-lock-algorithm)). +4. Finish the migration by pointing `.kibana` → + `.kibana_3ef25ff1-090a-4335-83a0-307a47712b4e`. This automatically releases + the document lock (and any leases) because the new index will contain an + empty `kibana_cluster_state`. + +If a process crashes or is stopped after (3) but before (4) the lock will have +to be manually removed by deleting the `kibana_cluster_state` document from +`.kibana` or restoring from a snapshot. + +### 5.2.2 Document lock algorithm +To improve on the existing Saved Objects migrations lock, a locking algorithm +needs to satisfy the following requirements: +- Must guarantee that only a single node can obtain the lock. Since we can + only provide strong data-consistency guarantees on the document level in + Elasticsearch our locking mechanism needs to be based on a document. +- Manually removing the lock + - shouldn't have any risk of accidentally causing data loss. + - can be done with a single command that's always the same (shouldn’t + require trying to find `n` for removing the correct `.kibana_n` index). +- Must be easy to retrieve the lock/cluster state to aid in debugging or to + provide visibility. + +Algorithm: +1. Node reads `kibana_cluster_state` lease document from `.kibana` +2. It sends a heartbeat every `heartbeat_interval` seconds by sending an + update operation that adds it’s UUID to the `nodes` array and sets the + `lastSeen` value to the current local node time. If the update fails due to + a version conflict the update operation is retried after a random delay by + fetching the document again and attempting the update operation once more. +3. To obtain a lease, a node: + 1. Fetches the `kibana_cluster_state` document + 2. If all the nodes’ `hasLock === false` it sets it’s own `hasLock` to + true and attempts to write the document. If the update fails + (presumably because of another node’s heartbeat update) it restarts the + process to obtain a lease from step (3). + 3. If another nodes’ `hasLock === true` the node failed to acquire a + lock and waits until the active lock has expired before attempting to + obtain a lock again. +4. Once a node is done with its lock, it releases it by fetching and then + updating `hasLock = false`. The fetch + update operations are retried until + this node’s `hasLock === false`. + +Each machine writes a `UUID` to a file, so a single machine may have multiple +processes with the same Kibana `UUID`, so we should rather generate a new UUID +just for the lifetime of this process. + +`KibanaClusterState` document format: +```js + nodes: { + "852bd94e-5121-47f3-a321-e09d9db8d16e": { + version: "7.6.0", + lastSeen: [ 1114793, 555149266 ], // hrtime() big int timestamp + hasLease: true, + hasLock: false, + }, + "8d975c5b-cbf6-4418-9afb-7aa3ea34ac90": { + version: "7.6.0", + lastSeen: [ 1114862, 841295591 ], + hasLease: false, + hasLock: false, + }, + "3ef25ff1-090a-4335-83a0-307a47712b4e": { + version: "7.6.0", + lastSeen: [ 1114877, 611368546 ], + hasLease: false, + hasLock: false, + }, + }, + oplog: [ + {op: 'ACQUIRE_LOCK', node: '852bd94e...', timestamp: '2020-04-20T11:58:56.176Z'} + ] +} +``` + +### 5.2.3 Checking for "weak lease" expiry +The simplest way to check for lease expiry is to inspect the `lastSeen` value. +If `lastSeen + expiry_timeout > now` the lock is considered expired. If there +are clock drift or daylight savings time adjustments, there’s a risk that a +node loses it’s lease before `expiry_timeout` has occurred. Since losing a +lock prematurely will not lead to data loss it’s not critical that the +expiry time is observed under all conditions. + +A slightly safer approach is to use a monotonically increasing clock +(`process.hrtime()`) and relative time to determine expiry. Using a +monotonically increasing clock guarantees that the clock will always increase +even if the system time changes due to daylight savings time, NTP clock syncs, +or manually setting the time. To check for expiry, other nodes poll the +cluster state document. Once they see that the `lastSeen` value has increased, +they capture the current hr time `current_hr_time` and starts waiting until +`process.hrtime() - current_hr_time > expiry_timeout` if at that point +`lastSeen` hasn’t been updated the lease is considered to have expired. This +means other nodes can take up to `2*expiry_timeout` to recognize an expired +lease, but a lease will never expire prematurely. + +Any node that detects an expired lease can release that lease by setting the +expired node’s `hasLease = false`. It can then attempt to acquire its lease. + +## 5.3 Minimize data loss with mixed Kibana versions during 7.x +When multiple versions of Kibana are running at the same time, writes from the +outdated node can end up either in the outdated Kibana index, the newly +migrated index, or both. New documents added (and some updates) into the old +index while a migration is in-progress will be lost. Writes that end up in the +new index will be in an outdated format. This could cause queries on the data +to only return a subset of the results which leads to incorrect results or +silent data loss. + +Minimizing data loss from mixed 7.x versions, introduces two additional steps +to rollback to a previous version without a snapshot: +1. (existing) Point the `.kibana` alias to the previous Kibana index `.kibana_n-1` +2. (existing) Delete `.kibana_n` +3. (new) Enable writes on `.kibana_n-1` +4. (new) Delete the dummy "version lock" document from `.kibana_n-1` + +Since our documentation and server logs have implicitly encouraged users to +rollback without using snapshots, many users might have to rely on these +additional migration steps to perform a rollback. Since even the existing +steps are error prone, introducing more steps will likely introduce more +problems than what it solves. + +1. All future versions of Kibana 7.x will use the `.kibana_saved_objects` + alias to locate the current index. If `.kibana_saved_objects` doesn't + exist, newer versions will fallback to reading `.kibana`. +2. All future versions of Kibana will locate the index that + `.kibana_saved_objects` points to and then read and write directly from + the _index_ instead of the alias. +3. Before starting a migration: + 1. Write a new dummy "version lock" document to the `.kibana` index with a + `migrationVersion` set to the current version of Kibana. If an outdated + node is started up after a migration was started it will detect that + newer documents are present in the index and refuse to start up. + 2. Set the outdated index to read-only. Since `.kibana` is never advanced, + it will be pointing to a read-only index which prevent writes from + 6.8+ releases which are already online. + +## 5.4 In-place migrations that re-use the same index (8.0) +> We considered an algorithm that re-uses the same index for migrations and an approach to minimize data-loss if our upgrade procedures aren't followed. This is no longer our preferred approach because of several downsides: +> - It requires taking snapshots to prevent data loss so we can only release this in 8.x +> - Minimizing data loss with unsupported upgrade configurations adds significant complexity and still doesn't guarantee that data isn't lost. + +### 5.4.1 Migration algorithm (8.0): +1. Exit Kibana with a fatal error if a newer node has started a migration by + checking for: + 1. Documents with a newer `migrationVersion` numbers. +2. If the mappings are out of date, update the mappings to the combination of + the index's current mappings and the expected mappings. +3. If there are outdated documents, migrate these in batches: + 1. Read a batch of outdated documents from the index. + 2. Transform documents by applying the migration transformation functions. + 3. Update the document batch in the same index using optimistic concurrency + control. If a batch fails due to an update version mismatch continue + migrating the other batches. + 4. If a batch fails due other reasons repeat the entire migration process. +4. If any of the batches in step (3.3) failed, repeat the entire migration + process. This ensures that in-progress bulk update operations from an + outdated node won't lead to unmigrated documents still being present after + the migration. +5. Once all documents are up to date, the migration is complete and Kibana can + start serving traffic. + +Advantages: +- Not duplicating all documents into a new index will speed up migrations and + reduce the downtime window. This will be especially important for the future + requirement to support > 10k or > 100k documents. +- We can check the health of an existing index before starting the migration, + but we cannot detect what kind of failures might occur while creating a new + index. Whereas retrying migrations will eventually recover from the errors + in (3.3), re-using an index allows us to detect these problems before trying + and avoid errors like (3.3.1) altogether. +- Single index to backup instead of “index pattern” that matches any + `.kibana_n`. +- Simplifies Kibana system index Elasticsearch plugin since it needs to work + on one index per "tenant". +- By leveraging optimistic concurrency control we can further minimize data + loss for unsupported upgrade configurations in the future. + +Drawbacks: +- Cannot make breaking mapping changes (even though it was possible, we have not + introduced a breaking mapping change during 7.x). +- Rollback is only possible by restoring a snapshot which requires educating + users to ensure that they don't rely on `.kibana_n` indices as backups. + (Apart from the need to educate users, snapshot restores provide many + benefits). +- It narrows the second restriction under (4.2.1) even further: migrations + cannot rely on any state that could change as part of a migration because we + can no longer use the previous index as a snapshot of unmigrated state. +- We can’t automatically perform a rollback from a half-way done migration. +- It’s impossible to provide read-only functionality for outdated nodes which + means we can't achieve goal (2.7). + +### 5.4.2 Minimizing data loss with unsupported upgrade configurations (8.0) +> This alternative can reduce some data loss when our upgrade procedure isn't +> followed with the algorithm in (5.4.1). + +Even if (4.5.2) is the only supported upgrade procedure, we should try to +prevent data loss when these instructions aren't followed. + +To prevent data loss we need to prevent any writes from older nodes. We use +a version-specific alias for this purpose. Each time a migration is started, +all other aliases are removed. However, aliases are stored inside +Elasticsearch's ClusterState and this state could remain inconsistent between +nodes for an unbounded amount of time. In addition, bulk operations that were +accepted before the alias was removed will continue to run even after removing +the alias. + +As a result, Kibana cannot guarantee that there would be no data loss but +instead, aims to minimize it as much as possible by adding the bold sections +to the migration algorithm from (5.4.1) + +1. **Disable `action.auto_create_index` for the Kibana system indices.** +2. Exit Kibana with a fatal error if a newer node has started a migration by + checking for: + 1. **Version-specific aliases on the `.kibana` index with a newer version.** + 2. Documents with newer `migrationVersion` numbers. +3. **Remove all other aliases and create a new version-specific alias for + reading and writing to the `.kibana` index .e.g `.kibana_8.0.1`. During and + after the migration, all saved object reads and writes use this alias + instead of reading or writing directly to the index. By using the atomic + `POST /_aliases` API we minimize the chance that an outdated node creating + new outdated documents can cause data loss.** +4. **Wait for the default bulk operation timeout of 30s. This ensures that any + bulk operations accepted before the removal of the alias have either + completed or returned a timeout error to it's initiator.** +5. If the mappings are out of date, update the mappings **through the alias** + to the combination of the index's current mappings and the expected + mappings. **If this operation fails due to an index missing exception (most + likely because another node removed our version-specific alias) repeat the + entire migration process.** +6. If there are outdated documents, migrate these in batches: + 1. Read a batch of outdated documents from `.kibana_n`. + 2. Transform documents by applying the migration functions. + 3. Update the document batch in the same index using optimistic concurrency + control. If a batch fails due to an update version mismatch continue + migrating the other batches. + 4. If a batch fails due other reasons repeat the entire migration process. +7. If any of the batches in step (6.3) failed, repeat the entire migration + process. This ensures that in-progress bulk update operations from an + outdated node won't lead to unmigrated documents still being present after + the migration. +8. Once all documents are up to date, the migration is complete and Kibana can + start serving traffic. + +Steps (2) and (3) from the migration algorithm in minimize the chances of the +following scenarios occuring but cannot guarantee it. It is therefore useful +to enumarate some scenarios and their worst case impact: +1. An outdated node issued a bulk create to it's version-specific alias. + Because a user doesn't wait for all traffic to drain a newer node starts + it's migration before the bulk create was complete. Since this bulk create + was accepted before the newer node deleted the previous version-specific + aliases, it is possible that the index now contains some outdated documents + that the new node is unaware of and doesn't migrate. Although these outdated + documents can lead to inconsistent query results and data loss, step (4) + ensures that an error will be returned to the node that created these + objects. +2. A 8.1.0 node and a 8.2.0 node starts migrating a 8.0.0 index in parallel. + Even though the 8.2.0 node will remove the 8.1.0 version-specific aliases, + the 8.1.0 node could have sent an bulk update operation that got accepted + before its alias was removed. When the 8.2.0 node tries to migrate these + 8.1.0 documents it gets a version conflict but cannot be sure if this was + because another node of the same version migrated this document (which can + safely be ignored) or interference from a different Kibana version. The + 8.1.0 node will hit the error in step (6.3) and restart the migration but + then ultimately fail at step (2). The 8.2.0 node will repeat the entire + migration process from step (7) thus ensuring that all documents are up to + date. +3. A race condition with another Kibana node on the same version, but with + different enabled plugins caused this node's required mappings to be + overwritten. If this causes a mapper parsing exception in step (6.3) we can + restart the migration. Because updating the mappings is additive and saved + object types are unique to a plugin, restarting the migration will allow + the node to update the mappings to be compatible with node's plugins. Both + nodes will be able to successfully complete the migration of their plugins' + registered saved object types. However, if the migration doesn't trigger a + mapper parsing exception the incompatible mappings would go undetected + which can cause future problems like write failures or inconsistent query + results. + +## 5.5 Tag objects as “invalid” if their transformation fails +> This alternative prevents a failed migration when there's a migration transform function bug or a document with invalid data. Although it seems preferable to not fail the entire migration because of a single saved object type's migration transform bug or a single invalid document this has several pitfalls: +> 1. When an object fails to migrate the data for that saved object type becomes inconsistent. This could load to a critical feature being unavailable to a user leaving them with no choice but to downgrade. +> 2. Because Kibana starts accepting traffic after encountering invalid objects a rollback will lead to data loss leaving users with no clean way to recover. +> As a result we prefer to let an upgrade fail and making it easy for users to rollback until they can resolve the root cause. + +> Achieves goals: (2.2) +> Mitigates Errors (3.1), (3.2) + +1. Tag objects as “invalid” if they cause an exception when being transformed, + but don’t fail the entire migration. +2. Log an error message informing administrators that there are invalid + objects which require inspection. For each invalid object, provide an error + stack trace to aid in debugging. +3. Administrators should be able to generate a migration report (similar to + the one dry run migrations create) which is an NDJSON export of all objects + tagged as “invalid”. + 1. Expose this as an HTTP API first + 2. (later) Notify administrators and allow them to export invalid objects + from the Kibana UI. +4. When an invalid object is read, the Saved Objects repository will throw an + invalid object exception which should include a link to the documentation + to help administrators resolve migration bugs. +5. Educate Kibana developers to no longer simply write back an unmigrated + document if an exception occurred. A migration function should either + successfully transform the object or throw. + +# 6. How we teach this +1. Update documentation and server logs to start educating users to depend on + snapshots for Kibana rollbacks. +2. Update developer documentation and educate developers with best practices + for writing migration functions. + +# 7. Unresolved questions +1. When cloning an index we can only ever add new fields to the mappings. When + a saved object type or specific field is removed, the mappings will remain + until we re-index. Is it sufficient to only re-index every major? How do we + track the field count as it grows over every upgrade? +2. More generally, how do we deal with the growing field count approaching the + default limit of 1000? diff --git a/scripts/es.js b/scripts/es.js index 93f1d69350bac..2d56496f2fdd2 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_team_assignments.js b/scripts/generate_team_assignments.js new file mode 100644 index 0000000000000..9dcb9bb90e0fd --- /dev/null +++ b/scripts/generate_team_assignments.js @@ -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. + */ + +require('../src/setup_node_env'); +require('../src/dev/code_coverage/ingest_coverage/team_assignment').generateTeamAssignments(); diff --git a/scripts/load_team_assignment.js b/scripts/load_team_assignment.js deleted file mode 100644 index b8f5edc833634..0000000000000 --- a/scripts/load_team_assignment.js +++ /dev/null @@ -1,21 +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. - */ - -require('../src/setup_node_env'); -require('../src/dev/code_coverage/ingest_coverage/team_assignment').uploadTeamAssignmentJson(); diff --git a/src/apm.js b/src/apm.js index effa6c77d7614..8a0c010d993f1 100644 --- a/src/apm.js +++ b/src/apm.js @@ -18,67 +18,11 @@ */ const { join } = require('path'); -const { readFileSync } = require('fs'); -const { execSync } = require('child_process'); -const { merge } = require('lodash'); -const { name, version, build } = require('../package.json'); +const { name, build } = require('../package.json'); +const { loadConfiguration } = require('@kbn/apm-config-loader'); const ROOT_DIR = join(__dirname, '..'); - -function gitRev() { - try { - return execSync('git rev-parse --short HEAD', { - encoding: 'utf-8', - stdio: ['ignore', 'pipe', 'ignore'], - }).trim(); - } catch (e) { - return null; - } -} - -function devConfig() { - try { - const apmDevConfigPath = join(ROOT_DIR, 'config', 'apm.dev.js'); - return require(apmDevConfigPath); // eslint-disable-line import/no-dynamic-require - } catch (e) { - return {}; - } -} - -const apmConfig = merge( - { - active: false, - serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443', - // The secretToken below is intended to be hardcoded in this file even though - // it makes it public. This is not a security/privacy issue. Normally we'd - // instead disable the need for a secretToken in the APM Server config where - // the data is transmitted to, but due to how it's being hosted, it's easier, - // for now, to simply leave it in. - secretToken: 'R0Gjg46pE9K9wGestd', - globalLabels: {}, - breakdownMetrics: true, - centralConfig: false, - logUncaughtExceptions: true, - }, - devConfig() -); - -try { - const filename = join(ROOT_DIR, 'data', 'uuid'); - apmConfig.globalLabels.kibana_uuid = readFileSync(filename, 'utf-8'); -} catch (e) {} // eslint-disable-line no-empty - -const rev = gitRev(); -if (rev !== null) apmConfig.globalLabels.git_rev = rev; - -function getConfig(serviceName) { - return { - ...apmConfig, - ...{ - serviceName: `${serviceName}-${version.replace(/\./g, '_')}`, - }, - }; -} +let apmConfig; /** * Flag to disable APM RUM support on all kibana builds by default @@ -86,12 +30,24 @@ function getConfig(serviceName) { const isKibanaDistributable = Boolean(build && build.distributable === true); module.exports = function (serviceName = name) { - if (process.env.kbnWorkerType === 'optmzr') return; - - const conf = getConfig(serviceName); + if (process.env.kbnWorkerType === 'optmzr') { + return; + } + apmConfig = loadConfiguration(process.argv, ROOT_DIR, isKibanaDistributable); + const conf = apmConfig.getConfig(serviceName); require('elastic-apm-node').start(conf); }; -module.exports.getConfig = getConfig; +module.exports.getConfig = (serviceName) => { + // integration test runner starts a kibana server that import the module without initializing APM. + // so we need to check initialization of the config. + // note that we can't just load the configuration during this module's import + // because jest IT are ran with `--config path-to-jest-config.js` which conflicts with the CLI's `config` arg + // causing the config loader to try to load the jest js config as yaml and throws. + if (apmConfig) { + return apmConfig.getConfig(serviceName); + } + return {}; +}; module.exports.isKibanaDistributable = isKibanaDistributable; diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index 78472bb3f517d..e1f56a2f267fa 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -227,9 +227,6 @@ export class ClusterManager { fromRoot('src/legacy/server'), fromRoot('src/legacy/ui'), fromRoot('src/legacy/utils'), - fromRoot('x-pack/legacy/common'), - fromRoot('x-pack/legacy/plugins'), - fromRoot('x-pack/legacy/server'), fromRoot('config'), ...extraPaths, ].map((path) => resolve(path)) @@ -242,7 +239,6 @@ export class ClusterManager { /\.md$/, /debug\.log$/, ...pluginInternalDirsIgnore, - fromRoot('src/legacy/server/sass/__tmp__'), fromRoot('x-pack/plugins/reporting/chromium'), fromRoot('x-pack/plugins/security_solution/cypress'), fromRoot('x-pack/plugins/apm/e2e'), @@ -253,7 +249,6 @@ export class ClusterManager { fromRoot('x-pack/plugins/lists/server/scripts'), fromRoot('x-pack/plugins/security_solution/scripts'), fromRoot('x-pack/plugins/security_solution/server/lib/detection_engine/scripts'), - 'plugins/java_languageserver', ]; this.watcher = chokidar.watch(watchPaths, { diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index d8bd39b9dcdf4..a1715cf3dba2c 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -48,11 +48,6 @@ const CAN_CLUSTER = canRequire(CLUSTER_MANAGER_PATH); const REPL_PATH = resolve(__dirname, '../repl'); const CAN_REPL = canRequire(REPL_PATH); -// xpack is installed in both dev and the distributable, it's optional if -// install is a link to the source, not an actual install -const XPACK_DIR = resolve(__dirname, '../../../x-pack'); -const XPACK_INSTALLED = canRequire(XPACK_DIR); - const pathCollector = function () { const paths = []; return function (path) { @@ -137,16 +132,7 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { if (opts.logFile) set('logging.dest', opts.logFile); set('plugins.scanDirs', _.compact([].concat(get('plugins.scanDirs'), opts.pluginDir))); - set( - 'plugins.paths', - _.compact( - [].concat( - get('plugins.paths'), - opts.pluginPath, - XPACK_INSTALLED && !opts.oss ? [XPACK_DIR] : [] - ) - ) - ); + set('plugins.paths', _.compact([].concat(get('plugins.paths'), opts.pluginPath))); merge(extraCliOptions); merge(readKeystore()); diff --git a/src/core/public/apm_system.ts b/src/core/public/apm_system.ts new file mode 100644 index 0000000000000..0a18f02c97293 --- /dev/null +++ b/src/core/public/apm_system.ts @@ -0,0 +1,75 @@ +/* + * 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. + */ + +/** + * This is the entry point used to boot the frontend when serving a application + * that lives in the Kibana Platform. + * + * Any changes to this file should be kept in sync with + * src/legacy/ui/ui_bundles/app_entry_template.js + */ +import type { InternalApplicationStart } from './application'; + +interface ApmConfig { + // AgentConfigOptions is not exported from @elastic/apm-rum + globalLabels?: Record; +} + +interface StartDeps { + application: InternalApplicationStart; +} + +export class ApmSystem { + private readonly enabled: boolean; + /** + * `apmConfig` would be populated with relevant APM RUM agent + * configuration if server is started with `ELASTIC_APM_ACTIVE=true` + */ + constructor(private readonly apmConfig?: ApmConfig) { + this.enabled = process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && apmConfig != null; + } + + async setup() { + if (!this.enabled) return; + const { init, apm } = await import('@elastic/apm-rum'); + const { globalLabels, ...apmConfig } = this.apmConfig!; + if (globalLabels) { + apm.addLabels(globalLabels); + } + + init(apmConfig); + } + + async start(start?: StartDeps) { + if (!this.enabled || !start) return; + /** + * Register listeners for navigation changes and capture them as + * route-change transactions after Kibana app is bootstrapped + */ + start.application.currentAppId$.subscribe((appId) => { + const apmInstance = (window as any).elasticApm; + if (appId && apmInstance && typeof apmInstance.startTransaction === 'function') { + apmInstance.startTransaction(`/app/${appId}`, 'route-change', { + managed: true, + canReuse: true, + }); + } + }); + } +} diff --git a/src/core/public/application/integration_tests/router.test.tsx b/src/core/public/application/integration_tests/router.test.tsx index e3f992990f9f9..d982136422268 100644 --- a/src/core/public/application/integration_tests/router.test.tsx +++ b/src/core/public/application/integration_tests/router.test.tsx @@ -107,7 +107,7 @@ describe('AppRouter', () => { expect(app1.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -119,7 +119,7 @@ describe('AppRouter', () => { expect(app1Unmount).toHaveBeenCalled(); expect(app2.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app2 html:
App 2
" @@ -133,7 +133,7 @@ describe('AppRouter', () => { expect(standardApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -145,7 +145,7 @@ describe('AppRouter', () => { expect(standardAppUnmount).toHaveBeenCalled(); expect(chromelessApp.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -157,7 +157,7 @@ describe('AppRouter', () => { expect(chromelessAppUnmount).toHaveBeenCalled(); expect(standardApp.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /app/app1 html: App 1
" @@ -171,7 +171,7 @@ describe('AppRouter', () => { expect(chromelessAppA.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" @@ -183,7 +183,7 @@ describe('AppRouter', () => { expect(chromelessAppAUnmount).toHaveBeenCalled(); expect(chromelessAppB.mounter.mount).toHaveBeenCalled(); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-b/path html:
Chromeless B
" @@ -195,7 +195,7 @@ describe('AppRouter', () => { expect(chromelessAppBUnmount).toHaveBeenCalled(); expect(chromelessAppA.mounter.mount).toHaveBeenCalledTimes(2); expect(dom?.html()).toMatchInlineSnapshot(` - "
+ "
basename: /chromeless-a/path html:
Chromeless A
" diff --git a/src/core/public/application/ui/app_container.tsx b/src/core/public/application/ui/app_container.tsx index 089d1cf3f3ced..9821db5ba2666 100644 --- a/src/core/public/application/ui/app_container.tsx +++ b/src/core/public/application/ui/app_container.tsx @@ -25,7 +25,7 @@ import React, { useState, MutableRefObject, } from 'react'; -import { EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingElastic } from '@elastic/eui'; import type { MountPoint } from '../../types'; import { AppLeaveHandler, AppStatus, AppUnmount, Mounter } from '../types'; @@ -120,7 +120,7 @@ export const AppContainer: FunctionComponent = ({ {appNotFound && } {showSpinner && (
- +
)}
diff --git a/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap b/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap index 3007be1e5dfe0..e6bf7e898d8c4 100644 --- a/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap +++ b/src/core/public/chrome/ui/__snapshots__/loading_indicator.test.tsx.snap @@ -1,23 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`kbnLoadingIndicator is hidden by default 1`] = ` -
-
-
+/> `; exports[`kbnLoadingIndicator is visible when loadingCount is > 0 1`] = ` -
-
-
+/> `; diff --git a/src/core/public/chrome/ui/_loading_indicator.scss b/src/core/public/chrome/ui/_loading_indicator.scss index ad934717b4b76..ccf1ecc873fc5 100644 --- a/src/core/public/chrome/ui/_loading_indicator.scss +++ b/src/core/public/chrome/ui/_loading_indicator.scss @@ -1,55 +1,4 @@ -$kbnLoadingIndicatorBackgroundSize: $euiSizeXXL * 10; -$kbnLoadingIndicatorColor1: tint($euiColorAccent, 15%); -$kbnLoadingIndicatorColor2: tint($euiColorAccent, 60%); - -/** - * 1. Position this loader on top of the content. - * 2. Make sure indicator isn't wider than the screen. - */ -.kbnLoadingIndicator { - position: fixed; // 1 - top: 0; // 1 - left: 0; // 1 - right: 0; // 1 - z-index: $euiZLevel2; // 1 - overflow: hidden; // 2 - height: $euiSizeXS / 2; - - &.hidden { - visibility: hidden; - opacity: 0; - transition-delay: 0.25s; - } -} - -.kbnLoadingIndicator__bar { - top: 0; - left: 0; - right: 0; - bottom: 0; - position: absolute; - z-index: $euiZLevel2 + 1; - visibility: visible; - display: block; - animation: kbn-animate-loading-indicator 2s linear infinite; - background-color: $kbnLoadingIndicatorColor2; - background-image: linear-gradient( - to right, - $kbnLoadingIndicatorColor1 0%, - $kbnLoadingIndicatorColor1 50%, - $kbnLoadingIndicatorColor2 50%, - $kbnLoadingIndicatorColor2 100% - ); - background-repeat: repeat-x; - background-size: $kbnLoadingIndicatorBackgroundSize $kbnLoadingIndicatorBackgroundSize; - width: 200%; -} - -@keyframes kbn-animate-loading-indicator { - from { - transform: translateX(0); - } - to { - transform: translateX(-$kbnLoadingIndicatorBackgroundSize); - } +.kbnLoadingIndicator-hidden { + visibility: hidden; + animation-play-state: paused; } diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 1a4d93aee9516..8937ecb4d9b4e 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -1690,66 +1690,6 @@ exports[`Header renders 1`] = ` } } > - -
-
-
-
, + , ], }, Object { @@ -2971,6 +2963,81 @@ exports[`Header renders 1`] = `
+ +
+ + + + + +
+
; + return ; } const toggleCollapsibleNavRef = createRef(); @@ -97,7 +97,6 @@ export function Header({ return ( <> -
, + , ], borders: 'none', }, diff --git a/src/core/public/chrome/ui/header/header_breadcrumbs.tsx b/src/core/public/chrome/ui/header/header_breadcrumbs.tsx index 174c46981db53..52412f8990c7a 100644 --- a/src/core/public/chrome/ui/header/header_breadcrumbs.tsx +++ b/src/core/public/chrome/ui/header/header_breadcrumbs.tsx @@ -20,7 +20,7 @@ import { EuiHeaderBreadcrumbs } from '@elastic/eui'; import classNames from 'classnames'; import React from 'react'; -import { useObservable } from 'react-use'; +import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; import { ChromeBreadcrumb } from '../../chrome_service'; diff --git a/src/core/public/chrome/ui/header/header_logo.tsx b/src/core/public/chrome/ui/header/header_logo.tsx index dee93ecb1a804..83e0c52ab3f3a 100644 --- a/src/core/public/chrome/ui/header/header_logo.tsx +++ b/src/core/public/chrome/ui/header/header_logo.tsx @@ -20,7 +20,7 @@ import { EuiHeaderLogo } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { useObservable } from 'react-use'; +import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; import Url from 'url'; import { ChromeNavLink } from '../..'; diff --git a/src/core/public/chrome/ui/header/header_nav_controls.tsx b/src/core/public/chrome/ui/header/header_nav_controls.tsx index 8d9d8097fd8e3..69b0e3bd8afe3 100644 --- a/src/core/public/chrome/ui/header/header_nav_controls.tsx +++ b/src/core/public/chrome/ui/header/header_nav_controls.tsx @@ -19,7 +19,7 @@ import { EuiHeaderSectionItem } from '@elastic/eui'; import React from 'react'; -import { useObservable } from 'react-use'; +import useObservable from 'react-use/lib/useObservable'; import { Observable } from 'rxjs'; import { ChromeNavControl } from '../../nav_controls'; import { HeaderExtension } from './header_extension'; diff --git a/src/core/public/chrome/ui/loading_indicator.tsx b/src/core/public/chrome/ui/loading_indicator.tsx index 0209612eae08c..ca3e95f722ec5 100644 --- a/src/core/public/chrome/ui/loading_indicator.tsx +++ b/src/core/public/chrome/ui/loading_indicator.tsx @@ -17,6 +17,8 @@ * under the License. */ +import { EuiLoadingSpinner, EuiProgress } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import React from 'react'; import classNames from 'classnames'; import { Subscription } from 'rxjs'; @@ -25,9 +27,12 @@ import { HttpStart } from '../../http'; export interface LoadingIndicatorProps { loadingCount$: ReturnType; + showAsBar?: boolean; } export class LoadingIndicator extends React.Component { + public static defaultProps = { showAsBar: false }; + private loadingCountSubscription?: Subscription; state = { @@ -50,16 +55,35 @@ export class LoadingIndicator extends React.Component -
-
+ const ariaHidden = this.state.visible ? false : true; + + const ariaLabel = i18n.translate('core.ui.loadingIndicatorAriaLabel', { + defaultMessage: 'Loading content', + }); + + return !this.props.showAsBar ? ( + + ) : ( + ); } } diff --git a/src/core/public/injected_metadata/injected_metadata_service.ts b/src/core/public/injected_metadata/injected_metadata_service.ts index 5b51bc823d166..bd8c9e91f15a2 100644 --- a/src/core/public/injected_metadata/injected_metadata_service.ts +++ b/src/core/public/injected_metadata/injected_metadata_service.ts @@ -58,19 +58,6 @@ export interface InjectedMetadataParams { uiPlugins: InjectedPluginMetadata[]; anonymousStatusPage: boolean; legacyMetadata: { - app: { - id: string; - title: string; - }; - bundleId: string; - version: string; - branch: string; - buildNum: number; - buildSha: string; - basePath: string; - serverName: string; - devMode: boolean; - category?: AppCategory; uiSettings: { defaults: Record; user?: Record; @@ -167,18 +154,6 @@ export interface InjectedMetadataSetup { getPlugins: () => InjectedPluginMetadata[]; getAnonymousStatusPage: () => boolean; getLegacyMetadata: () => { - app: { - id: string; - title: string; - }; - bundleId: string; - version: string; - branch: string; - buildNum: number; - buildSha: string; - basePath: string; - serverName: string; - devMode: boolean; uiSettings: { defaults: Record; user?: Record | undefined; diff --git a/src/core/public/kbn_bootstrap.test.mocks.ts b/src/core/public/kbn_bootstrap.test.mocks.ts new file mode 100644 index 0000000000000..30f292280a135 --- /dev/null +++ b/src/core/public/kbn_bootstrap.test.mocks.ts @@ -0,0 +1,50 @@ +/* + * 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 { applicationServiceMock } from './application/application_service.mock'; +import { fatalErrorsServiceMock } from './fatal_errors/fatal_errors_service.mock'; +export const fatalErrorMock = fatalErrorsServiceMock.createSetupContract(); +export const coreSystemMock = { + setup: jest.fn().mockResolvedValue({ + fatalErrors: fatalErrorMock, + }), + start: jest.fn().mockResolvedValue({ + application: applicationServiceMock.createInternalStartContract(), + }), +}; +jest.doMock('./core_system', () => ({ + CoreSystem: jest.fn().mockImplementation(() => coreSystemMock), +})); + +export const apmSystem = { + setup: jest.fn().mockResolvedValue(undefined), + start: jest.fn().mockResolvedValue(undefined), +}; +export const ApmSystemConstructor = jest.fn().mockImplementation(() => apmSystem); +jest.doMock('./apm_system', () => ({ + ApmSystem: ApmSystemConstructor, +})); + +export const i18nLoad = jest.fn().mockResolvedValue(undefined); +jest.doMock('@kbn/i18n', () => ({ + i18n: { + ...jest.requireActual('@kbn/i18n').i18n, + load: i18nLoad, + }, +})); diff --git a/src/core/public/kbn_bootstrap.test.ts b/src/core/public/kbn_bootstrap.test.ts new file mode 100644 index 0000000000000..9cd16dd6ab9df --- /dev/null +++ b/src/core/public/kbn_bootstrap.test.ts @@ -0,0 +1,54 @@ +/* + * 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 { apmSystem, fatalErrorMock, i18nLoad } from './kbn_bootstrap.test.mocks'; +import { __kbnBootstrap__ } from './'; + +describe('kbn_bootstrap', () => { + beforeAll(() => { + const metadata = { + i18n: { translationsUrl: 'http://localhost' }, + vars: { apmConfig: null }, + }; + // eslint-disable-next-line no-unsanitized/property + document.body.innerHTML = ` +`; + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('does not report a fatal error if apm load fails', async () => { + apmSystem.setup.mockRejectedValueOnce(new Error('reason')); + const consoleSpy = jest.spyOn(console, 'warn').mockImplementationOnce(() => undefined); + + await __kbnBootstrap__(); + + expect(fatalErrorMock.add).toHaveBeenCalledTimes(0); + expect(consoleSpy).toHaveBeenCalledTimes(1); + }); + + it('reports a fatal error if i18n load fails', async () => { + i18nLoad.mockRejectedValueOnce(new Error('reason')); + + await __kbnBootstrap__(); + + expect(fatalErrorMock.add).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/core/public/kbn_bootstrap.ts b/src/core/public/kbn_bootstrap.ts index a108b5aaa47ec..a083196004cf4 100644 --- a/src/core/public/kbn_bootstrap.ts +++ b/src/core/public/kbn_bootstrap.ts @@ -17,70 +17,38 @@ * under the License. */ -/** - * This is the entry point used to boot the frontend when serving a application - * that lives in the Kibana Platform. - * - * Any changes to this file should be kept in sync with - * src/legacy/ui/ui_bundles/app_entry_template.js - */ - import { i18n } from '@kbn/i18n'; import { CoreSystem } from './core_system'; +import { ApmSystem } from './apm_system'; /** @internal */ -export function __kbnBootstrap__() { +export async function __kbnBootstrap__() { const injectedMetadata = JSON.parse( document.querySelector('kbn-injected-metadata')!.getAttribute('data')! ); - /** - * `apmConfig` would be populated with relavant APM RUM agent - * configuration if server is started with `ELASTIC_APM_ACTIVE=true` - */ - const apmConfig = injectedMetadata.vars.apmConfig; - const APM_ENABLED = process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && apmConfig != null; - - if (APM_ENABLED) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { init, apm } = require('@elastic/apm-rum'); - if (apmConfig.globalLabels) { - apm.addLabels(apmConfig.globalLabels); - } - init(apmConfig); + let i18nError: Error | undefined; + const apmSystem = new ApmSystem(injectedMetadata.vars.apmConfig); + + await Promise.all([ + // eslint-disable-next-line no-console + apmSystem.setup().catch(console.warn), + i18n.load(injectedMetadata.i18n.translationsUrl).catch((error) => { + i18nError = error; + }), + ]); + + const coreSystem = new CoreSystem({ + injectedMetadata, + rootDomElement: document.body, + browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, + }); + + const setup = await coreSystem.setup(); + if (i18nError && setup) { + setup.fatalErrors.add(i18nError); } - i18n - .load(injectedMetadata.i18n.translationsUrl) - .catch((e) => e) - .then(async (i18nError) => { - const coreSystem = new CoreSystem({ - injectedMetadata, - rootDomElement: document.body, - browserSupportsCsp: !(window as any).__kbnCspNotEnforced__, - }); - - const setup = await coreSystem.setup(); - if (i18nError && setup) { - setup.fatalErrors.add(i18nError); - } - - const start = await coreSystem.start(); - - if (APM_ENABLED && start) { - /** - * Register listeners for navigation changes and capture them as - * route-change transactions after Kibana app is bootstrapped - */ - start.application.currentAppId$.subscribe((appId) => { - const apmInstance = (window as any).elasticApm; - if (appId && apmInstance && typeof apmInstance.startTransaction === 'function') { - apmInstance.startTransaction(`/app/${appId}`, 'route-change', { - managed: true, - canReuse: true, - }); - } - }); - } - }); + const start = await coreSystem.start(); + await apmSystem.start(start); } diff --git a/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap b/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap index 2b3bdb65708a3..0ed32a1dbccb2 100644 --- a/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap +++ b/src/core/public/notifications/toasts/__snapshots__/global_toast_list.test.tsx.snap @@ -2,6 +2,7 @@ exports[`renders matching snapshot 1`] = ` { public render() { return ( this.props.dismissToast(id)} diff --git a/src/core/public/overlays/banners/user_banner_service.tsx b/src/core/public/overlays/banners/user_banner_service.tsx index 643d95a1e3bb4..2b93c3e4b6c21 100644 --- a/src/core/public/overlays/banners/user_banner_service.tsx +++ b/src/core/public/overlays/banners/user_banner_service.tsx @@ -19,12 +19,11 @@ import React, { Fragment } from 'react'; import ReactDOM from 'react-dom'; -import ReactMarkdown from 'react-markdown'; import { filter } from 'rxjs/operators'; import { Subscription } from 'rxjs'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiCallOut, EuiButton } from '@elastic/eui'; +import { EuiCallOut, EuiButton, EuiLoadingSpinner } from '@elastic/eui'; import { I18nStart } from '../../i18n'; import { IUiSettingsClient } from '../../ui_settings'; @@ -36,6 +35,8 @@ interface StartDeps { uiSettings: IUiSettingsClient; } +const ReactMarkdownLazy = React.lazy(() => import('react-markdown')); + /** * Sets up the custom banner that can be specified in advanced settings. * @internal @@ -75,7 +76,15 @@ export class UserBannerService { } iconType="help" > - {content.trim()} + + +
+ } + > + + banners.remove(id!)}> ; // @public (undocumented) export interface App { @@ -1079,6 +1079,7 @@ export interface SavedObjectsFindOptions { sortOrder?: string; // (undocumented) type: string | string[]; + typeToNamespacesMap?: Map; } // @public diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 5a8949ca2f55f..6a10eb44d9ca4 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -34,7 +34,7 @@ import { HttpFetchOptions, HttpSetup } from '../http'; type SavedObjectsFindOptions = Omit< SavedObjectFindOptionsServer, - 'namespace' | 'sortOrder' | 'rootSearchFields' + 'sortOrder' | 'rootSearchFields' | 'typeToNamespacesMap' >; type PromiseType> = T extends Promise ? U : never; diff --git a/src/core/server/capabilities/capabilities_service.mock.ts b/src/core/server/capabilities/capabilities_service.mock.ts index 7d134f9592dc7..72824693705d9 100644 --- a/src/core/server/capabilities/capabilities_service.mock.ts +++ b/src/core/server/capabilities/capabilities_service.mock.ts @@ -18,6 +18,7 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; import { CapabilitiesService, CapabilitiesSetup, CapabilitiesStart } from './capabilities_service'; +import { Capabilities } from './types'; const createSetupContractMock = () => { const setupContract: jest.Mocked = { @@ -34,6 +35,14 @@ const createStartContractMock = () => { return setupContract; }; +const createCapabilitiesMock = (): Capabilities => { + return { + navLinks: {}, + management: {}, + catalogue: {}, + }; +}; + type CapabilitiesServiceContract = PublicMethodsOf; const createMock = () => { const mocked: jest.Mocked = { @@ -47,4 +56,5 @@ export const capabilitiesServiceMock = { create: createMock, createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, + createCapabilities: createCapabilitiesMock, }; diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 2701edcf44e03..6c85cfbed8e82 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -136,6 +136,8 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unu unusedFromRoot('optimize.workers'), unusedFromRoot('optimize.profile'), unusedFromRoot('optimize.validateSyntaxOfNodeModules'), + unusedFromRoot('elasticsearch.preserveHost'), + unusedFromRoot('elasticsearch.startupTimeout'), rename('cpu.cgroup.path.override', 'ops.cGroupOverrides.cpuPath'), rename('cpuacct.cgroup.path.override', 'ops.cGroupOverrides.cpuAcctPath'), configPathDeprecation, diff --git a/src/core/server/elasticsearch/client/mocks.ts b/src/core/server/elasticsearch/client/mocks.ts index 6fb3dc090bfb4..fb2826c787718 100644 --- a/src/core/server/elasticsearch/client/mocks.ts +++ b/src/core/server/elasticsearch/client/mocks.ts @@ -31,6 +31,7 @@ const createInternalClientMock = (): DeeplyMockedKeys => { '_events', '_eventsCount', '_maxListeners', + 'constructor', 'name', 'serializer', 'connectionPool', @@ -38,35 +39,57 @@ const createInternalClientMock = (): DeeplyMockedKeys => { 'helpers', ]; + const getAllPropertyDescriptors = (obj: Record) => { + const descriptors = Object.entries(Object.getOwnPropertyDescriptors(obj)); + let prototype = Object.getPrototypeOf(obj); + while (prototype != null && prototype !== Object.prototype) { + descriptors.push(...Object.entries(Object.getOwnPropertyDescriptors(prototype))); + prototype = Object.getPrototypeOf(prototype); + } + return descriptors; + }; + const mockify = (obj: Record, omitted: string[] = []) => { - Object.keys(obj) - .filter((key) => !omitted.includes(key)) - .forEach((key) => { - const propType = typeof obj[key]; - if (propType === 'function') { + // the @elastic/elasticsearch::Client uses prototypical inheritance + // so we have to crawl up the prototype chain and get all descriptors + // to find everything that we should be mocking + const descriptors = getAllPropertyDescriptors(obj); + descriptors + .filter(([key]) => !omitted.includes(key)) + .forEach(([key, descriptor]) => { + if (typeof descriptor.value === 'function') { obj[key] = jest.fn(() => createSuccessTransportRequestPromise({})); - } else if (propType === 'object' && obj[key] != null) { - mockify(obj[key]); + } else if (typeof obj[key] === 'object' && obj[key] != null) { + mockify(obj[key], omitted); } }); }; mockify(client, omittedProps); - // client got some read-only (getter) properties - // so we need to extend it to override the getter-only props. - const mock: any = { ...client }; + client.close = jest.fn().mockReturnValue(Promise.resolve()); + client.child = jest.fn().mockImplementation(() => createInternalClientMock()); + + const mockGetter = (obj: Record, propertyName: string) => { + Object.defineProperty(obj, propertyName, { + configurable: true, + enumerable: false, + get: () => jest.fn(), + set: undefined, + }); + }; - mock.transport = { + // `on`, `off`, and `once` are properties without a setter. + // We can't `client.on = jest.fn()` because the following error will be thrown: + // TypeError: Cannot set property on of # which has only a getter + mockGetter(client, 'on'); + mockGetter(client, 'off'); + mockGetter(client, 'once'); + client.transport = { request: jest.fn(), }; - mock.close = jest.fn().mockReturnValue(Promise.resolve()); - mock.child = jest.fn().mockImplementation(() => createInternalClientMock()); - mock.on = jest.fn(); - mock.off = jest.fn(); - mock.once = jest.fn(); - return (mock as unknown) as DeeplyMockedKeys; + return client as DeeplyMockedKeys; }; export type ElasticsearchClientMock = DeeplyMockedKeys; diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index cac8c75a04486..cfc1edfbd318e 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -45,7 +45,6 @@ export const configSchema = schema.object({ hosts: schema.oneOf([hostURISchema, schema.arrayOf(hostURISchema, { minSize: 1 })], { defaultValue: 'http://localhost:9200', }), - preserveHost: schema.boolean({ defaultValue: true }), username: schema.maybe( schema.conditional( schema.contextRef('dist'), @@ -71,7 +70,6 @@ export const configSchema = schema.object({ shardTimeout: schema.duration({ defaultValue: '30s' }), requestTimeout: schema.duration({ defaultValue: '30s' }), pingTimeout: schema.duration({ defaultValue: schema.siblingRef('requestTimeout') }), - startupTimeout: schema.duration({ defaultValue: '5s' }), logQueries: schema.boolean({ defaultValue: false }), ssl: schema.object( { diff --git a/src/core/server/index.ts b/src/core/server/index.ts index e136c699f7246..70ef93963c69f 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -339,14 +339,7 @@ export { SavedObjectsMigrationVersion, } from './types'; -export { - LegacyServiceSetupDeps, - LegacyServiceStartDeps, - LegacyServiceDiscoverPlugins, - LegacyConfig, - LegacyUiExports, - LegacyInternals, -} from './legacy'; +export { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig } from './legacy'; export { CoreStatus, diff --git a/src/core/server/legacy/config/ensure_valid_configuration.test.ts b/src/core/server/legacy/config/ensure_valid_configuration.test.ts index 702840b8a0a6a..700fe69954655 100644 --- a/src/core/server/legacy/config/ensure_valid_configuration.test.ts +++ b/src/core/server/legacy/config/ensure_valid_configuration.test.ts @@ -39,17 +39,12 @@ describe('ensureValidConfiguration', () => { configService as any, { settings: 'settings', - pluginSpecs: 'pluginSpecs', - disabledPluginSpecs: 'disabledPluginSpecs', - pluginExtendedConfig: 'pluginExtendedConfig', - uiExports: 'uiExports', + legacyConfig: 'pluginExtendedConfig', } as any ); expect(getUnusedConfigKeys).toHaveBeenCalledTimes(1); expect(getUnusedConfigKeys).toHaveBeenCalledWith({ coreHandledConfigPaths: ['core', 'elastic'], - pluginSpecs: 'pluginSpecs', - disabledPluginSpecs: 'disabledPluginSpecs', settings: 'settings', legacyConfig: 'pluginExtendedConfig', }); diff --git a/src/core/server/legacy/config/ensure_valid_configuration.ts b/src/core/server/legacy/config/ensure_valid_configuration.ts index 5cd1603ea65fb..34f98b9b3a795 100644 --- a/src/core/server/legacy/config/ensure_valid_configuration.ts +++ b/src/core/server/legacy/config/ensure_valid_configuration.ts @@ -19,19 +19,17 @@ import { getUnusedConfigKeys } from './get_unused_config_keys'; import { ConfigService } from '../../config'; -import { LegacyServiceDiscoverPlugins } from '../types'; import { CriticalError } from '../../errors'; +import { LegacyServiceSetupConfig } from '../types'; export async function ensureValidConfiguration( configService: ConfigService, - { pluginSpecs, disabledPluginSpecs, pluginExtendedConfig, settings }: LegacyServiceDiscoverPlugins + { legacyConfig, settings }: LegacyServiceSetupConfig ) { const unusedConfigKeys = await getUnusedConfigKeys({ coreHandledConfigPaths: await configService.getUsedPaths(), - pluginSpecs, - disabledPluginSpecs, settings, - legacyConfig: pluginExtendedConfig, + legacyConfig, }); if (unusedConfigKeys.length > 0) { diff --git a/src/core/server/legacy/config/get_unused_config_keys.test.ts b/src/core/server/legacy/config/get_unused_config_keys.test.ts index f8506b5744030..6ce69fca0270a 100644 --- a/src/core/server/legacy/config/get_unused_config_keys.test.ts +++ b/src/core/server/legacy/config/get_unused_config_keys.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { LegacyPluginSpec, LegacyConfig, LegacyVars } from '../types'; +import { LegacyConfig, LegacyVars } from '../types'; import { getUnusedConfigKeys } from './get_unused_config_keys'; describe('getUnusedConfigKeys', () => { @@ -35,8 +35,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: {}, legacyConfig: getConfig(), }) @@ -47,8 +45,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { presentInBoth: true, alsoInBoth: 'someValue', @@ -65,8 +61,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { presentInBoth: true, }, @@ -82,8 +76,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { presentInBoth: true, onlyInSetting: 'value', @@ -99,8 +91,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { elasticsearch: { username: 'foo', @@ -121,8 +111,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { env: 'development', }, @@ -139,8 +127,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { prop: ['a', 'b', 'c'], }, @@ -152,40 +138,10 @@ describe('getUnusedConfigKeys', () => { }); }); - it('ignores config for plugins that are disabled', async () => { - expect( - await getUnusedConfigKeys({ - coreHandledConfigPaths: [], - pluginSpecs: [], - disabledPluginSpecs: [ - ({ - id: 'foo', - getConfigPrefix: () => 'foo.bar', - } as unknown) as LegacyPluginSpec, - ], - settings: { - foo: { - bar: { - unused: true, - }, - }, - plugin: { - missingProp: false, - }, - }, - legacyConfig: getConfig({ - prop: 'a', - }), - }) - ).toEqual(['plugin.missingProp']); - }); - it('ignores properties managed by the new platform', async () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: ['core', 'foo.bar'], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { core: { prop: 'value', @@ -204,8 +160,6 @@ describe('getUnusedConfigKeys', () => { expect( await getUnusedConfigKeys({ coreHandledConfigPaths: ['core', 'array'], - pluginSpecs: [], - disabledPluginSpecs: [], settings: { core: { prop: 'value', diff --git a/src/core/server/legacy/config/get_unused_config_keys.ts b/src/core/server/legacy/config/get_unused_config_keys.ts index c15c3b270df05..5bbe169033e39 100644 --- a/src/core/server/legacy/config/get_unused_config_keys.ts +++ b/src/core/server/legacy/config/get_unused_config_keys.ts @@ -19,30 +19,20 @@ import { difference } from 'lodash'; import { getFlattenedObject } from '@kbn/std'; -import { unset } from '../../../../legacy/utils'; import { hasConfigPathIntersection } from '../../config'; -import { LegacyPluginSpec, LegacyConfig, LegacyVars } from '../types'; +import { LegacyConfig, LegacyVars } from '../types'; const getFlattenedKeys = (object: object) => Object.keys(getFlattenedObject(object)); export async function getUnusedConfigKeys({ coreHandledConfigPaths, - pluginSpecs, - disabledPluginSpecs, settings, legacyConfig, }: { coreHandledConfigPaths: string[]; - pluginSpecs: LegacyPluginSpec[]; - disabledPluginSpecs: LegacyPluginSpec[]; settings: LegacyVars; legacyConfig: LegacyConfig; }) { - // remove config values from disabled plugins - for (const spec of disabledPluginSpecs) { - unset(settings, spec.getConfigPrefix()); - } - const inputKeys = getFlattenedKeys(settings); const appliedKeys = getFlattenedKeys(legacyConfig.get()); diff --git a/src/core/server/legacy/index.ts b/src/core/server/legacy/index.ts index 6b0963e3129c6..1a0bc8955be0f 100644 --- a/src/core/server/legacy/index.ts +++ b/src/core/server/legacy/index.ts @@ -20,8 +20,6 @@ /** @internal */ export { ensureValidConfiguration } from './config'; /** @internal */ -export { LegacyInternals } from './legacy_internals'; -/** @internal */ export { LegacyService, ILegacyService } from './legacy_service'; /** @internal */ export * from './types'; diff --git a/src/core/server/legacy/legacy_internals.test.ts b/src/core/server/legacy/legacy_internals.test.ts deleted file mode 100644 index 935e36a989a0c..0000000000000 --- a/src/core/server/legacy/legacy_internals.test.ts +++ /dev/null @@ -1,211 +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 { Server } from 'hapi'; - -import { configMock } from '../config/mocks'; -import { httpServiceMock } from '../http/http_service.mock'; -import { httpServerMock } from '../http/http_server.mocks'; -import { findLegacyPluginSpecsMock } from './legacy_service.test.mocks'; -import { LegacyInternals } from './legacy_internals'; -import { ILegacyInternals, LegacyConfig, LegacyVars, LegacyUiExports } from './types'; - -function varsProvider(vars: LegacyVars, configValue?: any) { - return { - fn: jest.fn().mockReturnValue(vars), - pluginSpec: { - readConfigValue: jest.fn().mockReturnValue(configValue), - }, - }; -} - -describe('LegacyInternals', () => { - describe('getInjectedUiAppVars()', () => { - let uiExports: LegacyUiExports; - let config: LegacyConfig; - let server: Server; - let legacyInternals: ILegacyInternals; - - beforeEach(async () => { - uiExports = findLegacyPluginSpecsMock().uiExports; - config = configMock.create() as any; - server = httpServiceMock.createInternalSetupContract().server; - legacyInternals = new LegacyInternals(uiExports, config, server); - }); - - it('gets with no injectors', async () => { - await expect(legacyInternals.getInjectedUiAppVars('core')).resolves.toMatchInlineSnapshot( - `Object {}` - ); - }); - - it('gets with no matching injectors', async () => { - const injector = jest.fn().mockResolvedValue({ not: 'core' }); - legacyInternals.injectUiAppVars('not-core', injector); - - await expect(legacyInternals.getInjectedUiAppVars('core')).resolves.toMatchInlineSnapshot( - `Object {}` - ); - expect(injector).not.toHaveBeenCalled(); - }); - - it('gets with single matching injector', async () => { - const injector = jest.fn().mockResolvedValue({ is: 'core' }); - legacyInternals.injectUiAppVars('core', injector); - - await expect(legacyInternals.getInjectedUiAppVars('core')).resolves.toMatchInlineSnapshot(` - Object { - "is": "core", - } - `); - expect(injector).toHaveBeenCalled(); - }); - - it('gets with multiple matching injectors', async () => { - const injectors = [ - jest.fn().mockResolvedValue({ is: 'core' }), - jest.fn().mockReturnValue({ sync: 'injector' }), - jest.fn().mockResolvedValue({ is: 'merged-core' }), - ]; - - injectors.forEach((injector) => legacyInternals.injectUiAppVars('core', injector)); - - await expect(legacyInternals.getInjectedUiAppVars('core')).resolves.toMatchInlineSnapshot(` - Object { - "is": "merged-core", - "sync": "injector", - } - `); - expect(injectors[0]).toHaveBeenCalled(); - expect(injectors[1]).toHaveBeenCalled(); - expect(injectors[2]).toHaveBeenCalled(); - }); - }); - - describe('getVars()', () => { - let uiExports: LegacyUiExports; - let config: LegacyConfig; - let server: Server; - let legacyInternals: LegacyInternals; - - beforeEach(async () => { - uiExports = findLegacyPluginSpecsMock().uiExports; - config = configMock.create() as any; - server = httpServiceMock.createInternalSetupContract().server; - legacyInternals = new LegacyInternals(uiExports, config, server); - }); - - it('gets: no default injectors, no injected vars replacers, no ui app injectors, no inject arg', async () => { - const vars = await legacyInternals.getVars('core', httpServerMock.createRawRequest()); - - expect(vars).toMatchInlineSnapshot(`Object {}`); - }); - - it('gets: with default injectors, no injected vars replacers, no ui app injectors, no inject arg', async () => { - uiExports.defaultInjectedVarProviders = [ - varsProvider({ alpha: 'alpha' }), - varsProvider({ gamma: 'gamma' }), - varsProvider({ alpha: 'beta' }), - ]; - - const vars = await legacyInternals.getVars('core', httpServerMock.createRawRequest()); - - expect(vars).toMatchInlineSnapshot(` - Object { - "alpha": "beta", - "gamma": "gamma", - } - `); - }); - - it('gets: no default injectors, with injected vars replacers, with ui app injectors, no inject arg', async () => { - uiExports.injectedVarsReplacers = [ - jest.fn(async (vars) => ({ ...vars, added: 'key' })), - jest.fn((vars) => vars), - jest.fn((vars) => ({ replaced: 'all' })), - jest.fn(async (vars) => ({ ...vars, added: 'last-key' })), - ]; - - const request = httpServerMock.createRawRequest(); - const vars = await legacyInternals.getVars('core', request); - - expect(vars).toMatchInlineSnapshot(` - Object { - "added": "last-key", - "replaced": "all", - } - `); - }); - - it('gets: no default injectors, no injected vars replacers, with ui app injectors, no inject arg', async () => { - legacyInternals.injectUiAppVars('core', async () => ({ is: 'core' })); - legacyInternals.injectUiAppVars('core', () => ({ sync: 'injector' })); - legacyInternals.injectUiAppVars('core', async () => ({ is: 'merged-core' })); - - const vars = await legacyInternals.getVars('core', httpServerMock.createRawRequest()); - - expect(vars).toMatchInlineSnapshot(` - Object { - "is": "merged-core", - "sync": "injector", - } - `); - }); - - it('gets: no default injectors, no injected vars replacers, no ui app injectors, with inject arg', async () => { - const vars = await legacyInternals.getVars('core', httpServerMock.createRawRequest(), { - injected: 'arg', - }); - - expect(vars).toMatchInlineSnapshot(` - Object { - "injected": "arg", - } - `); - }); - - it('gets: with default injectors, with injected vars replacers, with ui app injectors, with inject arg', async () => { - uiExports.defaultInjectedVarProviders = [ - varsProvider({ alpha: 'alpha' }), - varsProvider({ gamma: 'gamma' }), - varsProvider({ alpha: 'beta' }), - ]; - uiExports.injectedVarsReplacers = [jest.fn(async (vars) => ({ ...vars, gamma: 'delta' }))]; - - legacyInternals.injectUiAppVars('core', async () => ({ is: 'core' })); - legacyInternals.injectUiAppVars('core', () => ({ sync: 'injector' })); - legacyInternals.injectUiAppVars('core', async () => ({ is: 'merged-core' })); - - const vars = await legacyInternals.getVars('core', httpServerMock.createRawRequest(), { - injected: 'arg', - sync: 'arg', - }); - - expect(vars).toMatchInlineSnapshot(` - Object { - "alpha": "beta", - "gamma": "delta", - "injected": "arg", - "is": "merged-core", - "sync": "arg", - } - `); - }); - }); -}); diff --git a/src/core/server/legacy/legacy_internals.ts b/src/core/server/legacy/legacy_internals.ts deleted file mode 100644 index 628ca4ed12f6b..0000000000000 --- a/src/core/server/legacy/legacy_internals.ts +++ /dev/null @@ -1,93 +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 { Server } from 'hapi'; - -import { KibanaRequest, LegacyRequest } from '../http'; -import { ensureRawRequest } from '../http/router'; -import { mergeVars } from './merge_vars'; -import { ILegacyInternals, LegacyVars, VarsInjector, LegacyConfig, LegacyUiExports } from './types'; - -/** - * @internal - * @deprecated - */ -export class LegacyInternals implements ILegacyInternals { - private readonly injectors = new Map>(); - private cachedDefaultVars?: LegacyVars; - - constructor( - private readonly uiExports: LegacyUiExports, - private readonly config: LegacyConfig, - private readonly server: Server - ) {} - - private get defaultVars(): LegacyVars { - if (this.cachedDefaultVars) { - return this.cachedDefaultVars; - } - - const { defaultInjectedVarProviders = [] } = this.uiExports; - - return (this.cachedDefaultVars = defaultInjectedVarProviders.reduce( - (vars, { fn, pluginSpec }) => - mergeVars(vars, fn(this.server, pluginSpec.readConfigValue(this.config, []))), - {} - )); - } - - private replaceVars(vars: LegacyVars, request: KibanaRequest | LegacyRequest) { - const { injectedVarsReplacers = [] } = this.uiExports; - - return injectedVarsReplacers.reduce( - async (injected, replacer) => - replacer(await injected, ensureRawRequest(request), this.server), - Promise.resolve(vars) - ); - } - - public injectUiAppVars(id: string, injector: VarsInjector) { - if (!this.injectors.has(id)) { - this.injectors.set(id, new Set()); - } - - this.injectors.get(id)!.add(injector); - } - - public getInjectedUiAppVars(id: string) { - return [...(this.injectors.get(id) || [])].reduce( - async (promise, injector) => ({ - ...(await promise), - ...(await injector()), - }), - Promise.resolve({}) - ); - } - - public async getVars( - id: string, - request: KibanaRequest | LegacyRequest, - injected: LegacyVars = {} - ) { - return this.replaceVars( - mergeVars(this.defaultVars, await this.getInjectedUiAppVars(id), injected), - request - ); - } -} diff --git a/src/core/server/legacy/legacy_service.mock.ts b/src/core/server/legacy/legacy_service.mock.ts index ab501bd6bb53b..781874f702cf8 100644 --- a/src/core/server/legacy/legacy_service.mock.ts +++ b/src/core/server/legacy/legacy_service.mock.ts @@ -18,26 +18,13 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; import { LegacyService } from './legacy_service'; -import { LegacyConfig, LegacyServiceDiscoverPlugins, LegacyServiceSetupDeps } from './types'; +import { LegacyConfig, LegacyServiceSetupDeps } from './types'; type LegacyServiceMock = jest.Mocked & { legacyId: symbol }>; -const createDiscoverPluginsMock = (): LegacyServiceDiscoverPlugins => ({ - pluginSpecs: [], - uiExports: {}, - navLinks: [], - pluginExtendedConfig: { - get: jest.fn(), - has: jest.fn(), - set: jest.fn(), - }, - disabledPluginSpecs: [], - settings: {}, -}); - const createLegacyServiceMock = (): LegacyServiceMock => ({ legacyId: Symbol(), - discoverPlugins: jest.fn().mockResolvedValue(createDiscoverPluginsMock()), + setupLegacyConfig: jest.fn(), setup: jest.fn(), start: jest.fn(), stop: jest.fn(), @@ -52,6 +39,5 @@ const createLegacyConfigMock = (): jest.Mocked => ({ export const legacyServiceMock = { create: createLegacyServiceMock, createSetupContract: (deps: LegacyServiceSetupDeps) => createLegacyServiceMock().setup(deps), - createDiscoverPlugins: createDiscoverPluginsMock, createLegacyConfig: createLegacyConfigMock, }; diff --git a/src/core/server/legacy/legacy_service.test.mocks.ts b/src/core/server/legacy/legacy_service.test.mocks.ts deleted file mode 100644 index 9ad554d63add0..0000000000000 --- a/src/core/server/legacy/legacy_service.test.mocks.ts +++ /dev/null @@ -1,40 +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 { LegacyVars } from './types'; - -export const findLegacyPluginSpecsMock = jest.fn().mockImplementation((settings: LegacyVars) => ({ - pluginSpecs: [], - pluginExtendedConfig: { - has: jest.fn(), - get: jest.fn().mockReturnValue(settings), - set: jest.fn(), - }, - disabledPluginSpecs: [], - uiExports: {}, - navLinks: [], -})); -jest.doMock('./plugins/find_legacy_plugin_specs', () => ({ - findLegacyPluginSpecs: findLegacyPluginSpecsMock, -})); - -export const logLegacyThirdPartyPluginDeprecationWarningMock = jest.fn(); -jest.doMock('./plugins/log_legacy_plugins_warning', () => ({ - logLegacyThirdPartyPluginDeprecationWarning: logLegacyThirdPartyPluginDeprecationWarningMock, -})); diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index a6fe95deb3979..57009f0d35c16 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -19,10 +19,6 @@ jest.mock('../../../legacy/server/kbn_server'); jest.mock('./cluster_manager'); -import { - findLegacyPluginSpecsMock, - logLegacyThirdPartyPluginDeprecationWarningMock, -} from './legacy_service.test.mocks'; import { BehaviorSubject, throwError } from 'rxjs'; import { REPO_ROOT } from '@kbn/dev-utils'; @@ -44,8 +40,7 @@ import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mo import { httpResourcesMock } from '../http_resources/http_resources_service.mock'; import { setupMock as renderingServiceMock } from '../rendering/__mocks__/rendering_service'; import { environmentServiceMock } from '../environment/environment_service.mock'; -import { findLegacyPluginSpecs } from './plugins'; -import { LegacyVars, LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types'; +import { LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types'; import { LegacyService } from './legacy_service'; import { coreMock } from '../mocks'; import { statusServiceMock } from '../status/status_service.mock'; @@ -73,7 +68,6 @@ beforeEach(() => { configService = configServiceMock.create(); environmentSetup = environmentServiceMock.createSetupContract(); - findLegacyPluginSpecsMock.mockClear(); MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve()); MockKbnServer.prototype.listen = jest.fn(); @@ -149,10 +143,10 @@ describe('once LegacyService is set up with connection info', () => { coreId, env, logger, - configService: configService as any, + configService, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); @@ -160,13 +154,14 @@ describe('once LegacyService is set up with connection info', () => { expect(MockKbnServer).toHaveBeenCalledWith( { path: { autoListen: true }, server: { autoListen: true } }, // Because of the mock, path also gets the value expect.objectContaining({ get: expect.any(Function) }), - expect.any(Object), - { disabledPluginSpecs: [], pluginSpecs: [], uiExports: {}, navLinks: [] } + expect.any(Object) + ); + expect(MockKbnServer.mock.calls[0][1].get()).toEqual( + expect.objectContaining({ + path: expect.objectContaining({ autoListen: true }), + server: expect.objectContaining({ autoListen: true }), + }) ); - expect(MockKbnServer.mock.calls[0][1].get()).toEqual({ - path: { autoListen: true }, - server: { autoListen: true }, - }); const [mockKbnServer] = MockKbnServer.mock.instances; expect(mockKbnServer.listen).toHaveBeenCalledTimes(1); @@ -182,7 +177,7 @@ describe('once LegacyService is set up with connection info', () => { logger, configService: configService as any, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); @@ -190,13 +185,12 @@ describe('once LegacyService is set up with connection info', () => { expect(MockKbnServer).toHaveBeenCalledWith( { path: { autoListen: false }, server: { autoListen: true } }, expect.objectContaining({ get: expect.any(Function) }), - expect.any(Object), - { disabledPluginSpecs: [], pluginSpecs: [], uiExports: {}, navLinks: [] } + expect.any(Object) ); - expect(MockKbnServer.mock.calls[0][1].get()).toEqual({ - path: { autoListen: false }, - server: { autoListen: true }, - }); + + const legacyConfig = MockKbnServer.mock.calls[0][1].get(); + expect(legacyConfig.path.autoListen).toBe(false); + expect(legacyConfig.server.autoListen).toBe(true); const [mockKbnServer] = MockKbnServer.mock.instances; expect(mockKbnServer.ready).toHaveBeenCalledTimes(1); @@ -214,7 +208,7 @@ describe('once LegacyService is set up with connection info', () => { configService: configService as any, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot( `"something failed"` @@ -234,11 +228,11 @@ describe('once LegacyService is set up with connection info', () => { configService: configService as any, }); - await expect(legacyService.discoverPlugins()).rejects.toThrowErrorMatchingInlineSnapshot( + await expect(legacyService.setupLegacyConfig()).rejects.toThrowErrorMatchingInlineSnapshot( `"something failed"` ); await expect(legacyService.setup(setupDeps)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Legacy service has not discovered legacy plugins yet. Ensure LegacyService.discoverPlugins() is called before LegacyService.setup()"` + `"Legacy config not initialized yet. Ensure LegacyService.setupLegacyConfig() is called before LegacyService.setup()"` ); await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot( `"Legacy service is not setup yet."` @@ -255,7 +249,7 @@ describe('once LegacyService is set up with connection info', () => { logger, configService: configService as any, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); @@ -276,7 +270,7 @@ describe('once LegacyService is set up with connection info', () => { logger, configService: configService as any, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); @@ -301,7 +295,7 @@ describe('once LegacyService is set up with connection info', () => { logger, configService: configService as any, }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); @@ -321,7 +315,7 @@ describe('once LegacyService is set up without connection info', () => { let legacyService: LegacyService; beforeEach(async () => { legacyService = new LegacyService({ coreId, env, logger, configService: configService as any }); - await legacyService.discoverPlugins(); + await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); await legacyService.start(startDeps); }); @@ -331,13 +325,13 @@ describe('once LegacyService is set up without connection info', () => { expect(MockKbnServer).toHaveBeenCalledWith( { path: {}, server: { autoListen: true } }, expect.objectContaining({ get: expect.any(Function) }), - expect.any(Object), - { disabledPluginSpecs: [], pluginSpecs: [], uiExports: {}, navLinks: [] } + expect.any(Object) + ); + expect(MockKbnServer.mock.calls[0][1].get()).toEqual( + expect.objectContaining({ + server: expect.objectContaining({ autoListen: true }), + }) ); - expect(MockKbnServer.mock.calls[0][1].get()).toEqual({ - path: {}, - server: { autoListen: true }, - }); }); test('reconfigures logging configuration if new config is received.', async () => { @@ -375,7 +369,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { configService: configService as any, }); - await devClusterLegacyService.discoverPlugins(); + await devClusterLegacyService.setupLegacyConfig(); await devClusterLegacyService.setup(setupDeps); await devClusterLegacyService.start(startDeps); @@ -404,7 +398,7 @@ describe('once LegacyService is set up in `devClusterMaster` mode', () => { configService: configService as any, }); - await devClusterLegacyService.discoverPlugins(); + await devClusterLegacyService.setupLegacyConfig(); await devClusterLegacyService.setup(setupDeps); await devClusterLegacyService.start(startDeps); @@ -434,50 +428,6 @@ describe('start', () => { }); }); -describe('#discoverPlugins()', () => { - it('calls findLegacyPluginSpecs with correct parameters', async () => { - const legacyService = new LegacyService({ - coreId, - env, - logger, - configService: configService as any, - }); - - await legacyService.discoverPlugins(); - expect(findLegacyPluginSpecs).toHaveBeenCalledTimes(1); - expect(findLegacyPluginSpecs).toHaveBeenCalledWith(expect.any(Object), logger, env.packageInfo); - }); - - it(`logs deprecations for legacy third party plugins`, async () => { - const pluginSpecs = [{ getId: () => 'pluginA' }, { getId: () => 'pluginB' }]; - findLegacyPluginSpecsMock.mockImplementation( - (settings) => - Promise.resolve({ - pluginSpecs, - pluginExtendedConfig: settings, - disabledPluginSpecs: [], - uiExports: {}, - navLinks: [], - }) as any - ); - - const legacyService = new LegacyService({ - coreId, - env, - logger, - configService: configService as any, - }); - - await legacyService.discoverPlugins(); - - expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledTimes(1); - expect(logLegacyThirdPartyPluginDeprecationWarningMock).toHaveBeenCalledWith({ - specs: pluginSpecs, - log: expect.any(Object), - }); - }); -}); - test('Sets the server.uuid property on the legacy configuration', async () => { configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true })); const legacyService = new LegacyService({ @@ -489,23 +439,8 @@ test('Sets the server.uuid property on the legacy configuration', async () => { environmentSetup.instanceUuid = 'UUID_FROM_SERVICE'; - const configSetMock = jest.fn(); - - findLegacyPluginSpecsMock.mockImplementation((settings: LegacyVars) => ({ - pluginSpecs: [], - pluginExtendedConfig: { - has: jest.fn(), - get: jest.fn().mockReturnValue(settings), - set: configSetMock, - }, - disabledPluginSpecs: [], - uiExports: {}, - navLinks: [], - })); - - await legacyService.discoverPlugins(); + const { legacyConfig } = await legacyService.setupLegacyConfig(); await legacyService.setup(setupDeps); - expect(configSetMock).toHaveBeenCalledTimes(1); - expect(configSetMock).toHaveBeenCalledWith('server.uuid', 'UUID_FROM_SERVICE'); + expect(legacyConfig.get('server.uuid')).toBe('UUID_FROM_SERVICE'); }); diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 4dc22be2a9971..086e20c98c1a3 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -16,11 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import type { PublicMethodsOf } from '@kbn/utility-types'; + import { combineLatest, ConnectableObservable, EMPTY, Observable, Subscription } from 'rxjs'; import { first, map, publishReplay, tap } from 'rxjs/operators'; - +import type { PublicMethodsOf } from '@kbn/utility-types'; import { PathConfigType } from '@kbn/utils'; + +// @ts-expect-error legacy config class +import { Config as LegacyConfigClass } from '../../../legacy/server/config'; import { CoreService } from '../../types'; import { Config } from '../config'; import { CoreContext } from '../core_context'; @@ -28,17 +31,7 @@ import { CspConfigType, config as cspConfig } from '../csp'; import { DevConfig, DevConfigType, config as devConfig } from '../dev'; import { BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig } from '../http'; import { Logger } from '../logging'; -import { findLegacyPluginSpecs, logLegacyThirdPartyPluginDeprecationWarning } from './plugins'; -import { - ILegacyInternals, - LegacyServiceSetupDeps, - LegacyServiceStartDeps, - LegacyPlugins, - LegacyServiceDiscoverPlugins, - LegacyConfig, - LegacyVars, -} from './types'; -import { LegacyInternals } from './legacy_internals'; +import { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig, LegacyVars } from './types'; import { CoreSetup, CoreStart } from '..'; interface LegacyKbnServer { @@ -80,9 +73,7 @@ export class LegacyService implements CoreService { private setupDeps?: LegacyServiceSetupDeps; private update$?: ConnectableObservable<[Config, PathConfigType]>; private legacyRawConfig?: LegacyConfig; - private legacyPlugins?: LegacyPlugins; private settings?: LegacyVars; - public legacyInternals?: ILegacyInternals; constructor(private readonly coreContext: CoreContext) { const { logger, configService } = coreContext; @@ -97,11 +88,11 @@ export class LegacyService implements CoreService { ).pipe(map(([http, csp]) => new HttpConfig(http, csp))); } - public async discoverPlugins(): Promise { - this.update$ = combineLatest( + public async setupLegacyConfig() { + this.update$ = combineLatest([ this.coreContext.configService.getConfig$(), - this.coreContext.configService.atPath('path') - ).pipe( + this.coreContext.configService.atPath('path'), + ]).pipe( tap(([config, pathConfig]) => { if (this.kbnServer !== undefined) { this.kbnServer.applyLoggingConfiguration(getLegacyRawConfig(config, pathConfig)); @@ -120,74 +111,33 @@ export class LegacyService implements CoreService { ) .toPromise(); - const { - pluginSpecs, - pluginExtendedConfig, - disabledPluginSpecs, - uiExports, - navLinks, - } = await findLegacyPluginSpecs( - this.settings, - this.coreContext.logger, - this.coreContext.env.packageInfo - ); - - logLegacyThirdPartyPluginDeprecationWarning({ - specs: pluginSpecs, - log: this.log, - }); - - this.legacyPlugins = { - pluginSpecs, - disabledPluginSpecs, - uiExports, - navLinks, - }; - - this.legacyRawConfig = pluginExtendedConfig; - - // check for unknown uiExport types - if (uiExports.unknown && uiExports.unknown.length > 0) { - throw new Error( - `Unknown uiExport types: ${uiExports.unknown - .map(({ pluginSpec, type }) => `${type} from ${pluginSpec.getId()}`) - .join(', ')}` - ); - } + this.legacyRawConfig = LegacyConfigClass.withDefaultSchema(this.settings); return { - pluginSpecs, - disabledPluginSpecs, - uiExports, - navLinks, - pluginExtendedConfig, settings: this.settings, + legacyConfig: this.legacyRawConfig!, }; } public async setup(setupDeps: LegacyServiceSetupDeps) { this.log.debug('setting up legacy service'); - if (!this.legacyPlugins) { + if (!this.legacyRawConfig) { throw new Error( - 'Legacy service has not discovered legacy plugins yet. Ensure LegacyService.discoverPlugins() is called before LegacyService.setup()' + 'Legacy config not initialized yet. Ensure LegacyService.setupLegacyConfig() is called before LegacyService.setup()' ); } // propagate the instance uuid to the legacy config, as it was the legacy way to access it. this.legacyRawConfig!.set('server.uuid', setupDeps.core.environment.instanceUuid); + this.setupDeps = setupDeps; - this.legacyInternals = new LegacyInternals( - this.legacyPlugins.uiExports, - this.legacyRawConfig!, - setupDeps.core.http.server - ); } public async start(startDeps: LegacyServiceStartDeps) { const { setupDeps } = this; - if (!setupDeps || !this.legacyPlugins) { + if (!setupDeps || !this.legacyRawConfig) { throw new Error('Legacy service is not setup yet.'); } @@ -201,8 +151,7 @@ export class LegacyService implements CoreService { this.settings!, this.legacyRawConfig!, setupDeps, - startDeps, - this.legacyPlugins! + startDeps ); } } @@ -245,8 +194,7 @@ export class LegacyService implements CoreService { settings: LegacyVars, config: LegacyConfig, setupDeps: LegacyServiceSetupDeps, - startDeps: LegacyServiceStartDeps, - legacyPlugins: LegacyPlugins + startDeps: LegacyServiceStartDeps ) { const coreStart: CoreStart = { capabilities: startDeps.core.capabilities, @@ -337,36 +285,26 @@ export class LegacyService implements CoreService { // eslint-disable-next-line @typescript-eslint/no-var-requires const KbnServer = require('../../../legacy/server/kbn_server'); - const kbnServer: LegacyKbnServer = new KbnServer( - settings, - config, - { - env: { - mode: this.coreContext.env.mode, - packageInfo: this.coreContext.env.packageInfo, - }, - setupDeps: { - core: coreSetup, - plugins: setupDeps.plugins, - }, - startDeps: { - core: coreStart, - plugins: startDeps.plugins, - }, - __internals: { - http: { - registerStaticDir: setupDeps.core.http.registerStaticDir, - }, - hapiServer: setupDeps.core.http.server, - uiPlugins: setupDeps.uiPlugins, - elasticsearch: setupDeps.core.elasticsearch, - rendering: setupDeps.core.rendering, - legacy: this.legacyInternals, - }, - logger: this.coreContext.logger, + const kbnServer: LegacyKbnServer = new KbnServer(settings, config, { + env: { + mode: this.coreContext.env.mode, + packageInfo: this.coreContext.env.packageInfo, }, - legacyPlugins - ); + setupDeps: { + core: coreSetup, + plugins: setupDeps.plugins, + }, + startDeps: { + core: coreStart, + plugins: startDeps.plugins, + }, + __internals: { + hapiServer: setupDeps.core.http.server, + uiPlugins: setupDeps.uiPlugins, + rendering: setupDeps.core.rendering, + }, + logger: this.coreContext.logger, + }); // The kbnWorkerType check is necessary to prevent the repl // from being started multiple times in different processes. diff --git a/src/core/server/legacy/plugins/collect_ui_exports.js b/src/core/server/legacy/plugins/collect_ui_exports.js deleted file mode 100644 index 842ab554d79d1..0000000000000 --- a/src/core/server/legacy/plugins/collect_ui_exports.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { collectUiExports } from '../../../../legacy/ui/ui_exports/collect_ui_exports'; diff --git a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts b/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts deleted file mode 100644 index cb4277b130a88..0000000000000 --- a/src/core/server/legacy/plugins/find_legacy_plugin_specs.ts +++ /dev/null @@ -1,135 +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 { Observable, merge, forkJoin } from 'rxjs'; -import { toArray, tap, distinct, map } from 'rxjs/operators'; - -import { - findPluginSpecs, - defaultConfig, - // @ts-expect-error -} from '../../../../legacy/plugin_discovery/find_plugin_specs.js'; -// @ts-expect-error -import { collectUiExports as collectLegacyUiExports } from './collect_ui_exports'; - -import { LoggerFactory } from '../../logging'; -import { PackageInfo } from '../../config'; -import { LegacyUiExports, LegacyPluginSpec, LegacyPluginPack, LegacyConfig } from '../types'; - -export async function findLegacyPluginSpecs( - settings: unknown, - loggerFactory: LoggerFactory, - packageInfo: PackageInfo -) { - const configToMutate: LegacyConfig = defaultConfig(settings); - const { - pack$, - invalidDirectoryError$, - invalidPackError$, - otherError$, - deprecation$, - invalidVersionSpec$, - spec$, - disabledSpec$, - }: { - pack$: Observable; - invalidDirectoryError$: Observable<{ path: string }>; - invalidPackError$: Observable<{ path: string }>; - otherError$: Observable; - deprecation$: Observable<{ spec: LegacyPluginSpec; message: string }>; - invalidVersionSpec$: Observable; - spec$: Observable; - disabledSpec$: Observable; - } = findPluginSpecs(settings, configToMutate) as any; - - const logger = loggerFactory.get('legacy-plugins'); - - const log$ = merge( - pack$.pipe( - tap((definition) => { - const path = definition.getPath(); - logger.debug(`Found plugin at ${path}`, { path }); - }) - ), - - invalidDirectoryError$.pipe( - tap((error) => { - logger.warn(`Unable to scan directory for plugins "${error.path}"`, { - err: error, - dir: error.path, - }); - }) - ), - - invalidPackError$.pipe( - tap((error) => { - logger.warn(`Skipping non-plugin directory at ${error.path}`, { - path: error.path, - }); - }) - ), - - otherError$.pipe( - tap((error) => { - // rethrow unhandled errors, which will fail the server - throw error; - }) - ), - - invalidVersionSpec$.pipe( - map((spec) => { - const name = spec.getId(); - const pluginVersion = spec.getExpectedKibanaVersion(); - const kibanaVersion = packageInfo.version; - return `Plugin "${name}" was disabled because it expected Kibana version "${pluginVersion}", and found "${kibanaVersion}".`; - }), - distinct(), - tap((message) => { - logger.warn(message); - }) - ), - - deprecation$.pipe( - tap(({ spec, message }) => { - const deprecationLogger = loggerFactory.get( - 'plugins', - spec.getConfigPrefix(), - 'config', - 'deprecation' - ); - deprecationLogger.warn(message); - }) - ) - ); - - const [disabledPluginSpecs, pluginSpecs] = await forkJoin( - disabledSpec$.pipe(toArray()), - spec$.pipe(toArray()), - log$.pipe(toArray()) - ).toPromise(); - const uiExports: LegacyUiExports = collectLegacyUiExports(pluginSpecs); - - return { - disabledPluginSpecs, - pluginSpecs, - pluginExtendedConfig: configToMutate, - uiExports, - navLinks: [], - }; -} diff --git a/src/core/server/legacy/plugins/index.ts b/src/core/server/legacy/plugins/index.ts deleted file mode 100644 index 7ec5dbc1983ab..0000000000000 --- a/src/core/server/legacy/plugins/index.ts +++ /dev/null @@ -1,21 +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 { findLegacyPluginSpecs } from './find_legacy_plugin_specs'; -export { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning'; diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts deleted file mode 100644 index 2317f1036ce42..0000000000000 --- a/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts +++ /dev/null @@ -1,89 +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 { loggerMock } from '../../logging/logger.mock'; -import { logLegacyThirdPartyPluginDeprecationWarning } from './log_legacy_plugins_warning'; -import { LegacyPluginSpec } from '../types'; - -const createPluginSpec = ({ id, path }: { id: string; path: string }): LegacyPluginSpec => { - return { - getId: () => id, - getExpectedKibanaVersion: () => 'kibana', - getConfigPrefix: () => 'plugin.config', - getPack: () => ({ - getPath: () => path, - }), - }; -}; - -describe('logLegacyThirdPartyPluginDeprecationWarning', () => { - let log: ReturnType; - - beforeEach(() => { - log = loggerMock.create(); - }); - - it('logs warning for third party plugins', () => { - logLegacyThirdPartyPluginDeprecationWarning({ - specs: [createPluginSpec({ id: 'plugin', path: '/some-external-path' })], - log, - }); - expect(log.warn).toHaveBeenCalledTimes(1); - expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "Some installed third party plugin(s) [plugin] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://ela.st/kibana-breaking-changes-8-0 for a list of breaking changes and https://ela.st/kibana-platform-migration for documentation on how to migrate legacy plugins.", - ] - `); - }); - - it('lists all the deprecated plugins and only log once', () => { - logLegacyThirdPartyPluginDeprecationWarning({ - specs: [ - createPluginSpec({ id: 'pluginA', path: '/abs/path/to/pluginA' }), - createPluginSpec({ id: 'pluginB', path: '/abs/path/to/pluginB' }), - createPluginSpec({ id: 'pluginC', path: '/abs/path/to/pluginC' }), - ], - log, - }); - expect(log.warn).toHaveBeenCalledTimes(1); - expect(log.warn.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "Some installed third party plugin(s) [pluginA, pluginB, pluginC] are using the legacy plugin format and will no longer work in a future Kibana release. Please refer to https://ela.st/kibana-breaking-changes-8-0 for a list of breaking changes and https://ela.st/kibana-platform-migration for documentation on how to migrate legacy plugins.", - ] - `); - }); - - it('does not log warning for internal legacy plugins', () => { - logLegacyThirdPartyPluginDeprecationWarning({ - specs: [ - createPluginSpec({ - id: 'plugin', - path: '/absolute/path/to/kibana/src/legacy/core_plugins', - }), - createPluginSpec({ - id: 'plugin', - path: '/absolute/path/to/kibana/x-pack', - }), - ], - log, - }); - - expect(log.warn).not.toHaveBeenCalled(); - }); -}); diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts deleted file mode 100644 index 4a4a1b1b0e60b..0000000000000 --- a/src/core/server/legacy/plugins/log_legacy_plugins_warning.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 { Logger } from '../../logging'; -import { LegacyPluginSpec } from '../types'; - -const internalPaths = ['/src/legacy/core_plugins', '/x-pack']; - -// Use shortened URLs so destinations can be updated if/when documentation moves -// All platform team members have access to edit these -const breakingChangesUrl = 'https://ela.st/kibana-breaking-changes-8-0'; -const migrationGuideUrl = 'https://ela.st/kibana-platform-migration'; - -export const logLegacyThirdPartyPluginDeprecationWarning = ({ - specs, - log, -}: { - specs: LegacyPluginSpec[]; - log: Logger; -}) => { - const thirdPartySpecs = specs.filter(isThirdPartyPluginSpec); - if (thirdPartySpecs.length > 0) { - const pluginIds = thirdPartySpecs.map((spec) => spec.getId()); - log.warn( - `Some installed third party plugin(s) [${pluginIds.join( - ', ' - )}] are using the legacy plugin format and will no longer work in a future Kibana release. ` + - `Please refer to ${breakingChangesUrl} for a list of breaking changes ` + - `and ${migrationGuideUrl} for documentation on how to migrate legacy plugins.` - ); - } -}; - -const isThirdPartyPluginSpec = (spec: LegacyPluginSpec): boolean => { - const pluginPath = spec.getPack().getPath(); - return !internalPaths.some((internalPath) => pluginPath.indexOf(internalPath) > -1); -}; diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts index 1105308fd44cf..12bfddfff1961 100644 --- a/src/core/server/legacy/types.ts +++ b/src/core/server/legacy/types.ts @@ -17,10 +17,6 @@ * under the License. */ -import { Server } from 'hapi'; - -import { ChromeNavLink } from '../../public'; -import { KibanaRequest, LegacyRequest } from '../http'; import { InternalCoreSetup, InternalCoreStart } from '../internal_types'; import { PluginsServiceSetup, PluginsServiceStart, UiPlugins } from '../plugins'; import { InternalRenderingServiceSetup } from '../rendering'; @@ -50,91 +46,6 @@ export interface LegacyConfig { set(config: LegacyVars): void; } -/** - * @internal - * @deprecated - */ -export interface LegacyPluginPack { - getPath(): string; -} - -/** - * @internal - * @deprecated - */ -export interface LegacyPluginSpec { - getId: () => unknown; - getExpectedKibanaVersion: () => string; - getConfigPrefix: () => string; - getPack: () => LegacyPluginPack; -} - -/** - * @internal - * @deprecated - */ -export interface VarsProvider { - fn: (server: Server, configValue: any) => LegacyVars; - pluginSpec: { - readConfigValue(config: any, key: string | string[]): any; - }; -} - -/** - * @internal - * @deprecated - */ -export type VarsInjector = () => LegacyVars; - -/** - * @internal - * @deprecated - */ -export type VarsReplacer = ( - vars: LegacyVars, - request: LegacyRequest, - server: Server -) => LegacyVars | Promise; - -/** - * @internal - * @deprecated - */ -export type LegacyNavLinkSpec = Partial & { - id: string; - title: string; - url: string; -}; - -/** - * @internal - * @deprecated - */ -export type LegacyAppSpec = Partial & { - pluginId?: string; - listed?: boolean; -}; - -/** - * @internal - * @deprecated - */ -export type LegacyNavLink = Omit & { - order: number; -}; - -/** - * @internal - * @deprecated - */ -export interface LegacyUiExports { - defaultInjectedVarProviders?: VarsProvider[]; - injectedVarsReplacers?: VarsReplacer[]; - navLinkSpecs?: LegacyNavLinkSpec[] | null; - uiAppSpecs?: Array; - unknown?: [{ pluginSpec: LegacyPluginSpec; type: unknown }]; -} - /** * @public * @deprecated @@ -158,43 +69,7 @@ export interface LegacyServiceStartDeps { * @internal * @deprecated */ -export interface ILegacyInternals { - /** - * Inject UI app vars for a particular plugin - */ - injectUiAppVars(id: string, injector: VarsInjector): void; - - /** - * Get all the merged injected UI app vars for a particular plugin - */ - getInjectedUiAppVars(id: string): Promise; - - /** - * Get the metadata vars for a particular plugin - */ - getVars( - id: string, - request: KibanaRequest | LegacyRequest, - injected?: LegacyVars - ): Promise; -} - -/** - * @internal - * @deprecated - */ -export interface LegacyPlugins { - disabledPluginSpecs: LegacyPluginSpec[]; - pluginSpecs: LegacyPluginSpec[]; - uiExports: LegacyUiExports; - navLinks: LegacyNavLink[]; -} - -/** - * @internal - * @deprecated - */ -export interface LegacyServiceDiscoverPlugins extends LegacyPlugins { - pluginExtendedConfig: LegacyConfig; +export interface LegacyServiceSetupConfig { + legacyConfig: LegacyConfig; settings: LegacyVars; } diff --git a/src/core/server/metrics/collectors/cgroup.test.ts b/src/core/server/metrics/collectors/cgroup.test.ts index 39f917b9f0ba1..163646bf55424 100644 --- a/src/core/server/metrics/collectors/cgroup.test.ts +++ b/src/core/server/metrics/collectors/cgroup.test.ts @@ -18,6 +18,7 @@ */ import mockFs from 'mock-fs'; +import { loggerMock } from '@kbn/logging/target/mocks'; import { OsCgroupMetricsCollector } from './cgroup'; describe('OsCgroupMetricsCollector', () => { @@ -30,8 +31,10 @@ describe('OsCgroupMetricsCollector', () => { }, }); - const collector = new OsCgroupMetricsCollector({}); + const logger = loggerMock.create(); + const collector = new OsCgroupMetricsCollector({ logger }); expect(await collector.collect()).toEqual({}); + expect(logger.error).not.toHaveBeenCalled(); }); it('collects default cgroup data', async () => { @@ -51,7 +54,7 @@ throttled_time 666 `, }); - const collector = new OsCgroupMetricsCollector({}); + const collector = new OsCgroupMetricsCollector({ logger: loggerMock.create() }); expect(await collector.collect()).toMatchInlineSnapshot(` Object { "cpu": Object { @@ -90,6 +93,7 @@ throttled_time 666 }); const collector = new OsCgroupMetricsCollector({ + logger: loggerMock.create(), cpuAcctPath: 'xxcustomcpuacctxx', cpuPath: 'xxcustomcpuxx', }); @@ -112,4 +116,23 @@ throttled_time 666 } `); }); + + it('returns empty object and logs error on an EACCES error', async () => { + mockFs({ + '/proc/self/cgroup': ` +123:memory:/groupname +123:cpu:/groupname +123:cpuacct:/groupname + `, + '/sys/fs/cgroup': mockFs.directory({ mode: parseInt('0000', 8) }), + }); + + const logger = loggerMock.create(); + + const collector = new OsCgroupMetricsCollector({ logger }); + expect(await collector.collect()).toEqual({}); + expect(logger.error).toHaveBeenCalledWith( + "cgroup metrics could not be read due to error: [Error: EACCES, permission denied '/sys/fs/cgroup/cpuacct/groupname/cpuacct.usage']" + ); + }); }); diff --git a/src/core/server/metrics/collectors/cgroup.ts b/src/core/server/metrics/collectors/cgroup.ts index 867ea44dff1ae..42f5d30d115fe 100644 --- a/src/core/server/metrics/collectors/cgroup.ts +++ b/src/core/server/metrics/collectors/cgroup.ts @@ -19,11 +19,13 @@ import fs from 'fs'; import { join as joinPath } from 'path'; +import { Logger } from '@kbn/logging'; import { MetricsCollector, OpsOsMetrics } from './types'; type OsCgroupMetrics = Pick; interface OsCgroupMetricsCollectorOptions { + logger: Logger; cpuPath?: string; cpuAcctPath?: string; } @@ -38,8 +40,12 @@ export class OsCgroupMetricsCollector implements MetricsCollector { try { + if (this.noCgroupPresent) { + return {}; + } + await this.initializePaths(); - if (this.noCgroupPresent || !this.cpuAcctPath || !this.cpuPath) { + if (!this.cpuAcctPath || !this.cpuPath) { return {}; } @@ -64,12 +70,15 @@ export class OsCgroupMetricsCollector implements MetricsCollector (cb: Function) => cb(null, { dist: 'distrib', release: 'release' })); +import { loggerMock } from '@kbn/logging/target/mocks'; import os from 'os'; import { cgroupCollectorMock } from './os.test.mocks'; import { OsMetricsCollector } from './os'; @@ -27,7 +28,7 @@ describe('OsMetricsCollector', () => { let collector: OsMetricsCollector; beforeEach(() => { - collector = new OsMetricsCollector(); + collector = new OsMetricsCollector({ logger: loggerMock.create() }); cgroupCollectorMock.collect.mockReset(); cgroupCollectorMock.reset.mockReset(); }); diff --git a/src/core/server/metrics/collectors/os.ts b/src/core/server/metrics/collectors/os.ts index eae49278405a9..a9d727e57aaf9 100644 --- a/src/core/server/metrics/collectors/os.ts +++ b/src/core/server/metrics/collectors/os.ts @@ -20,12 +20,14 @@ import os from 'os'; import getosAsync, { LinuxOs } from 'getos'; import { promisify } from 'util'; +import { Logger } from '@kbn/logging'; import { OpsOsMetrics, MetricsCollector } from './types'; import { OsCgroupMetricsCollector } from './cgroup'; const getos = promisify(getosAsync); export interface OpsMetricsCollectorOptions { + logger: Logger; cpuPath?: string; cpuAcctPath?: string; } @@ -33,8 +35,11 @@ export interface OpsMetricsCollectorOptions { export class OsMetricsCollector implements MetricsCollector { private readonly cgroupCollector: OsCgroupMetricsCollector; - constructor(options: OpsMetricsCollectorOptions = {}) { - this.cgroupCollector = new OsCgroupMetricsCollector(options); + constructor(options: OpsMetricsCollectorOptions) { + this.cgroupCollector = new OsCgroupMetricsCollector({ + ...options, + logger: options.logger.get('cgroup'), + }); } public async collect(): Promise { diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts index ab58a75d49a98..d3495f2748c71 100644 --- a/src/core/server/metrics/metrics_service.ts +++ b/src/core/server/metrics/metrics_service.ts @@ -50,7 +50,10 @@ export class MetricsService .pipe(first()) .toPromise(); - this.metricsCollector = new OpsMetricsCollector(http.server, config.cGroupOverrides); + this.metricsCollector = new OpsMetricsCollector(http.server, { + logger: this.logger, + ...config.cGroupOverrides, + }); await this.refreshMetrics(); diff --git a/src/core/server/metrics/ops_metrics_collector.test.ts b/src/core/server/metrics/ops_metrics_collector.test.ts index 7aa3f7cd3baf0..c748d1cce12e4 100644 --- a/src/core/server/metrics/ops_metrics_collector.test.ts +++ b/src/core/server/metrics/ops_metrics_collector.test.ts @@ -17,6 +17,7 @@ * under the License. */ +import { loggerMock } from '@kbn/logging/target/mocks'; import { mockOsCollector, mockProcessCollector, @@ -30,7 +31,7 @@ describe('OpsMetricsCollector', () => { beforeEach(() => { const hapiServer = httpServiceMock.createInternalSetupContract().server; - collector = new OpsMetricsCollector(hapiServer, {}); + collector = new OpsMetricsCollector(hapiServer, { logger: loggerMock.create() }); mockOsCollector.collect.mockResolvedValue('osMetrics'); }); diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 7e001ffe28100..3030cd9f4e0cb 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -54,6 +54,7 @@ export { metricsServiceMock } from './metrics/metrics_service.mock'; export { renderingMock } from './rendering/rendering_service.mock'; export { statusServiceMock } from './status/status_service.mock'; export { contextServiceMock } from './context/context_service.mock'; +export { capabilitiesServiceMock } from './capabilities/capabilities_service.mock'; export function pluginInitializerContextConfigMock(config: T) { const globalConfig: SharedGlobalConfig = { @@ -66,7 +67,6 @@ export function pluginInitializerContextConfigMock(config: T) { shardTimeout: duration('30s'), requestTimeout: duration('30s'), pingTimeout: duration('30s'), - startupTimeout: duration('30s'), }, path: { data: '/tmp' }, savedObjects: { diff --git a/src/core/server/plugins/plugin_context.test.ts b/src/core/server/plugins/plugin_context.test.ts index cb4e8f20be982..7b2a5cd3b35f8 100644 --- a/src/core/server/plugins/plugin_context.test.ts +++ b/src/core/server/plugins/plugin_context.test.ts @@ -91,7 +91,6 @@ describe('createPluginInitializerContext', () => { shardTimeout: duration(30, 's'), requestTimeout: duration(30, 's'), pingTimeout: duration(30, 's'), - startupTimeout: duration(5, 's'), }, path: { data: fromRoot('data') }, savedObjects: { maxImportPayloadBytes: new ByteSizeValue(10485760) }, diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 9de181124a349..0dd481687eb59 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -262,7 +262,7 @@ export interface Plugin< export const SharedGlobalConfigKeys = { // We can add more if really needed kibana: ['index', 'autocompleteTerminateAfter', 'autocompleteTimeout'] as const, - elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout', 'startupTimeout'] as const, + elasticsearch: ['shardTimeout', 'requestTimeout', 'pingTimeout'] as const, path: ['data'] as const, savedObjects: ['maxImportPayloadBytes'] as const, }; diff --git a/src/core/server/rendering/__mocks__/params.ts b/src/core/server/rendering/__mocks__/params.ts index 0901cec768cd2..ae3830f703a53 100644 --- a/src/core/server/rendering/__mocks__/params.ts +++ b/src/core/server/rendering/__mocks__/params.ts @@ -20,19 +20,16 @@ import { mockCoreContext } from '../../core_context.mock'; import { httpServiceMock } from '../../http/http_service.mock'; import { pluginServiceMock } from '../../plugins/plugins_service.mock'; -import { legacyServiceMock } from '../../legacy/legacy_service.mock'; import { statusServiceMock } from '../../status/status_service.mock'; const context = mockCoreContext.create(); const http = httpServiceMock.createInternalSetupContract(); const uiPlugins = pluginServiceMock.createUiPlugins(); -const legacyPlugins = legacyServiceMock.createDiscoverPlugins(); const status = statusServiceMock.createInternalSetupContract(); export const mockRenderingServiceParams = context; export const mockRenderingSetupDeps = { http, - legacyPlugins, uiPlugins, status, }; diff --git a/src/core/server/rendering/__mocks__/rendering_service.ts b/src/core/server/rendering/__mocks__/rendering_service.ts index 179a09b8619b0..01d084f9ae53c 100644 --- a/src/core/server/rendering/__mocks__/rendering_service.ts +++ b/src/core/server/rendering/__mocks__/rendering_service.ts @@ -27,11 +27,9 @@ export const setupMock: jest.Mocked = { render: jest.fn(), }; export const mockSetup = jest.fn().mockResolvedValue(setupMock); -export const mockStart = jest.fn(); export const mockStop = jest.fn(); export const mockRenderingService: jest.Mocked = { setup: mockSetup, - start: mockStart, stop: mockStop, }; export const RenderingService = jest.fn( diff --git a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap index ab828a1780425..07ca59a48c6b0 100644 --- a/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap +++ b/src/core/server/rendering/__snapshots__/rendering_service.test.ts.snap @@ -27,15 +27,6 @@ Object { "translationsUrl": "/mock-server-basepath/translations/en.json", }, "legacyMetadata": Object { - "app": Object {}, - "basePath": "/mock-server-basepath", - "branch": Any, - "buildNum": Any, - "buildSha": Any, - "bundleId": "app:core", - "devMode": true, - "nav": Array [], - "serverName": "http-server-test", "uiSettings": Object { "defaults": Object { "registered": Object { @@ -44,7 +35,6 @@ Object { }, "user": Object {}, }, - "version": Any, }, "serverBasePath": "/mock-server-basepath", "uiPlugins": Array [], @@ -80,15 +70,6 @@ Object { "translationsUrl": "/mock-server-basepath/translations/en.json", }, "legacyMetadata": Object { - "app": Object {}, - "basePath": "/mock-server-basepath", - "branch": Any, - "buildNum": Any, - "buildSha": Any, - "bundleId": "app:core", - "devMode": true, - "nav": Array [], - "serverName": "http-server-test", "uiSettings": Object { "defaults": Object { "registered": Object { @@ -97,7 +78,6 @@ Object { }, "user": Object {}, }, - "version": Any, }, "serverBasePath": "/mock-server-basepath", "uiPlugins": Array [], @@ -133,15 +113,6 @@ Object { "translationsUrl": "/mock-server-basepath/translations/en.json", }, "legacyMetadata": Object { - "app": Object {}, - "basePath": "/mock-server-basepath", - "branch": Any, - "buildNum": Any, - "buildSha": Any, - "bundleId": "app:core", - "devMode": true, - "nav": Array [], - "serverName": "http-server-test", "uiSettings": Object { "defaults": Object { "registered": Object { @@ -154,7 +125,6 @@ Object { }, }, }, - "version": Any, }, "serverBasePath": "/mock-server-basepath", "uiPlugins": Array [], @@ -190,15 +160,6 @@ Object { "translationsUrl": "/translations/en.json", }, "legacyMetadata": Object { - "app": Object {}, - "basePath": "", - "branch": Any, - "buildNum": Any, - "buildSha": Any, - "bundleId": "app:core", - "devMode": true, - "nav": Array [], - "serverName": "http-server-test", "uiSettings": Object { "defaults": Object { "registered": Object { @@ -207,7 +168,6 @@ Object { }, "user": Object {}, }, - "version": Any, }, "serverBasePath": "/mock-server-basepath", "uiPlugins": Array [], @@ -243,15 +203,6 @@ Object { "translationsUrl": "/mock-server-basepath/translations/en.json", }, "legacyMetadata": Object { - "app": Object {}, - "basePath": "/mock-server-basepath", - "branch": Any, - "buildNum": Any, - "buildSha": Any, - "bundleId": "app:core", - "devMode": true, - "nav": Array [], - "serverName": "http-server-test", "uiSettings": Object { "defaults": Object { "registered": Object { @@ -260,7 +211,6 @@ Object { }, "user": Object {}, }, - "version": Any, }, "serverBasePath": "/mock-server-basepath", "uiPlugins": Array [], diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index 254bafed5b194..08978cd1df64d 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -43,12 +43,6 @@ const INJECTED_METADATA = { version: expect.any(String), }, }, - legacyMetadata: { - branch: expect.any(String), - buildNum: expect.any(Number), - buildSha: expect.any(String), - version: expect.any(String), - }, }; const { createKibanaRequest, createRawRequest } = httpServerMock; @@ -72,13 +66,6 @@ describe('RenderingService', () => { registered: { name: 'title' }, }); render = (await service.setup(mockRenderingSetupDeps)).render; - await service.start({ - legacy: { - legacyInternals: { - getVars: () => ({}), - }, - }, - } as any); }); it('renders "core" page', async () => { diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 7761c89044f6f..738787f940905 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -20,14 +20,11 @@ import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { take } from 'rxjs/operators'; - import { i18n } from '@kbn/i18n'; import { UiPlugins } from '../plugins'; -import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Template } from './views'; -import { LegacyService } from '../legacy'; import { IRenderOptions, RenderingSetupDeps, @@ -36,25 +33,20 @@ import { } from './types'; /** @internal */ -export class RenderingService implements CoreService { - private legacyInternals?: LegacyService['legacyInternals']; +export class RenderingService { constructor(private readonly coreContext: CoreContext) {} public async setup({ http, status, - legacyPlugins, uiPlugins, }: RenderingSetupDeps): Promise { return { render: async ( request, uiSettings, - { app = { getId: () => 'core' }, includeUserSettings = true, vars }: IRenderOptions = {} + { includeUserSettings = true, vars }: IRenderOptions = {} ) => { - if (!this.legacyInternals) { - throw new Error('Cannot render before "start"'); - } const env = { mode: this.coreContext.env.mode, packageInfo: this.coreContext.env.packageInfo, @@ -65,7 +57,6 @@ export class RenderingService implements CoreService ({ id, @@ -96,16 +87,6 @@ export class RenderingService implements CoreService; }>; legacyMetadata: { - app: { getId(): string }; - bundleId: string; - nav: LegacyNavLink[]; - version: string; - branch: string; - buildNum: number; - buildSha: string; - serverName: string; - devMode: boolean; - basePath: string; uiSettings: { defaults: Record; user: Record>; @@ -78,7 +67,6 @@ export interface RenderingMetadata { /** @internal */ export interface RenderingSetupDeps { http: InternalHttpServiceSetup; - legacyPlugins: LegacyServiceDiscoverPlugins; status: InternalStatusServiceSetup; uiPlugins: UiPlugins; } @@ -91,14 +79,6 @@ export interface IRenderOptions { */ includeUserSettings?: boolean; - /** - * Render the bootstrapped HTML content for an optional legacy application. - * Defaults to `core`. - * @deprecated for legacy use only, remove with ui_render_mixin - * @internal - */ - app?: { getId(): string }; - /** * Inject custom vars into the page metadata. * @deprecated for legacy use only, remove with ui_render_mixin diff --git a/src/core/server/saved_objects/migrations/core/migration_es_client.ts b/src/core/server/saved_objects/migrations/core/migration_es_client.ts index ff859057f8fe8..e8482e6352a82 100644 --- a/src/core/server/saved_objects/migrations/core/migration_es_client.ts +++ b/src/core/server/saved_objects/migrations/core/migration_es_client.ts @@ -80,7 +80,7 @@ export function createMigrationEsClient( throw new Error(`unknown ElasticsearchClient client method [${key}]`); } return await migrationRetryCallCluster( - () => fn(params, { maxRetries: 0, ...options }), + () => fn.call(client, params, { maxRetries: 0, ...options }), log, delay ); diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 352ce4c1c16eb..0e72ad2fec06c 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -2477,6 +2477,33 @@ describe('SavedObjectsRepository', () => { expect(client.search).not.toHaveBeenCalled(); }); + it(`throws when namespaces is an empty array`, async () => { + await expect( + savedObjectsRepository.find({ type: 'foo', namespaces: [] }) + ).rejects.toThrowError('options.namespaces cannot be an empty array'); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when type is not falsy and typeToNamespacesMap is defined`, async () => { + await expect( + savedObjectsRepository.find({ type: 'foo', typeToNamespacesMap: new Map() }) + ).rejects.toThrowError( + 'options.type must be an empty string when options.typeToNamespacesMap is used' + ); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when type is not an empty array and typeToNamespacesMap is defined`, async () => { + const test = async (args) => { + await expect(savedObjectsRepository.find(args)).rejects.toThrowError( + 'options.namespaces must be an empty array when options.typeToNamespacesMap is used' + ); + expect(client.search).not.toHaveBeenCalled(); + }; + await test({ type: '', typeToNamespacesMap: new Map() }); + await test({ type: '', namespaces: ['some-ns'], typeToNamespacesMap: new Map() }); + }); + it(`throws when searchFields is defined but not an array`, async () => { await expect( savedObjectsRepository.find({ type, searchFields: 'string' }) @@ -2493,7 +2520,7 @@ describe('SavedObjectsRepository', () => { it(`throws when KQL filter syntax is invalid`, async () => { const findOpts = { - namespace, + namespaces: [namespace], search: 'foo*', searchFields: ['foo'], type: ['dashboard'], @@ -2577,38 +2604,70 @@ describe('SavedObjectsRepository', () => { const test = async (types) => { const result = await savedObjectsRepository.find({ type: types }); expect(result).toEqual(expect.objectContaining({ saved_objects: [] })); + expect(client.search).not.toHaveBeenCalled(); }; await test('unknownType'); await test(HIDDEN_TYPE); await test(['unknownType', HIDDEN_TYPE]); }); + + it(`should return empty results when attempting to find only invalid or hidden types using typeToNamespacesMap`, async () => { + const test = async (types) => { + const result = await savedObjectsRepository.find({ + typeToNamespacesMap: new Map(types.map((x) => [x, undefined])), + type: '', + namespaces: [], + }); + expect(result).toEqual(expect.objectContaining({ saved_objects: [] })); + expect(client.search).not.toHaveBeenCalled(); + }; + + await test(['unknownType']); + await test([HIDDEN_TYPE]); + await test(['unknownType', HIDDEN_TYPE]); + }); }); describe('search dsl', () => { - it(`passes mappings, registry, search, defaultSearchOperator, searchFields, type, sortField, sortOrder and hasReference to getSearchDsl`, async () => { + const commonOptions = { + type: [type], // cannot be used when `typeToNamespacesMap` is present + namespaces: [namespace], // cannot be used when `typeToNamespacesMap` is present + search: 'foo*', + searchFields: ['foo'], + sortField: 'name', + sortOrder: 'desc', + defaultSearchOperator: 'AND', + hasReference: { + type: 'foo', + id: '1', + }, + kueryNode: undefined, + }; + + it(`passes mappings, registry, and search options to getSearchDsl`, async () => { + await findSuccess(commonOptions, namespace); + expect(getSearchDslNS.getSearchDsl).toHaveBeenCalledWith(mappings, registry, commonOptions); + }); + + it(`accepts typeToNamespacesMap`, async () => { const relevantOpts = { - namespaces: [namespace], - search: 'foo*', - searchFields: ['foo'], - type: [type], - sortField: 'name', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - hasReference: { - type: 'foo', - id: '1', - }, - kueryNode: undefined, + ...commonOptions, + type: '', + namespaces: [], + typeToNamespacesMap: new Map([[type, [namespace]]]), // can only be used when `type` is falsy and `namespaces` is an empty array }; await findSuccess(relevantOpts, namespace); - expect(getSearchDslNS.getSearchDsl).toHaveBeenCalledWith(mappings, registry, relevantOpts); + expect(getSearchDslNS.getSearchDsl).toHaveBeenCalledWith(mappings, registry, { + ...relevantOpts, + type: [type], + }); }); it(`accepts KQL expression filter and passes KueryNode to getSearchDsl`, async () => { const findOpts = { - namespace, + namespaces: [namespace], search: 'foo*', searchFields: ['foo'], type: ['dashboard'], @@ -2649,7 +2708,7 @@ describe('SavedObjectsRepository', () => { it(`accepts KQL KueryNode filter and passes KueryNode to getSearchDsl`, async () => { const findOpts = { - namespace, + namespaces: [namespace], search: 'foo*', searchFields: ['foo'], type: ['dashboard'], diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 125f97e7feb11..a83c86e585628 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -67,7 +67,7 @@ import { } from '../../types'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { validateConvertFilterToKueryNode } from './filter_utils'; -import { SavedObjectsUtils } from './utils'; +import { FIND_DEFAULT_PAGE, FIND_DEFAULT_PER_PAGE, SavedObjectsUtils } from './utils'; // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. @@ -693,37 +693,51 @@ export class SavedObjectsRepository { * @property {string} [options.preference] * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ - async find({ - search, - defaultSearchOperator = 'OR', - searchFields, - rootSearchFields, - hasReference, - page = 1, - perPage = 20, - sortField, - sortOrder, - fields, - namespaces, - type, - filter, - preference, - }: SavedObjectsFindOptions): Promise> { - if (!type) { + async find(options: SavedObjectsFindOptions): Promise> { + const { + search, + defaultSearchOperator = 'OR', + searchFields, + rootSearchFields, + hasReference, + page = FIND_DEFAULT_PAGE, + perPage = FIND_DEFAULT_PER_PAGE, + sortField, + sortOrder, + fields, + namespaces, + type, + typeToNamespacesMap, + filter, + preference, + } = options; + + if (!type && !typeToNamespacesMap) { throw SavedObjectsErrorHelpers.createBadRequestError( 'options.type must be a string or an array of strings' ); + } else if (namespaces?.length === 0 && !typeToNamespacesMap) { + throw SavedObjectsErrorHelpers.createBadRequestError( + 'options.namespaces cannot be an empty array' + ); + } else if (type && typeToNamespacesMap) { + throw SavedObjectsErrorHelpers.createBadRequestError( + 'options.type must be an empty string when options.typeToNamespacesMap is used' + ); + } else if ((!namespaces || namespaces?.length) && typeToNamespacesMap) { + throw SavedObjectsErrorHelpers.createBadRequestError( + 'options.namespaces must be an empty array when options.typeToNamespacesMap is used' + ); } - const types = Array.isArray(type) ? type : [type]; + const types = type + ? Array.isArray(type) + ? type + : [type] + : Array.from(typeToNamespacesMap!.keys()); const allowedTypes = types.filter((t) => this._allowedTypes.includes(t)); if (allowedTypes.length === 0) { - return { - page, - per_page: perPage, - total: 0, - saved_objects: [], - }; + return SavedObjectsUtils.createEmptyFindResponse(options); } if (searchFields && !Array.isArray(searchFields)) { @@ -766,6 +780,7 @@ export class SavedObjectsRepository { sortField, sortOrder, namespaces, + typeToNamespacesMap, hasReference, kueryNode, }), diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts index 4adc92df31805..e13c67a720400 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts @@ -50,6 +50,40 @@ const ALL_TYPE_SUBSETS = ALL_TYPES.reduce( .filter((x) => x.length) // exclude empty set .map((x) => (x.length === 1 ? x[0] : x)); // if a subset is a single string, destructure it +const createTypeClause = (type: string, namespaces?: string[]) => { + if (registry.isMultiNamespace(type)) { + return { + bool: { + must: expect.arrayContaining([{ terms: { namespaces: namespaces ?? ['default'] } }]), + must_not: [{ exists: { field: 'namespace' } }], + }, + }; + } else if (registry.isSingleNamespace(type)) { + const nonDefaultNamespaces = namespaces?.filter((n) => n !== 'default') ?? []; + const should: any = []; + if (nonDefaultNamespaces.length > 0) { + should.push({ terms: { namespace: nonDefaultNamespaces } }); + } + if (namespaces?.includes('default')) { + should.push({ bool: { must_not: [{ exists: { field: 'namespace' } }] } }); + } + return { + bool: { + must: [{ term: { type } }], + should: expect.arrayContaining(should), + minimum_should_match: 1, + must_not: [{ exists: { field: 'namespaces' } }], + }, + }; + } + // isNamespaceAgnostic + return { + bool: expect.objectContaining({ + must_not: [{ exists: { field: 'namespace' } }, { exists: { field: 'namespaces' } }], + }), + }; +}; + /** * Note: these tests cases are defined in the order they appear in the source code, for readability's sake */ @@ -198,40 +232,6 @@ describe('#getQueryParams', () => { }); describe('`namespaces` parameter', () => { - const createTypeClause = (type: string, namespaces?: string[]) => { - if (registry.isMultiNamespace(type)) { - return { - bool: { - must: expect.arrayContaining([{ terms: { namespaces: namespaces ?? ['default'] } }]), - must_not: [{ exists: { field: 'namespace' } }], - }, - }; - } else if (registry.isSingleNamespace(type)) { - const nonDefaultNamespaces = namespaces?.filter((n) => n !== 'default') ?? []; - const should: any = []; - if (nonDefaultNamespaces.length > 0) { - should.push({ terms: { namespace: nonDefaultNamespaces } }); - } - if (namespaces?.includes('default')) { - should.push({ bool: { must_not: [{ exists: { field: 'namespace' } }] } }); - } - return { - bool: { - must: [{ term: { type } }], - should: expect.arrayContaining(should), - minimum_should_match: 1, - must_not: [{ exists: { field: 'namespaces' } }], - }, - }; - } - // isNamespaceAgnostic - return { - bool: expect.objectContaining({ - must_not: [{ exists: { field: 'namespace' } }, { exists: { field: 'namespaces' } }], - }), - }; - }; - const expectResult = (result: Result, ...typeClauses: any) => { expect(result.query.bool.filter).toEqual( expect.arrayContaining([ @@ -281,6 +281,37 @@ describe('#getQueryParams', () => { test(['default']); }); }); + + describe('`typeToNamespacesMap` parameter', () => { + const expectResult = (result: Result, ...typeClauses: any) => { + expect(result.query.bool.filter).toEqual( + expect.arrayContaining([ + { bool: expect.objectContaining({ should: typeClauses, minimum_should_match: 1 }) }, + ]) + ); + }; + + it('supersedes `type` and `namespaces` parameters', () => { + const result = getQueryParams({ + mappings, + registry, + type: ['pending', 'saved', 'shared', 'global'], + namespaces: ['foo', 'bar', 'default'], + typeToNamespacesMap: new Map([ + ['pending', ['foo']], // 'pending' is only authorized in the 'foo' namespace + // 'saved' is not authorized in any namespaces + ['shared', ['bar', 'default']], // 'shared' is only authorized in the 'bar' and 'default' namespaces + ['global', ['foo', 'bar', 'default']], // 'global' is authorized in all namespaces (which are ignored anyway) + ]), + }); + expectResult( + result, + createTypeClause('pending', ['foo']), + createTypeClause('shared', ['bar', 'default']), + createTypeClause('global') + ); + }); + }); }); describe('search clause (query.bool.must.simple_query_string)', () => { diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index 642d51c70766e..eaddc05fa921c 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -129,6 +129,7 @@ interface QueryParams { registry: ISavedObjectTypeRegistry; namespaces?: string[]; type?: string | string[]; + typeToNamespacesMap?: Map; search?: string; searchFields?: string[]; rootSearchFields?: string[]; @@ -145,6 +146,7 @@ export function getQueryParams({ registry, namespaces, type, + typeToNamespacesMap, search, searchFields, rootSearchFields, @@ -152,7 +154,10 @@ export function getQueryParams({ hasReference, kueryNode, }: QueryParams) { - const types = getTypes(mappings, type); + const types = getTypes( + mappings, + typeToNamespacesMap ? Array.from(typeToNamespacesMap.keys()) : type + ); // A de-duplicated set of namespaces makes for a more effecient query. // @@ -163,9 +168,12 @@ export function getQueryParams({ // since that is consistent with how a single-namespace search behaves in the OSS distribution. Leaving the wildcard in place // would result in no results being returned, as the wildcard is treated as a literal, and not _actually_ as a wildcard. // We had a good discussion around the tradeoffs here: https://github.com/elastic/kibana/pull/67644#discussion_r441055716 - const normalizedNamespaces = namespaces - ? Array.from(new Set(namespaces.map((x) => (x === '*' ? DEFAULT_NAMESPACE_STRING : x)))) - : undefined; + const normalizeNamespaces = (namespacesToNormalize?: string[]) => + namespacesToNormalize + ? Array.from( + new Set(namespacesToNormalize.map((x) => (x === '*' ? DEFAULT_NAMESPACE_STRING : x))) + ) + : undefined; const bool: any = { filter: [ @@ -197,9 +205,12 @@ export function getQueryParams({ }, ] : undefined, - should: types.map((shouldType) => - getClauseForType(registry, normalizedNamespaces, shouldType) - ), + should: types.map((shouldType) => { + const normalizedNamespaces = normalizeNamespaces( + typeToNamespacesMap ? typeToNamespacesMap.get(shouldType) : namespaces + ); + return getClauseForType(registry, normalizedNamespaces, shouldType); + }), minimum_should_match: 1, }, }, diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts index 62e629ad33cc8..7276e505bce7d 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.test.ts @@ -57,10 +57,11 @@ describe('getSearchDsl', () => { }); describe('passes control', () => { - it('passes (mappings, schema, namespaces, type, search, searchFields, rootSearchFields, hasReference) to getQueryParams', () => { + it('passes (mappings, schema, namespaces, type, typeToNamespacesMap, search, searchFields, rootSearchFields, hasReference) to getQueryParams', () => { const opts = { namespaces: ['foo-namespace'], type: 'foo', + typeToNamespacesMap: new Map(), search: 'bar', searchFields: ['baz'], rootSearchFields: ['qux'], @@ -78,6 +79,7 @@ describe('getSearchDsl', () => { registry, namespaces: opts.namespaces, type: opts.type, + typeToNamespacesMap: opts.typeToNamespacesMap, search: opts.search, searchFields: opts.searchFields, rootSearchFields: opts.rootSearchFields, diff --git a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts index aa79a10b2a9be..858770579fb9e 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/search_dsl.ts @@ -35,6 +35,7 @@ interface GetSearchDslOptions { sortField?: string; sortOrder?: string; namespaces?: string[]; + typeToNamespacesMap?: Map; hasReference?: { type: string; id: string; @@ -56,6 +57,7 @@ export function getSearchDsl( sortField, sortOrder, namespaces, + typeToNamespacesMap, hasReference, kueryNode, } = options; @@ -74,6 +76,7 @@ export function getSearchDsl( registry, namespaces, type, + typeToNamespacesMap, search, searchFields, rootSearchFields, diff --git a/src/core/server/saved_objects/service/lib/utils.test.ts b/src/core/server/saved_objects/service/lib/utils.test.ts index ea4fa68242bea..ac06ca9275783 100644 --- a/src/core/server/saved_objects/service/lib/utils.test.ts +++ b/src/core/server/saved_objects/service/lib/utils.test.ts @@ -17,10 +17,11 @@ * under the License. */ +import { SavedObjectsFindOptions } from '../../types'; import { SavedObjectsUtils } from './utils'; describe('SavedObjectsUtils', () => { - const { namespaceIdToString, namespaceStringToId } = SavedObjectsUtils; + const { namespaceIdToString, namespaceStringToId, createEmptyFindResponse } = SavedObjectsUtils; describe('#namespaceIdToString', () => { it('converts `undefined` to default namespace string', () => { @@ -54,4 +55,26 @@ describe('SavedObjectsUtils', () => { test(''); }); }); + + describe('#createEmptyFindResponse', () => { + it('returns expected result', () => { + const options = {} as SavedObjectsFindOptions; + expect(createEmptyFindResponse(options)).toEqual({ + page: 1, + per_page: 20, + total: 0, + saved_objects: [], + }); + }); + + it('handles `page` field', () => { + const options = { page: 42 } as SavedObjectsFindOptions; + expect(createEmptyFindResponse(options).page).toEqual(42); + }); + + it('handles `perPage` field', () => { + const options = { perPage: 42 } as SavedObjectsFindOptions; + expect(createEmptyFindResponse(options).per_page).toEqual(42); + }); + }); }); diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/src/core/server/saved_objects/service/lib/utils.ts index 6101ad57cc401..3efe8614da1d7 100644 --- a/src/core/server/saved_objects/service/lib/utils.ts +++ b/src/core/server/saved_objects/service/lib/utils.ts @@ -17,7 +17,12 @@ * under the License. */ +import { SavedObjectsFindOptions } from '../../types'; +import { SavedObjectsFindResponse } from '..'; + export const DEFAULT_NAMESPACE_STRING = 'default'; +export const FIND_DEFAULT_PAGE = 1; +export const FIND_DEFAULT_PER_PAGE = 20; /** * @public @@ -50,4 +55,17 @@ export class SavedObjectsUtils { return namespace !== DEFAULT_NAMESPACE_STRING ? namespace : undefined; }; + + /** + * Creates an empty response for a find operation. This is only intended to be used by saved objects client wrappers. + */ + public static createEmptyFindResponse = ({ + page = FIND_DEFAULT_PAGE, + perPage = FIND_DEFAULT_PER_PAGE, + }: SavedObjectsFindOptions): SavedObjectsFindResponse => ({ + page, + per_page: perPage, + total: 0, + saved_objects: [], + }); } diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 1885f5ec50139..01128e4f8cf51 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -89,6 +89,14 @@ export interface SavedObjectsFindOptions { defaultSearchOperator?: 'AND' | 'OR'; filter?: string | KueryNode; namespaces?: string[]; + /** + * This map defines each type to search for, and the namespace(s) to search for the type in; this is only intended to be used by a saved + * object client wrapper. + * If this is defined, it supersedes the `type` and `namespaces` fields when building the Elasticsearch query. + * Any types that are not included in this map will be excluded entirely. + * If a type is included but its value is undefined, the operation will search for that type in the Default namespace. + */ + typeToNamespacesMap?: Map; /** An optional ES preference value to be used for the query **/ preference?: string; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index d755ef3e1b676..450be3b0e9a6c 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -348,7 +348,6 @@ export const config: { sniffInterval: Type; sniffOnConnectionFault: Type; hosts: Type; - preserveHost: Type; username: Type; password: Type; requestHeadersWhitelist: Type; @@ -356,7 +355,6 @@ export const config: { shardTimeout: Type; requestTimeout: Type; pingTimeout: Type; - startupTimeout: Type; logQueries: Type; ssl: import("@kbn/config-schema").ObjectType<{ verificationMode: Type<"none" | "certificate" | "full">; @@ -864,10 +862,6 @@ export interface IndexSettingsDeprecationInfo { // @public (undocumented) export interface IRenderOptions { - // @internal @deprecated - app?: { - getId(): string; - }; includeUserSettings?: boolean; // @internal @deprecated vars?: Record; @@ -1286,21 +1280,6 @@ export class LegacyElasticsearchErrorHelpers { static isNotAuthorizedError(error: any): error is LegacyElasticsearchError; } -// Warning: (ae-forgotten-export) The symbol "ILegacyInternals" needs to be exported by the entry point index.d.ts -// -// @internal @deprecated (undocumented) -export class LegacyInternals implements ILegacyInternals { - constructor(uiExports: LegacyUiExports, config: LegacyConfig, server: Server); - // (undocumented) - getInjectedUiAppVars(id: string): Promise>; - // (undocumented) - getVars(id: string, request: KibanaRequest | LegacyRequest, injected?: LegacyVars): Promise>; - // Warning: (ae-forgotten-export) The symbol "VarsInjector" needs to be exported by the entry point index.d.ts - // - // (undocumented) - injectUiAppVars(id: string, injector: VarsInjector): void; - } - // @public @deprecated (undocumented) export interface LegacyRequest extends Request { } @@ -1312,16 +1291,6 @@ export class LegacyScopedClusterClient implements ILegacyScopedClusterClient { callAsInternalUser(endpoint: string, clientParams?: Record, options?: LegacyCallAPIOptions): Promise; } -// Warning: (ae-forgotten-export) The symbol "LegacyPlugins" needs to be exported by the entry point index.d.ts -// -// @internal @deprecated (undocumented) -export interface LegacyServiceDiscoverPlugins extends LegacyPlugins { - // (undocumented) - pluginExtendedConfig: LegacyConfig; - // (undocumented) - settings: LegacyVars; -} - // @public @deprecated (undocumented) export interface LegacyServiceSetupDeps { // Warning: (ae-forgotten-export) The symbol "LegacyCoreSetup" needs to be exported by the entry point index.d.ts @@ -1346,31 +1315,6 @@ export interface LegacyServiceStartDeps { plugins: Record; } -// @internal @deprecated (undocumented) -export interface LegacyUiExports { - // Warning: (ae-forgotten-export) The symbol "VarsProvider" needs to be exported by the entry point index.d.ts - // - // (undocumented) - defaultInjectedVarProviders?: VarsProvider[]; - // Warning: (ae-forgotten-export) The symbol "VarsReplacer" needs to be exported by the entry point index.d.ts - // - // (undocumented) - injectedVarsReplacers?: VarsReplacer[]; - // Warning: (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts - // - // (undocumented) - navLinkSpecs?: LegacyNavLinkSpec[] | null; - // Warning: (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts - // - // (undocumented) - uiAppSpecs?: Array; - // (undocumented) - unknown?: [{ - pluginSpec: LegacyPluginSpec; - type: unknown; - }]; -} - // Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts // // @public @@ -2177,6 +2121,7 @@ export interface SavedObjectsFindOptions { sortOrder?: string; // (undocumented) type: string | string[]; + typeToNamespacesMap?: Map; } // @public @@ -2388,7 +2333,7 @@ export class SavedObjectsRepository { deleteByNamespace(namespace: string, options?: SavedObjectsDeleteByNamespaceOptions): Promise; deleteFromNamespaces(type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions): Promise; // (undocumented) - find({ search, defaultSearchOperator, searchFields, rootSearchFields, hasReference, page, perPage, sortField, sortOrder, fields, namespaces, type, filter, preference, }: SavedObjectsFindOptions): Promise>; + find(options: SavedObjectsFindOptions): Promise>; get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; incrementCounter(type: string, id: string, counterFieldName: string, options?: SavedObjectsIncrementCounterOptions): Promise; update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; @@ -2496,6 +2441,7 @@ export interface SavedObjectsUpdateResponse extends Omit({ page, perPage, }: SavedObjectsFindOptions) => SavedObjectsFindResponse; static namespaceIdToString: (namespace?: string | undefined) => string; static namespaceStringToId: (namespace: string) => string | undefined; } @@ -2732,7 +2678,6 @@ export const validBodyOutput: readonly ["data", "stream"]; // Warnings were encountered during analysis: // // src/core/server/http/router/response.ts:316:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts -// src/core/server/legacy/types.ts:135:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:274:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts // src/core/server/plugins/types.ts:277:3 - (ae-forgotten-export) The symbol "SavedObjectsConfigType" needs to be exported by the entry point index.d.ts diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 8502f563cb0c2..5935636d54f9d 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -113,11 +113,12 @@ export class Server { const { pluginTree, uiPlugins } = await this.plugins.discover({ environment: environmentSetup, }); - const legacyPlugins = await this.legacy.discoverPlugins(); + const legacyConfigSetup = await this.legacy.setupLegacyConfig(); // Immediately terminate in case of invalid configuration + // This needs to be done after plugin discovery await this.configService.validate(); - await ensureValidConfiguration(this.configService, legacyPlugins); + await ensureValidConfiguration(this.configService, legacyConfigSetup); const contextServiceSetup = this.context.setup({ // We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins: @@ -166,7 +167,6 @@ export class Server { const renderingSetup = await this.rendering.setup({ http: httpSetup, status: statusSetup, - legacyPlugins, uiPlugins, }); @@ -248,10 +248,6 @@ export class Server { await this.http.start(); - await this.rendering.start({ - legacy: this.legacy, - }); - return this.coreStart; } diff --git a/src/core/server/status/plugins_status.test.ts b/src/core/server/status/plugins_status.test.ts index a75dc8c283698..176e2414a8d04 100644 --- a/src/core/server/status/plugins_status.test.ts +++ b/src/core/server/status/plugins_status.test.ts @@ -161,13 +161,13 @@ describe('PluginStatusService', () => { }, b: { level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '[savedObjects]: savedObjects degraded', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.degraded, - summary: '[3] services are degraded', + summary: '[savedObjects]: savedObjects degraded', detail: 'See the status page for more information', meta: expect.any(Object), }, @@ -186,13 +186,13 @@ describe('PluginStatusService', () => { }, b: { level: ServiceStatusLevels.critical, - summary: '[2] services are critical', + summary: '[elasticsearch]: elasticsearch critical', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.critical, - summary: '[3] services are critical', + summary: '[elasticsearch]: elasticsearch critical', detail: 'See the status page for more information', meta: expect.any(Object), }, diff --git a/src/core/server/status/plugins_status.ts b/src/core/server/status/plugins_status.ts index 113d59b327c11..988f2d9969ccb 100644 --- a/src/core/server/status/plugins_status.ts +++ b/src/core/server/status/plugins_status.ts @@ -33,7 +33,17 @@ interface Deps { export class PluginsStatusService { private readonly pluginStatuses = new Map>(); private readonly update$ = new BehaviorSubject(true); - constructor(private readonly deps: Deps) {} + private readonly defaultInheritedStatus$: Observable; + + constructor(private readonly deps: Deps) { + this.defaultInheritedStatus$ = this.deps.core$.pipe( + map((coreStatus) => { + return getSummaryStatus(Object.entries(coreStatus), { + allAvailableSummary: `All dependencies are available`, + }); + }) + ); + } public set(plugin: PluginName, status$: Observable) { this.pluginStatuses.set(plugin, status$); @@ -57,14 +67,24 @@ export class PluginsStatusService { } public getDerivedStatus$(plugin: PluginName): Observable { - return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( - map(([coreStatus, pluginStatuses]) => { - return getSummaryStatus( - [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], - { - allAvailableSummary: `All dependencies are available`, - } - ); + return this.update$.pipe( + switchMap(() => { + // Only go up the dependency tree if any of this plugin's dependencies have a custom status + // Helps eliminate memory overhead of creating thousands of Observables unnecessarily. + if (this.anyCustomStatuses(plugin)) { + return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( + map(([coreStatus, pluginStatuses]) => { + return getSummaryStatus( + [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], + { + allAvailableSummary: `All dependencies are available`, + } + ); + }) + ); + } else { + return this.defaultInheritedStatus$; + } }) ); } @@ -95,4 +115,17 @@ export class PluginsStatusService { }) ); } + + /** + * Determines whether or not this plugin or any plugin in it's dependency tree have a custom status registered. + */ + private anyCustomStatuses(plugin: PluginName): boolean { + if (this.pluginStatuses.get(plugin)) { + return true; + } + + return this.deps.pluginDependencies + .get(plugin)! + .reduce((acc, depName) => acc || this.anyCustomStatuses(depName), false as boolean); + } } diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 9acf93f2f8197..62f226405e81a 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -70,10 +70,10 @@ export class StatusService implements CoreService { const core$ = this.setupCoreStatus({ elasticsearch, savedObjects }); this.pluginsStatus = new PluginsStatusService({ core$, pluginDependencies }); - const overall$: Observable = combineLatest( + const overall$: Observable = combineLatest([ core$, - this.pluginsStatus.getAll$() - ).pipe( + this.pluginsStatus.getAll$(), + ]).pipe( // Prevent many emissions at once from dependency status resolution from making this too noisy debounceTime(500), map(([coreStatus, pluginsStatus]) => { diff --git a/src/core/typings.ts b/src/core/typings.ts index a84e1c01d2bd2..f271d0b03e0d3 100644 --- a/src/core/typings.ts +++ b/src/core/typings.ts @@ -17,34 +17,6 @@ * under the License. */ -declare module 'query-string' { - type ArrayFormat = 'bracket' | 'index' | 'none'; - - export interface ParseOptions { - arrayFormat?: ArrayFormat; - sort: ((itemLeft: string, itemRight: string) => number) | false; - } - - export interface ParsedQuery { - [key: string]: T | T[] | null | undefined; - } - - export function parse(str: string, options?: ParseOptions): ParsedQuery; - - export function parseUrl(str: string, options?: ParseOptions): { url: string; query: any }; - - export interface StringifyOptions { - strict?: boolean; - encode?: boolean; - arrayFormat?: ArrayFormat; - sort: ((itemLeft: string, itemRight: string) => number) | false; - } - - export function stringify(obj: object, options?: StringifyOptions): string; - - export function extract(str: string): string; -} - type DeeplyMockedKeys = { [P in keyof T]: T[P] extends (...args: any[]) => any ? jest.MockInstance, Parameters> diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index b02b7cc16ec4a..2770f288b6af8 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -8,11 +8,11 @@ # # eg. Setting the environment variable: # -# ELASTICSEARCH_STARTUPTIMEOUT=60 +# ELASTICSEARCH_LOGQUERIES=true # # will cause Kibana to be invoked with: # -# --elasticsearch.startupTimeout=60 +# --elasticsearch.logQueries=true kibana_vars=( console.enabled @@ -30,7 +30,6 @@ kibana_vars=( elasticsearch.logQueries elasticsearch.password elasticsearch.pingTimeout - elasticsearch.preserveHost elasticsearch.requestHeadersWhitelist elasticsearch.requestTimeout elasticsearch.shardTimeout @@ -47,7 +46,6 @@ kibana_vars=( elasticsearch.ssl.truststore.path elasticsearch.ssl.truststore.password elasticsearch.ssl.verificationMode - elasticsearch.startupTimeout elasticsearch.username i18n.locale interpreter.enableInVisualize @@ -235,6 +233,7 @@ kibana_vars=( xpack.security.cookieName xpack.security.enabled xpack.security.encryptionKey + xpack.security.sameSiteCookies xpack.security.secureCookies xpack.security.sessionTimeout xpack.security.session.idleTimeout diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/either.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/either.test.js index 3a493539f6743..0ae55508e8434 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/either.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/either.test.js @@ -53,7 +53,7 @@ describe(`either datatype functions`, () => { expect(sut.inspect()).to.be('Right(undefined)'); }); }); - describe(`'fromNullable`, () => { + describe(`fromNullable`, () => { it(`should continue processing if a truthy is calculated`, () => { attempt({ detail: 'x' }).fold( () => {}, @@ -64,4 +64,18 @@ describe(`either datatype functions`, () => { attempt(false).fold(expectNull, () => {}); }); }); + describe(`predicate fns`, () => { + it(`right.isRight() is true`, () => { + expect(Either.right('a').isRight()).to.be(true); + }); + it(`right.isLeft() is false`, () => { + expect(Either.right('a').isLeft()).to.be(false); + }); + it(`left.isLeft() is true`, () => { + expect(Either.left().isLeft()).to.be(true); + }); + it(`left.isRight() is true`, () => { + expect(Either.left().isRight()).to.be(false); + }); + }); }); diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js new file mode 100644 index 0000000000000..371695337ed56 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js @@ -0,0 +1,60 @@ +/* + * 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 expect from '@kbn/expect'; +import { enumeratePatterns } from '../team_assignment/enumerate_patterns'; +import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; + +const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout, +}); + +describe(`enumeratePatterns`, () => { + it(`should resolve x-pack/plugins/reporting/server/browsers/extract/unzip.js to kibana-reporting`, () => { + const actual = enumeratePatterns(REPO_ROOT)(log)( + new Map([['x-pack/plugins/reporting', ['kibana-reporting']]]) + ); + + expect( + actual[0].includes( + 'x-pack/plugins/reporting/server/browsers/extract/unzip.js kibana-reporting' + ) + ).to.be(true); + }); + it(`should resolve src/plugins/charts/public/static/color_maps/color_maps.ts to kibana-app`, () => { + const actual = enumeratePatterns(REPO_ROOT)(log)( + new Map([['src/plugins/charts/public/static/color_maps', ['kibana-app']]]) + ); + + expect(actual[0][0]).to.be( + 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app' + ); + }); + it(`should resolve x-pack/plugins/security_solution/public/common/components/exceptions/builder/translations.ts to kibana-security`, () => { + const short = 'x-pack/plugins/security_solution'; + const actual = enumeratePatterns(REPO_ROOT)(log)(new Map([[short, ['kibana-security']]])); + + expect( + actual[0].includes( + `${short}/public/common/components/exceptions/builder/translations.ts kibana-security` + ) + ).to.be(true); + }); +}); diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/enumeration_helpers.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/enumeration_helpers.test.js new file mode 100644 index 0000000000000..f480135b45ac6 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/__tests__/enumeration_helpers.test.js @@ -0,0 +1,50 @@ +/* + * 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 expect from '@kbn/expect'; +import { tryPath } from '../team_assignment/enumeration_helpers'; + +describe(`enumeration helper fns`, () => { + describe(`tryPath`, () => { + describe(`w/o glob file paths`, () => { + it(`should return a right on an existing path`, () => { + const aPath = 'src/dev/code_coverage/ingest_coverage/ingest.js'; + const actual = tryPath(aPath); + expect(actual.isRight()).to.be(true); + }); + it(`should return a left on a non existing path`, () => { + const aPath = 'src/dev/code_coverage/ingest_coverage/does_not_exist.js'; + const actual = tryPath(aPath); + expect(actual.isLeft()).to.be(true); + }); + }); + describe(`with glob file paths`, () => { + it(`should not error when the glob expands to nothing, but instead return a Left`, () => { + const aPath = 'src/legacy/core_plugins/kibana/public/home/*.ts'; + const actual = tryPath(aPath); + expect(actual.isLeft()).to.be(true); + }); + it(`should return a right on a glob that does indeed expand`, () => { + const aPath = 'src/dev/code_coverage/ingest_coverage/*.js'; + const actual = tryPath(aPath); + expect(actual.isRight()).to.be(true); + }); + }); + }); +}); diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/ingest_helpers.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/ingest_helpers.test.js index 7ca7279e0d64c..f668c1f86f5b0 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/ingest_helpers.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/ingest_helpers.test.js @@ -18,13 +18,8 @@ */ import expect from '@kbn/expect'; -import { maybeTeamAssign, whichIndex } from '../ingest_helpers'; -import { - TOTALS_INDEX, - RESEARCH_TOTALS_INDEX, - RESEARCH_COVERAGE_INDEX, - // COVERAGE_INDEX, -} from '../constants'; +import { whichIndex } from '../ingest_helpers'; +import { TOTALS_INDEX, RESEARCH_TOTALS_INDEX, RESEARCH_COVERAGE_INDEX } from '../constants'; describe(`Ingest Helper fns`, () => { describe(`whichIndex`, () => { @@ -56,20 +51,4 @@ describe(`Ingest Helper fns`, () => { }); }); }); - describe(`maybeTeamAssign`, () => { - describe(`against a coverage index`, () => { - it(`should have the pipeline prop`, () => { - const actual = maybeTeamAssign(true, { a: 'blah' }); - expect(actual).to.have.property('pipeline'); - }); - }); - describe(`against a totals index`, () => { - describe(`for "prod"`, () => { - it(`should not have the pipeline prop`, () => { - const actual = maybeTeamAssign(false, { b: 'blah' }); - expect(actual).not.to.have.property('pipeline'); - }); - }); - }); - }); }); diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.json b/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.json deleted file mode 100644 index 355c484a84fa3..0000000000000 --- a/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "abc": "123" -} diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.txt b/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.txt new file mode 100644 index 0000000000000..d8924bd563f30 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.txt @@ -0,0 +1,194 @@ +x-pack/plugins/dashboard_enhanced/public/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/mocks.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/plugin.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/drilldown_shared.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.test.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/flyout_create_drilldown.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_create_drilldown/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.test.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/flyout_edit_drilldown.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/i18n.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/index.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.test.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/test_helpers.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_drilldowns_services.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/collect_config_container.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.story.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.test.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/i18n.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/i18n.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/constants.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.test.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/drilldown.tsx kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/i18n.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/types.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/drilldowns/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/public/services/index.ts kibana-app +x-pack/plugins/dashboard_enhanced/scripts/storybook.js kibana-app +x-pack/plugins/discover_enhanced/common/config.ts kibana-app +x-pack/plugins/discover_enhanced/common/index.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/abstract_explore_data_action.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.test.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.test.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/index.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/kibana_url.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/explore_data/shared.ts kibana-app +x-pack/plugins/discover_enhanced/public/actions/index.ts kibana-app +x-pack/plugins/discover_enhanced/public/index.ts kibana-app +x-pack/plugins/discover_enhanced/public/plugin.ts kibana-app +x-pack/plugins/discover_enhanced/server/config.ts kibana-app +x-pack/plugins/discover_enhanced/server/index.ts kibana-app +x-pack/plugins/discover_enhanced/server/plugin.ts kibana-app +x-pack/plugins/lens/common/api.ts kibana-app +x-pack/plugins/lens/common/constants.ts kibana-app +x-pack/plugins/lens/common/index.ts kibana-app +x-pack/plugins/lens/common/types.ts kibana-app +x-pack/plugins/lens/config.ts kibana-app +x-pack/plugins/lens/public/app_plugin/app.test.tsx kibana-app +x-pack/plugins/lens/public/app_plugin/app.tsx kibana-app +x-pack/plugins/lens/public/app_plugin/index.ts kibana-app +x-pack/plugins/lens/public/app_plugin/mounter.tsx kibana-app +x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx kibana-app +x-pack/plugins/lens/public/datatable_visualization/expression.tsx kibana-app +x-pack/plugins/lens/public/datatable_visualization/index.ts kibana-app +x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx kibana-app +x-pack/plugins/lens/public/datatable_visualization/visualization.tsx kibana-app +x-pack/plugins/lens/public/debounced_component/debounced_component.test.tsx kibana-app +x-pack/plugins/lens/public/debounced_component/debounced_component.tsx kibana-app +x-pack/plugins/lens/public/debounced_component/index.ts kibana-app +x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx kibana-app +x-pack/plugins/lens/public/drag_drop/drag_drop.tsx kibana-app +x-pack/plugins/lens/public/drag_drop/index.ts kibana-app +x-pack/plugins/lens/public/drag_drop/providers.test.tsx kibana-app +x-pack/plugins/lens/public/drag_drop/providers.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/__mocks__/expression_helpers.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/__mocks__/suggestion_helpers.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/index.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.test.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_actions.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_settings.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.test.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.test.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/chart_switch.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/index.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/format_column.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/index.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts kibana-app +x-pack/plugins/lens/public/editor_frame_service/mocks.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/service.test.tsx kibana-app +x-pack/plugins/lens/public/editor_frame_service/service.tsx kibana-app +x-pack/plugins/lens/public/help_menu_util.tsx kibana-app +x-pack/plugins/lens/public/id_generator/id_generator.test.ts kibana-app +x-pack/plugins/lens/public/id_generator/id_generator.ts kibana-app +x-pack/plugins/lens/public/id_generator/index.ts kibana-app +x-pack/plugins/lens/public/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/__mocks__/loader.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/__mocks__/state_helpers.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/datapanel.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/format_selector.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/document_field.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/field_item.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.test.tsx kibana-app +x-pack/plugins/reporting/server/browsers/download/clean.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/download/download.test.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/download/download.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/download/index.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/download/util.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/extract/extract.js kibana-reporting +x-pack/plugins/reporting/server/browsers/extract/extract_error.js kibana-reporting +x-pack/plugins/reporting/server/browsers/extract/index.js kibana-reporting +x-pack/plugins/reporting/server/browsers/extract/unzip.js kibana-reporting +x-pack/plugins/reporting/server/browsers/index.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/install.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/network_policy.test.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/network_policy.ts kibana-reporting +x-pack/plugins/reporting/server/browsers/safe_child_process.ts kibana-reporting +x-pack/plugins/reporting/server/config/config.ts kibana-reporting +x-pack/plugins/reporting/server/config/create_config.test.ts kibana-reporting +x-pack/plugins/reporting/server/config/create_config.ts kibana-reporting +x-pack/plugins/reporting/server/config/default_chromium_sandbox_disabled.test.ts kibana-reporting +x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/lens_field_icon.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/lens_field_icon.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/loader.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.test.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/pure_helpers.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/pure_helpers.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.test.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/types.ts kibana-app +x-pack/plugins/lens/public/indexpattern_datasource/utils.ts kibana-app +x-pack/plugins/lens/public/lens_ui_telemetry/factory.test.ts kibana-app diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/team_assignment.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/team_assignment.test.js deleted file mode 100644 index e597ffb5d2f4b..0000000000000 --- a/src/dev/code_coverage/ingest_coverage/__tests__/team_assignment.test.js +++ /dev/null @@ -1,45 +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 expect from '@kbn/expect'; -import { fetch } from '../team_assignment/get_data'; -import { noop } from '../utils'; - -describe(`Team Assignment`, () => { - const mockPath = 'src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.json'; - describe(`fetch fn`, () => { - it(`should be a fn`, () => { - expect(typeof fetch).to.be('function'); - }); - describe(`applied to a path that exists`, () => { - it(`should return the contents of the path`, () => { - const sut = fetch(mockPath); - expect(sut.chain(JSON.parse)).to.have.property('abc'); - }); - }); - describe(`applied to an non-existing path`, () => { - it(`should return a Left with the error message within`, () => { - const expectLeft = (err) => - expect(err.message).to.contain('ENOENT: no such file or directory'); - - fetch('fake_path.json').fold(expectLeft, noop); - }); - }); - }); -}); diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js index 746bccc3d718a..b6d17f83e327e 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js @@ -18,9 +18,17 @@ */ import expect from '@kbn/expect'; -import { ciRunUrl, coveredFilePath, itemizeVcs, prokPrevious } from '../transforms'; +import { + ciRunUrl, + coveredFilePath, + itemizeVcs, + prokPrevious, + teamAssignment, + last, +} from '../transforms'; +import { ToolingLog } from '@kbn/dev-utils'; -describe(`Transform fn`, () => { +describe(`Transform fns`, () => { describe(`ciRunUrl`, () => { it(`should add the url when present in the environment`, () => { process.env.CI_RUN_URL = 'blah'; @@ -83,4 +91,59 @@ describe(`Transform fn`, () => { ); }); }); + describe(`teamAssignment`, () => { + const teamAssignmentsPathMOCK = + 'src/dev/code_coverage/ingest_coverage/__tests__/mocks/team_assign_mock.txt'; + const coveredFilePath = 'x-pack/plugins/reporting/server/browsers/extract/unzip.js'; + const obj = { coveredFilePath }; + const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout, + }); + + describe(`with a coveredFilePath of ${coveredFilePath}`, () => { + const expected = 'kibana-reporting'; + it(`should resolve to ${expected}`, async () => { + const actual = await teamAssignment(teamAssignmentsPathMOCK)(log)(obj); + const { team } = actual; + expect(team).to.eql(expected); + }); + }); + + describe(`with a coveredFilePath of src/plugins/charts/public/static/color_maps/color_maps.ts`, () => { + const expected = 'kibana-reporting'; + it(`should resolve to ${expected}`, async () => { + const actual = await teamAssignment(teamAssignmentsPathMOCK)(log)(obj); + const { team } = actual; + expect(team).to.eql(expected); + }); + }); + + describe(`last fn`, () => { + describe(`applied to n results`, () => { + it(`should pick the last one`, () => { + const nteams = `src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app +src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch`; + + const actual = last(nteams); + + expect(actual).to.be( + 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch' + ); + }); + }); + describe(`applied to 1 result`, () => { + it(`should pick that 1 result`, () => { + const nteams = + 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch'; + + const actual = last(nteams); + + expect(actual).to.be( + 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch' + ); + }); + }); + }); + }); }); diff --git a/src/dev/code_coverage/ingest_coverage/constants.js b/src/dev/code_coverage/ingest_coverage/constants.js index f2f467e461ae5..8f850ac2f1f12 100644 --- a/src/dev/code_coverage/ingest_coverage/constants.js +++ b/src/dev/code_coverage/ingest_coverage/constants.js @@ -27,8 +27,6 @@ export const RESEARCH_COVERAGE_INDEX = export const RESEARCH_TOTALS_INDEX = process.env.RESEARCH_TOTALS_INDEX || `qa_research_total_code_coverage`; -export const TEAM_ASSIGNMENT_PIPELINE_NAME = process.env.PIPELINE_NAME || 'team_assignment'; - export const CODE_COVERAGE_CI_JOB_NAME = 'elastic+kibana+code-coverage'; export const RESEARCH_CI_JOB_NAME = 'elastic+kibana+qa-research'; export const CI_JOB_NAME = process.env.COVERAGE_JOB_NAME || RESEARCH_CI_JOB_NAME; diff --git a/src/dev/code_coverage/ingest_coverage/either.js b/src/dev/code_coverage/ingest_coverage/either.js index eeb48893f18d8..326f238074e30 100644 --- a/src/dev/code_coverage/ingest_coverage/either.js +++ b/src/dev/code_coverage/ingest_coverage/either.js @@ -20,11 +20,15 @@ /* eslint new-cap: 0 */ /* eslint no-unused-vars: 0 */ +import { always } from './utils'; + export const Right = (x) => ({ chain: (f) => f(x), map: (f) => Right(f(x)), fold: (leftFn, rightFn) => rightFn(x), inspect: () => `Right(${x})`, + isLeft: always(false), + isRight: always(true), }); Right.of = function of(x) { @@ -40,6 +44,8 @@ export const Left = (x) => ({ map: (f) => Left(x), fold: (leftFn, rightFn) => leftFn(x), inspect: () => `Left(${x})`, + isLeft: always(true), + isRight: always(false), }); Left.of = function of(x) { diff --git a/src/dev/code_coverage/ingest_coverage/index.js b/src/dev/code_coverage/ingest_coverage/index.js index 4047ee78ee6ec..f29739c4cf29c 100644 --- a/src/dev/code_coverage/ingest_coverage/index.js +++ b/src/dev/code_coverage/ingest_coverage/index.js @@ -20,13 +20,16 @@ import { resolve } from 'path'; import { prok } from './process'; import { run, createFlagError } from '@kbn/dev-utils'; +import { pathExists } from './team_assignment/enumeration_helpers'; +import { id, reThrow } from './utils'; const ROOT = resolve(__dirname, '../../../..'); const flags = { - string: ['path', 'verbose', 'vcsInfoPath'], + string: ['path', 'verbose', 'vcsInfoPath', 'teamAssignmentsPath'], help: ` --path Required, path to the file to extract coverage data --vcsInfoPath Required, path to the git info file (branch, sha, author, & commit msg) +--teamAssignmentsPath Required, path to the team assignments data file `, }; @@ -36,12 +39,18 @@ export function runCoverageIngestionCli() { if (flags.path === '') throw createFlagError('please provide a single --path flag'); if (flags.vcsInfoPath === '') throw createFlagError('please provide a single --vcsInfoPath flag'); + if (flags.teamAssignmentsPath === '') + throw createFlagError('please provide a single --teamAssignments flag'); if (flags.verbose) log.verbose(`Verbose logging enabled`); const resolveRoot = resolve.bind(null, ROOT); const jsonSummaryPath = resolveRoot(flags.path); const vcsInfoFilePath = resolveRoot(flags.vcsInfoPath); - prok({ jsonSummaryPath, vcsInfoFilePath }, log); + const { teamAssignmentsPath } = flags; + + pathExists(teamAssignmentsPath).fold(reThrow, id); + + prok({ jsonSummaryPath, vcsInfoFilePath, teamAssignmentsPath }, log); }, { description: ` diff --git a/src/dev/code_coverage/ingest_coverage/ingest.js b/src/dev/code_coverage/ingest_coverage/ingest.js index 31a94d161a3cc..24da7a9338a46 100644 --- a/src/dev/code_coverage/ingest_coverage/ingest.js +++ b/src/dev/code_coverage/ingest_coverage/ingest.js @@ -19,7 +19,7 @@ const { Client } = require('@elastic/elasticsearch'); import { createFailError } from '@kbn/dev-utils'; -import { RESEARCH_CI_JOB_NAME, TEAM_ASSIGNMENT_PIPELINE_NAME } from './constants'; +import { RESEARCH_CI_JOB_NAME } from './constants'; import { errMsg, redact, whichIndex } from './ingest_helpers'; import { pretty, green } from './utils'; import { right, left } from './either'; @@ -34,14 +34,10 @@ const isResearchJob = process.env.COVERAGE_JOB_NAME === RESEARCH_CI_JOB_NAME ? t export const ingest = (log) => async (body) => { const isTotal = !!body.isTotal; const index = whichIndex(isResearchJob)(isTotal); - const isACoverageIndex = isTotal ? false : true; const stringified = pretty(body); - const pipeline = TEAM_ASSIGNMENT_PIPELINE_NAME; - const finalPayload = isACoverageIndex - ? { index, body: stringified, pipeline } - : { index, body: stringified }; + const finalPayload = { index, body: stringified }; const justLog = dontSendButLog(log); const doSendToIndex = doSend(index); @@ -77,11 +73,11 @@ async function send(logF, idx, redactedEsHostUrl, client, requestBody) { const sendMsg = (actuallySent, redactedEsHostUrl, payload) => { const { index, body } = payload; return `### ${actuallySent ? 'Sent' : 'Fake Sent'}: -${payload.pipeline ? `\t### Team Assignment Pipeline: ${green(payload.pipeline)}` : ''} ${redactedEsHostUrl ? `\t### ES Host: ${redactedEsHostUrl}` : ''} + \t### Index: ${green(index)} + \t### payload.body: ${body} -${process.env.NODE_ENV === 'integration_test' ? `ingest-pipe=>${payload.pipeline}` : ''} `; }; diff --git a/src/dev/code_coverage/ingest_coverage/ingest_helpers.js b/src/dev/code_coverage/ingest_coverage/ingest_helpers.js index 86bcf03977082..a7822a671887b 100644 --- a/src/dev/code_coverage/ingest_coverage/ingest_helpers.js +++ b/src/dev/code_coverage/ingest_coverage/ingest_helpers.js @@ -24,7 +24,6 @@ import { COVERAGE_INDEX, RESEARCH_COVERAGE_INDEX, RESEARCH_TOTALS_INDEX, - TEAM_ASSIGNMENT_PIPELINE_NAME, TOTALS_INDEX, } from './constants'; @@ -70,12 +69,6 @@ function color(whichColor) { }; } -export function maybeTeamAssign(isACoverageIndex, body) { - const doAddTeam = isACoverageIndex ? true : false; - const payload = doAddTeam ? { ...body, pipeline: TEAM_ASSIGNMENT_PIPELINE_NAME } : body; - return payload; -} - export function whichIndex(isResearchJob) { return (isTotal) => isTotal ? whichTotalsIndex(isResearchJob) : whichCoverageIndex(isResearchJob); diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js b/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js index ba73922ec508a..a4d07215efec1 100644 --- a/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/ingest_coverage.test.js @@ -20,6 +20,7 @@ import { resolve } from 'path'; import execa from 'execa'; import expect from '@kbn/expect'; +import shell from 'shelljs'; const ROOT_DIR = resolve(__dirname, '../../../../..'); const MOCKS_DIR = resolve(__dirname, './mocks'); @@ -35,9 +36,14 @@ const env = { }; describe('Ingesting coverage', () => { + const teamAssignmentsPath = + 'src/dev/code_coverage/ingest_coverage/team_assignment/team_assignments.txt'; + const verboseArgs = [ 'scripts/ingest_coverage.js', '--verbose', + '--teamAssignmentsPath', + teamAssignmentsPath, '--vcsInfoPath', 'src/dev/code_coverage/ingest_coverage/integration_tests/mocks/VCS_INFO.txt', '--path', @@ -46,6 +52,21 @@ describe('Ingesting coverage', () => { const summaryPath = 'jest-combined/coverage-summary-manual-mix.json'; const resolved = resolve(MOCKS_DIR, summaryPath); + beforeAll(async () => { + const params = [ + 'scripts/generate_team_assignments.js', + '--src', + '.github/CODEOWNERS', + '--dest', + teamAssignmentsPath, + ]; + await execa(process.execPath, params, { cwd: ROOT_DIR, env }); + }); + + afterAll(() => { + shell.rm(teamAssignmentsPath); + }); + describe(`staticSiteUrl`, () => { let actualUrl = ''; const siteUrlRegex = /"staticSiteUrl":\s*(.+,)/; diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/CODEOWNERS b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/CODEOWNERS new file mode 100644 index 0000000000000..1822c3fd95e34 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/CODEOWNERS @@ -0,0 +1,6 @@ +# GitHub CODEOWNERS definition +# Identify which groups will be pinged by changes to different parts of the codebase. +# For more info, see https://help.github.com/articles/about-codeowners/ + +# App +/x-pack/plugins/code/ @elastic/kibana-tre diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-possibly-n-teams.json b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-possibly-n-teams.json new file mode 100644 index 0000000000000..9e66d8f5cb101 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-possibly-n-teams.json @@ -0,0 +1,28 @@ +{ + "/var/lib/jenkins/workspace/elastic+kibana+code-coverage/kibana/src/plugins/charts/public/static/color_maps/color_maps.ts": { + "lines": { + "total": 4, + "covered": 4, + "skipped": 0, + "pct": 100 + }, + "functions": { + "total": 1, + "covered": 1, + "skipped": 0, + "pct": 100 + }, + "statements": { + "total": 4, + "covered": 4, + "skipped": 0, + "pct": 100 + }, + "branches": { + "total": 0, + "covered": 0, + "skipped": 0, + "pct": 100 + } + } +} diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-qa-research-job.json b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-qa-research-job.json new file mode 100644 index 0000000000000..6e4d8ea954c2c --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/mocks/jest-combined/coverage-summary-qa-research-job.json @@ -0,0 +1,28 @@ +{ + "/var/lib/jenkins/workspace/elastic+kibana+qa-research/kibana/x-pack/plugins/reporting/server/browsers/extract/unzip.js": { + "lines": { + "total": 4, + "covered": 4, + "skipped": 0, + "pct": 100 + }, + "functions": { + "total": 1, + "covered": 1, + "skipped": 0, + "pct": 100 + }, + "statements": { + "total": 4, + "covered": 4, + "skipped": 0, + "pct": 100 + }, + "branches": { + "total": 0, + "covered": 0, + "skipped": 0, + "pct": 100 + } + } +} diff --git a/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js b/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js new file mode 100644 index 0000000000000..c666581ddb08c --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/integration_tests/team_assignment.test.js @@ -0,0 +1,58 @@ +/* + * 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 { resolve } from 'path'; +import execa from 'execa'; +import expect from '@kbn/expect'; +import shell from 'shelljs'; + +const ROOT_DIR = resolve(__dirname, '../../../../..'); +const MOCKS_DIR = resolve(__dirname, './mocks'); + +describe('Team Assignment', () => { + const teamAssignmentsPath = + 'src/dev/code_coverage/ingest_coverage/team_assignment/team_assignments.txt'; + const mockCodeOwners = 'CODEOWNERS'; + const resolved = resolve(MOCKS_DIR, mockCodeOwners); + + beforeAll(async () => { + const params = [ + 'scripts/generate_team_assignments.js', + '--src', + resolved, + '--dest', + teamAssignmentsPath, + ]; + await execa(process.execPath, params, { cwd: ROOT_DIR }); + }); + + afterAll(() => { + shell.rm(teamAssignmentsPath); + }); + + describe(`when the codeowners file contains #CC#`, () => { + it(`should strip the prefix and still drill down through the fs`, async () => { + const { stdout } = await execa('grep', ['tre', teamAssignmentsPath], { cwd: ROOT_DIR }); + expect(stdout).to.be(`x-pack/plugins/code/server/config.ts kibana-tre +x-pack/plugins/code/server/index.ts kibana-tre +x-pack/plugins/code/server/plugin.test.ts kibana-tre +x-pack/plugins/code/server/plugin.ts kibana-tre`); + }); + }); +}); diff --git a/src/dev/code_coverage/ingest_coverage/process.js b/src/dev/code_coverage/ingest_coverage/process.js index 85a42cfffa6e2..28a7c9ccd41b0 100644 --- a/src/dev/code_coverage/ingest_coverage/process.js +++ b/src/dev/code_coverage/ingest_coverage/process.js @@ -18,7 +18,7 @@ */ import { fromEventPattern, of, fromEvent } from 'rxjs'; -import { concatMap, delay, map, takeUntil } from 'rxjs/operators'; +import { concatMap, delay, map, mergeMap, takeUntil } from 'rxjs/operators'; import jsonStream from './json_stream'; import { pipe, noop, green, always } from './utils'; import { ingest } from './ingest'; @@ -32,6 +32,7 @@ import { coveredFilePath, ciRunUrl, itemizeVcs, + teamAssignment, } from './transforms'; import { resolve } from 'path'; import { createReadStream } from 'fs'; @@ -50,9 +51,10 @@ const addPrePopulatedTimeStamp = addTimeStamp(process.env.TIME_STAMP || formatte const preamble = pipe(statsAndstaticSiteUrl, rootDirAndOrigPath, buildId, addPrePopulatedTimeStamp); const addTestRunnerAndStaticSiteUrl = pipe(testRunner, staticSite(staticSiteUrlBase)); -const transform = (jsonSummaryPath) => (log) => (vcsInfo) => { +const transform = (jsonSummaryPath) => (log) => (vcsInfo) => (teamAssignmentsPath) => { const objStream = jsonStream(jsonSummaryPath).on('done', noop); const itemizeVcsInfo = itemizeVcs(vcsInfo); + const assignTeams = teamAssignment(teamAssignmentsPath)(log); const jsonSummary$ = (_) => objStream.on('node', '!.*', _); @@ -64,6 +66,7 @@ const transform = (jsonSummaryPath) => (log) => (vcsInfo) => { map(ciRunUrl), map(addJsonSummaryPath(jsonSummaryPath)), map(addTestRunnerAndStaticSiteUrl), + mergeMap(assignTeams), concatMap((x) => of(x).pipe(delay(ms))) ) .subscribe(ingest(log)); @@ -83,7 +86,7 @@ const vcsInfoLines$ = (vcsInfoFilePath) => { return fromEvent(rl, 'line').pipe(takeUntil(fromEvent(rl, 'close'))); }; -export const prok = ({ jsonSummaryPath, vcsInfoFilePath }, log) => { +export const prok = ({ jsonSummaryPath, vcsInfoFilePath, teamAssignmentsPath }, log) => { validateRoot(COVERAGE_INGESTION_KIBANA_ROOT, log); logAll(jsonSummaryPath, log); @@ -93,7 +96,7 @@ export const prok = ({ jsonSummaryPath, vcsInfoFilePath }, log) => { vcsInfoLines$(vcsInfoFilePath).subscribe( mutateVcsInfo(vcsInfo), (err) => log.error(err), - always(xformWithPath(vcsInfo)) + always(xformWithPath(vcsInfo)(teamAssignmentsPath)) ); }; diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/enumerate_patterns.js b/src/dev/code_coverage/ingest_coverage/team_assignment/enumerate_patterns.js new file mode 100644 index 0000000000000..dcdb32c91b8f8 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/enumerate_patterns.js @@ -0,0 +1,78 @@ +/* + * 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 { readdirSync, statSync } from 'fs'; +import { join } from 'path'; +import { + push, + prokGlob, + trim, + isRejectedDir, + isFileAllowed, + isDir, + tryPath, + dropEmpty, + notFound, +} from './enumeration_helpers'; +import { stripLeading } from '../transforms'; + +export const enumeratePatterns = (rootPath) => (log) => (patterns) => { + const res = []; + const resPush = push(res); + const logNotFound = notFound(log); + + for (const entry of patterns) { + const [pathPattern, team] = entry; + const cleaned = stripLeading(pathPattern); + const existsWithOwner = pathExists(team); + + const collect = (x) => existsWithOwner(x).forEach(resPush); + tryPath(cleaned).fold(logNotFound, collect); + } + + return res; + + function pathExists(owner) { + const creeper = (x) => creepFsSync(x, [], rootPath, owner); + return function creepAllAsGlobs(pathPattern) { + return prokGlob(pathPattern).map(creeper).filter(dropEmpty); + }; + } +}; + +function creepFsSync(aPath, xs, rootPath, owner) { + xs = xs || []; + + const joinRoot = join.bind(null, rootPath); + const trimRoot = trim(rootPath); + const joined = joinRoot(aPath); + const isADir = isDir(joined); + + (isADir ? readdirSync(joined) : [aPath]).forEach(maybeRecurse); + + return xs; + + function maybeRecurse(entry) { + const full = isADir ? join(aPath, entry) : entry; + const fullIsDir = statSync(full).isDirectory(); + + if (fullIsDir && !isRejectedDir(full)) xs = creepFsSync(full, xs, rootPath, owner); + else if (isFileAllowed(full)) xs.push(`${trimRoot(full)} ${owner}`); + } +} diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/enumeration_helpers.js b/src/dev/code_coverage/ingest_coverage/team_assignment/enumeration_helpers.js new file mode 100644 index 0000000000000..b06141965d2b6 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/enumeration_helpers.js @@ -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 { statSync } from 'fs'; +import isGlob from 'is-glob'; +import glob from 'glob'; +import { left, right, tryCatch } from '../either'; + +export const push = (xs) => (x) => xs.push(x); +export const pathExists = (x) => tryCatch(() => statSync(x)).fold(left, right); +export const isDir = (x) => statSync(x).isDirectory(); +export const prokGlob = (x) => glob.sync(x, { nonull: true }); +export const trim = (ROOT) => (x) => x.replace(`${ROOT}/`, ''); +export const isFileAllowed = (x) => { + const isJsOrTsOrTsxOrJsx = /.(j|t)(s|sx)$/gm; + return isJsOrTsOrTsxOrJsx.test(x); +}; +export const isRejectedDir = (x) => + /node_modules|__tests__|__fixture__|__fixtures__|build\//gm.test(x); +const isGlobFound = (x) => (xs) => (x === xs[0] ? false : true); +export const globExpands = (x) => isGlobFound(x)(prokGlob(x)); +export const tryPath = (x) => { + const isAGlob = isGlob(x); + + if (isAGlob) return globExpands(x) ? right(x) : left(x); + + if (!isAGlob) return pathExists(x).isRight() ? right(x) : left(x); +}; +export const dropEmpty = (x) => x.length > 0; +export const notFound = (log) => (err) => log.error(`\n!!! Not Found: \n${err}`); diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/flush.js b/src/dev/code_coverage/ingest_coverage/team_assignment/flush.js new file mode 100644 index 0000000000000..5150ac2e655f2 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/flush.js @@ -0,0 +1,44 @@ +/* + * 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 { writeFileSync } from 'fs'; +import shell from 'shelljs'; +import { tryCatch } from '../either'; +import { id } from '../utils'; + +const encoding = 'utf8'; +const appendUtf8 = { flag: 'a', encoding }; + +export const flush = (dest) => (log) => (assignments) => { + log.verbose(`\n### Flushing assignments to: \n\t${dest}`); + + const writeToFile = writeFileSync.bind(null, dest); + + writeToFile('', { encoding }); + + for (const xs of assignments) xs.forEach((x) => writeToFile(`${x}\n`, appendUtf8)); + + tryCatch(() => maybeShowSize(dest)).fold(id, (x) => { + log.verbose(`\n### Flushed [${x}] lines`); + }); +}; +function maybeShowSize(x) { + const { output } = shell.exec(`wc -l ${x}`, { silent: true }); + return output.match(/\s*\d*\s*/)[0].trim(); +} diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js b/src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js deleted file mode 100644 index 34526a2f79302..0000000000000 --- a/src/dev/code_coverage/ingest_coverage/team_assignment/get_data.js +++ /dev/null @@ -1,33 +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 { readFileSync } from 'fs'; -import { resolve } from 'path'; -import { tryCatch as tc } from '../either'; - -const ROOT = resolve(__dirname, '../../../../..'); - -const resolveFromRoot = resolve.bind(null, ROOT); - -const resolved = (path) => () => resolveFromRoot(path); - -const getContents = (path) => tc(() => readFileSync(path, 'utf8')); - -// fetch :: String -> Left | Right -export const fetch = (path) => tc(resolved(path)).chain(getContents); diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/index.js b/src/dev/code_coverage/ingest_coverage/team_assignment/index.js index 11f9748708283..30112df3b6ba4 100644 --- a/src/dev/code_coverage/ingest_coverage/team_assignment/index.js +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/index.js @@ -17,42 +17,55 @@ * under the License. */ -import { run } from '@kbn/dev-utils'; -import { TEAM_ASSIGNMENT_PIPELINE_NAME } from '../constants'; -import { fetch } from './get_data'; -import { update } from './update_ingest_pipeline'; +import { run, createFlagError, REPO_ROOT } from '@kbn/dev-utils'; +import { parse } from './parse_owners'; +import { flush } from './flush'; +import { enumeratePatterns } from './enumerate_patterns'; +import { pipe } from '../utils'; +import { reduce } from 'rxjs/operators'; -const updatePipeline = update(TEAM_ASSIGNMENT_PIPELINE_NAME); - -const execute = ({ flags, log }) => { - if (flags.verbose) log.verbose(`### Verbose logging enabled`); - - const logLeft = handleErr(log); - const updateAndLog = updatePipeline(log); - - const { path } = flags; - - fetch(path).fold(logLeft, updateAndLog); +const flags = { + string: ['src', 'dest'], + help: ` +--src Required, path to CODEOWNERS file. +--dest Required, destination path of the assignments. + `, }; -function handleErr(log) { - return (msg) => log.error(msg); -} +export const generateTeamAssignments = () => { + run( + ({ flags, log }) => { + if (flags.src === '') throw createFlagError('please provide a single --src flag'); + if (flags.dest === '') throw createFlagError('please provide a single --dest flag'); -const description = ` + const logCreepAndFlush = pipe( + logSuccess(flags.src, log), + enumeratePatterns(REPO_ROOT)(log), + flush(flags.dest)(log) + ); -Upload the latest team assignment pipeline def from src, -to the cluster. + parse(flags.src).pipe(reduce(toMap, new Map())).subscribe(logCreepAndFlush); + }, + { + description: ` - `; +Create a file defining the team assignments, + parsed from .github/CODEOWNERS -const flags = { - string: ['path', 'verbose'], - help: ` ---path Required, path to painless definition for team assignment. - `, + `, + flags, + } + ); }; -const usage = 'node scripts/load_team_assignment.js --verbose --path PATH_TO_PAINLESS_SCRIPT.json'; +function toMap(acc, x) { + acc.set(x[0], x[1][0]); + return acc; +} -export const uploadTeamAssignmentJson = () => run(execute, { description, flags, usage }); +function logSuccess(src, log) { + return (dataObj) => { + log.verbose(`\n### Parsing [${src}] Complete`); + return dataObj; + }; +} diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json b/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json deleted file mode 100644 index 017d208133cdc..0000000000000 --- a/src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json +++ /dev/null @@ -1 +0,0 @@ -{"description":"Kibana code coverage team assignments","processors":[{"script":{"lang":"painless","source":"\n String path = ctx.coveredFilePath; \n if (path.indexOf('src/legacy/core_plugins/kibana/') == 0) {\n\n if (path.indexOf('src/legacy/core_plugins/kibana/common/utils') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/migrations') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/dashboard/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/dev_tools/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/discover/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/home') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/home/np_ready/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/local_application_service/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/kibana/public/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/lib') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/lib/management/saved_objects') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/import/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/kibana/server/routes/api/export/') == 0) ctx.team = 'kibana-platform';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/core_plugins/') == 0) {\n\n if (path.indexOf('src/legacy/core_plugins/apm_oss/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/legacy/core_plugins/console_legacy') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/elasticsearch') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/core_plugins/embeddable_api/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/input_control_vis') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/interpreter/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/kibana_react/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/newsfeed') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/legacy/core_plugins/region_map') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/legacy/core_plugins/status_page/public') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/core_plugins/testbed') == 0) ctx.team = 'kibana-platform';\n // else if (path.indexOf('src/legacy/core_plugins/tests_bundle/') == 0) ctx.team = 'kibana-platform';\n \n else if (path.indexOf('src/legacy/core_plugins/tile_map') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/legacy/core_plugins/timelion') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/ui_metric/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_tagcloud') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_vega') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/vis_type_vislib/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/core_plugins/visualizations/') == 0) ctx.team = 'kibana-app-arch';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/server/') == 0) {\n\n if (path.indexOf('src/legacy/server/config/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/http/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/legacy/server/index_patterns/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/server/keystore/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/logging/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/pid/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/sample_data/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/server/sass/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/saved_objects/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/status/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/server/url_shortening/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/server/utils/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/legacy/server/warnings/') == 0) ctx.team = 'kibana-operations';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('src/legacy/ui') == 0) {\n\n if (path.indexOf('src/legacy/ui/public/field_editor') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/timefilter') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/management') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/legacy/ui/public/state_management') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/legacy/ui/public/new_platform') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/plugin_discovery') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/chrome') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/notify') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/documentation_links') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/autoload') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/legacy/ui/public/capabilities') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('src/legacy/ui/public/apm') == 0) ctx.team = 'apm-ui';\n\n } else if (path.indexOf('src/plugins/') == 0) {\n\n if (path.indexOf('src/plugins/advanced_settings/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/apm_oss/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/plugins/bfetch/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/charts/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/charts/public/static/color_maps') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/console/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/dashboard/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/data/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/dev_tools/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/discover/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/embeddable/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/es_ui_shared/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('src/plugins/expressions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/home/public') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/home/server/tutorials') == 0) ctx.team = 'observability';\n else if (path.indexOf('src/plugins/home/server/services/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/home/') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/index_pattern_management/public/service') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/index_pattern_management/public') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/input_control_vis/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/inspector/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_legacy/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/kibana_react/public/code_editor') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('src/plugins/kibana_react/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_utils/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/management/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/kibana_usage_collection/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/legacy_export/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/maps_legacy/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/region_map/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/tile_map/') == 0) ctx.team = 'maps';\n else if (path.indexOf('src/plugins/timelion') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/navigation/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/newsfeed') == 0) ctx.team = 'kibana-core-ui';\n else if (path.indexOf('src/plugins/saved_objects_management/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/saved_objects/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/share/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/status_page/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/telemetry') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/testbed/server/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/plugins/ui_actions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/usage_collection/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('src/plugins/vis_default_editor') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/vis_type') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('src/plugins/visualizations/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('src/plugins/visualize/') == 0) ctx.team = 'kibana-app';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('x-pack/legacy/') == 0) {\n\n if (path.indexOf('x-pack/legacy/plugins/actions/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/alerting/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/apm/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/legacy/plugins/beats_management/') == 0) ctx.team = 'beats';\n else if (path.indexOf('x-pack/legacy/plugins/canvas/') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('x-pack/legacy/plugins/cross_cluster_replication/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/dashboard_mode/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/legacy/plugins/encrypted_saved_objects/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/legacy/plugins/index_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/infra/') == 0) ctx.team = 'logs-metrics-ui';\n else if (path.indexOf('x-pack/legacy/plugins/ingest_manager/') == 0) ctx.team = 'ingest-management';\n else if (path.indexOf('x-pack/legacy/plugins/license_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/maps/') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/legacy/plugins/ml/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/legacy/plugins/monitoring/') == 0) ctx.team = 'stack-monitoring-ui';\n else if (path.indexOf('x-pack/legacy/plugins/reporting') == 0) ctx.team = 'kibana-reporting';\n else if (path.indexOf('x-pack/legacy/plugins/rollup/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/security/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/legacy/plugins/siem/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules') == 0) ctx.team = 'security-intelligence-analytics';\n else if (path.indexOf('x-pack/legacy/plugins/snapshot_restore/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/task_manager') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/triggers_actions_ui/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/legacy/plugins/upgrade_assistant/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/plugins/uptime') == 0) ctx.team = 'uptime';\n else if (path.indexOf('x-pack/legacy/plugins/xpack_main/server/') == 0) ctx.team = 'kibana-platform';\n\n else if (path.indexOf('x-pack/legacy/server/lib/create_router/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/legacy/server/lib/check_license/') == 0) ctx.team = 'es-ui'; \n else if (path.indexOf('x-pack/legacy/server/lib/') == 0) ctx.team = 'kibana-platform'; \n else ctx.team = 'unknown';\n\n } else if (path.indexOf('x-pack/plugins/') == 0) {\n\n if (path.indexOf('x-pack/plugins/actions/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/advanced_ui_actions/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/alerts') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/alerting_builtins') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/apm/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/plugins/beats_management/') == 0) ctx.team = 'beats';\n else if (path.indexOf('x-pack/plugins/canvas/') == 0) ctx.team = 'kibana-canvas';\n else if (path.indexOf('x-pack/plugins/case') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/cloud/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/code/') == 0) ctx.team = 'code';\n else if (path.indexOf('x-pack/plugins/console_extensions/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/cross_cluster_replication/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/dashboard_enhanced') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/dashboard_mode') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/discover_enhanced') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/embeddable_enhanced') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/data_enhanced/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/drilldowns/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/encrypted_saved_objects/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/endpoint/') == 0) ctx.team = 'endpoint-app-team';\n else if (path.indexOf('x-pack/plugins/es_ui_shared/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/event_log/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/features/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/file_upload') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/plugins/global_search') == 0) ctx.team = 'kibana-platform';\n \n else if (path.indexOf('x-pack/plugins/graph/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/grokdebugger/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/index_lifecycle_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/index_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/infra/') == 0) ctx.team = 'logs-metrics-ui';\n else if (path.indexOf('x-pack/plugins/ingest_manager/') == 0) ctx.team = 'ingest-management';\n else if (path.indexOf('x-pack/plugins/ingest_pipelines/') == 0) ctx.team = 'es-ui';\n \n else if (path.indexOf('x-pack/plugins/lens/') == 0) ctx.team = 'kibana-app';\n else if (path.indexOf('x-pack/plugins/license_management/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/licensing/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('x-pack/plugins/lists/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/logstash') == 0) ctx.team = 'logstash';\n else if (path.indexOf('x-pack/plugins/maps/') == 0) ctx.team = 'kibana-gis';\n else if (path.indexOf('x-pack/plugins/maps_legacy_licensing') == 0) ctx.team = 'maps';\n else if (path.indexOf('x-pack/plugins/ml/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/plugins/monitoring') == 0) ctx.team = 'stack-monitoring-ui';\n else if (path.indexOf('x-pack/plugins/observability/') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('x-pack/plugins/oss_telemetry/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('x-pack/plugins/painless_lab/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/remote_clusters/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/reporting') == 0) ctx.team = 'kibana-reporting';\n else if (path.indexOf('x-pack/plugins/rollup/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/searchprofiler/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/security/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/security_solution/') == 0) ctx.team = 'siem';\n \n else if (path.indexOf('x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules') == 0) ctx.team = 'security-intelligence-analytics';\n else if (path.indexOf('x-pack/plugins/siem/') == 0) ctx.team = 'siem';\n else if (path.indexOf('x-pack/plugins/snapshot_restore/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/spaces/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('x-pack/plugins/task_manager/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/telemetry_collection_xpack/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('x-pack/plugins/transform/') == 0) ctx.team = 'ml-ui';\n else if (path.indexOf('x-pack/plugins/translations/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('x-pack/plugins/triggers_actions_ui/') == 0) ctx.team = 'kibana-alerting-services';\n else if (path.indexOf('x-pack/plugins/upgrade_assistant/') == 0) ctx.team = 'es-ui';\n else if (path.indexOf('x-pack/plugins/ui_actions_enhanced') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('x-pack/plugins/uptime') == 0) ctx.team = 'uptime';\n \n else if (path.indexOf('x-pack/plugins/watcher/') == 0) ctx.team = 'es-ui';\n else ctx.team = 'unknown';\n\n } else if (path.indexOf('packages') == 0) {\n\n if (path.indexOf('packages/kbn-analytics/') == 0) ctx.team = 'pulse';\n else if (path.indexOf('packages/kbn-babel') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-config-schema/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('packages/elastic-datemath') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('packages/kbn-dev-utils') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-es/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-eslint') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-expect') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('packages/kbn-interpreter/') == 0) ctx.team = 'kibana-app-arch';\n else if (path.indexOf('packages/kbn-optimizer/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-pm/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-test/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-test-subj-selector/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('packages/kbn-ui-framework/') == 0) ctx.team = 'kibana-design';\n else if (path.indexOf('packages/kbn-ui-shared-deps/') == 0) ctx.team = 'kibana-operations';\n else ctx.team = 'unknown';\n\n } else {\n\n if (path.indexOf('config/kibana.yml') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/apm.js') == 0) ctx.team = 'apm-ui';\n else if (path.indexOf('src/core/') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('src/core/public/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/core/server/csp/') == 0) ctx.team = 'kibana-security';\n else if (path.indexOf('src/dev/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/dev/i18n/') == 0) ctx.team = 'kibana-localization';\n else if (path.indexOf('src/dev/run_check_published_api_changes.ts') == 0) ctx.team = 'kibana-platform';\n else if (path.indexOf('packages/kbn-es-archiver/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/optimize/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/setup_node_env/') == 0) ctx.team = 'kibana-operations';\n else if (path.indexOf('src/test_utils/') == 0) ctx.team = 'kibana-operations'; \n else ctx.team = 'unknown';\n }"}}]} diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners.js b/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners.js new file mode 100644 index 0000000000000..a07d556c9b403 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners.js @@ -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 { fromEvent } from 'rxjs'; +import { map, filter, takeUntil } from 'rxjs/operators'; +import { lineRead, pathAndTeams, empties, comments, dropCCDelim } from './parse_owners_helpers'; +import { pipe } from '../utils'; + +const cleanAndParse = pipe(dropCCDelim, pathAndTeams); + +const allLines$ = (lineReader) => + fromEvent(lineReader, 'line').pipe( + filter(empties), + filter(comments), + map(cleanAndParse), + takeUntil(fromEvent(lineReader, 'close')) + ); + +export const parse = (codeOwnersPath) => allLines$(lineRead(codeOwnersPath)); diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners_helpers.js b/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners_helpers.js new file mode 100644 index 0000000000000..454accb00a7b6 --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/parse_owners_helpers.js @@ -0,0 +1,48 @@ +/* + * 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 { always, id, pipe } from '../utils'; +import * as Either from '../either'; +import readline from 'readline'; +import { createReadStream } from 'fs'; +import { pluckIndex } from '../transforms'; + +const coverageDelimRe = /^#CC#\s/; + +export const empties = (x) => x !== ''; +export const comments = (x) => !/^#\s{1,3}/.test(x); +const dropDelim = (x) => () => x.replace(coverageDelimRe, ''); + +export const dropCCDelim = (x) => + Either.fromNullable(coverageDelimRe.test(x)).fold(always(x), id(dropDelim(x))); + +const splitFilter = (splitter) => (x) => x.split(splitter).filter(empties); +const spaceSplit = splitFilter(' '); +const esSplit = splitFilter('@elastic/'); +const getFirst = pluckIndex(0); +const trimEsGrabFirst = pipe(esSplit, getFirst); + +export const pathAndTeams = (x) => { + const [path, ...teamEntries] = spaceSplit(x); + const teams = teamEntries.map(trimEsGrabFirst); + + return [path, teams]; +}; + +export const lineRead = (x) => readline.createInterface({ input: createReadStream(x) }); diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/parsing_helpers.js b/src/dev/code_coverage/ingest_coverage/team_assignment/parsing_helpers.js new file mode 100644 index 0000000000000..50dd6f719f34e --- /dev/null +++ b/src/dev/code_coverage/ingest_coverage/team_assignment/parsing_helpers.js @@ -0,0 +1,30 @@ +/* + * 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 { always } from '../utils'; +import * as Either from '../either'; + +const coverageDelimRe = /^#CC#\s/; + +export const empties = (x) => x !== ''; +export const comments = (x) => !/^#\s{1,3}/.test(x); +const dropDelim = (x) => x.replace(coverageDelimRe, ''); + +export const dropCCDelim = (x) => + Either.fromNullable(coverageDelimRe.test(x)).fold(always(x), always(dropDelim(x))); diff --git a/src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js b/src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js deleted file mode 100644 index 22a9d0a461ebf..0000000000000 --- a/src/dev/code_coverage/ingest_coverage/team_assignment/update_ingest_pipeline.js +++ /dev/null @@ -1,37 +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 { createFailError } from '@kbn/dev-utils'; -import { ES_HOST } from '../constants'; -import { pretty, green } from '../utils'; - -const { Client } = require('@elastic/elasticsearch'); - -const node = ES_HOST; -const client = new Client({ node }); - -export const update = (id) => (log) => async (body) => { - try { - await client.ingest.putPipeline({ id, body }); - log.verbose(`### Ingestion Pipeline ID: ${green(id)}`); - log.verbose(`### Payload Partial: \n${body.slice(0, 600)}...`); - } catch (e) { - throw createFailError(`${pretty(e.meta)}`); - } -}; diff --git a/src/dev/code_coverage/ingest_coverage/transforms.js b/src/dev/code_coverage/ingest_coverage/transforms.js index b8c9acd6fc49d..ff6ffd46462ed 100644 --- a/src/dev/code_coverage/ingest_coverage/transforms.js +++ b/src/dev/code_coverage/ingest_coverage/transforms.js @@ -18,8 +18,12 @@ */ import * as Either from './either'; -import { fromNullable } from './maybe'; -import { always, id, noop } from './utils'; +import * as Maybe from './maybe'; +import { always, id, noop, pink } from './utils'; +import execa from 'execa'; +import { resolve } from 'path'; + +const ROOT_DIR = resolve(__dirname, '../../../..'); const maybeTotal = (x) => (x === 'total' ? Either.left(x) : Either.right(x)); @@ -83,20 +87,60 @@ export const staticSite = (urlBase) => (obj) => { return { ...obj, staticSiteUrl: prokForBoth() }; }; +const leadingSlashRe = /^\//; +export const maybeDropLeadingSlash = (x) => + leadingSlashRe.test(x) ? Either.right(x) : Either.left(x); +export const dropLeadingSlash = (x) => x.replace(leadingSlashRe, ''); +export const stripLeading = (x) => maybeDropLeadingSlash(x).fold(id, dropLeadingSlash); + export const coveredFilePath = (obj) => { const { staticSiteUrl, COVERAGE_INGESTION_KIBANA_ROOT } = obj; const withoutCoveredFilePath = always(obj); - const leadingSlashRe = /^\//; - const maybeDropLeadingSlash = (x) => (leadingSlashRe.test(x) ? Either.right(x) : Either.left(x)); - const dropLeadingSlash = (x) => x.replace(leadingSlashRe, ''); - const dropRoot = (root) => (x) => - maybeDropLeadingSlash(x.replace(root, '')).fold(id, dropLeadingSlash); + const dropRoot = (root) => (x) => stripLeading(x.replace(root, '')); return maybeTotal(staticSiteUrl) .map(dropRoot(COVERAGE_INGESTION_KIBANA_ROOT)) .fold(withoutCoveredFilePath, (coveredFilePath) => ({ ...obj, coveredFilePath })); }; +const findTeam = (x) => x.match(/.+\s{1,3}(.+)$/, 'gm'); +export const pluckIndex = (idx) => (xs) => xs[idx]; +const pluckTeam = pluckIndex(1); + +export const teamAssignment = (teamAssignmentsPath) => (log) => async (obj) => { + const { coveredFilePath } = obj; + const isTotal = Either.fromNullable(obj.isTotal); + + return isTotal.isRight() ? obj : await assignTeam(teamAssignmentsPath, coveredFilePath, log, obj); +}; +export const last = (x) => { + const xs = x.split('\n'); + const len = xs.length; + + return len === 1 ? xs[0] : xs[len - 1]; +}; +async function assignTeam(teamAssignmentsPath, coveredFilePath, log, obj) { + const params = [coveredFilePath, teamAssignmentsPath]; + + let grepResponse; + + try { + const { stdout } = await execa('grep', params, { cwd: ROOT_DIR }); + grepResponse = stdout; + } catch (e) { + log.error(`\n!!! Unknown Team for path: \n\t\t${pink(coveredFilePath)}\n`); + } + + return Either.fromNullable(grepResponse) + .map(last) + .map(findTeam) + .map(pluckTeam) + .fold( + () => ({ team: 'unknown', ...obj }), + (team) => ({ team, ...obj }) + ); +} + export const ciRunUrl = (obj) => Either.fromNullable(process.env.CI_RUN_URL).fold(always(obj), (ciRunUrl) => ({ ...obj, @@ -126,13 +170,12 @@ export const itemizeVcs = (vcsInfo) => (obj) => { }; const mutateVcs = (x) => (vcs.commitMsg = truncateMsg(x)); - fromNullable(commitMsg).map(mutateVcs); + Maybe.fromNullable(commitMsg).map(mutateVcs); const vcsCompareUrl = process.env.FETCHED_PREVIOUS ? `${comparePrefix()}/${process.env.FETCHED_PREVIOUS}...${sha}` : 'PREVIOUS SHA NOT PROVIDED'; - // const withoutPreviousL = always({ ...obj, vcs }); const withPreviousR = () => ({ ...obj, vcs: { diff --git a/src/dev/code_coverage/ingest_coverage/utils.js b/src/dev/code_coverage/ingest_coverage/utils.js index 7d817bdf7a6f3..e854e3d3b7248 100644 --- a/src/dev/code_coverage/ingest_coverage/utils.js +++ b/src/dev/code_coverage/ingest_coverage/utils.js @@ -22,6 +22,10 @@ import chalk from 'chalk'; export const pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); export const noop = () => {}; export const green = (x) => chalk.greenBright.bold(x); +export const pink = (x) => chalk.bgMagenta.bold.cyan.bold(x); export const id = (x) => x; -export const always = (x) => () => x; +export const always = (x) => () => x; // Wraps a value in a fn. Eager evaluation if passed a fn. export const pretty = (x) => JSON.stringify(x, null, 2); +export const reThrow = (e) => { + throw e; +}; diff --git a/src/dev/code_coverage/shell_scripts/assign_teams.sh b/src/dev/code_coverage/shell_scripts/assign_teams.sh deleted file mode 100644 index aaa14655a9a26..0000000000000 --- a/src/dev/code_coverage/shell_scripts/assign_teams.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -echo "### Code Coverage Team Assignment" -echo "" - -PIPELINE_NAME=$1 -export PIPELINE_NAME - -ES_HOST="https://${USER_FROM_VAULT}:${PASS_FROM_VAULT}@${HOST_FROM_VAULT}" -export ES_HOST - -node scripts/load_team_assignment.js --verbose --path src/dev/code_coverage/ingest_coverage/team_assignment/ingestion_pipeline_painless.json - -echo "### Code Coverage Team Assignment - Complete" -echo "" diff --git a/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh b/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh new file mode 100644 index 0000000000000..62b81929ae79b --- /dev/null +++ b/src/dev/code_coverage/shell_scripts/generate_team_assignments_and_ingest_coverage.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +echo "### Ingesting Code Coverage" +echo "" + +COVERAGE_JOB_NAME=$1 +export COVERAGE_JOB_NAME +echo "### debug COVERAGE_JOB_NAME: ${COVERAGE_JOB_NAME}" + +BUILD_ID=$2 +export BUILD_ID + +CI_RUN_URL=$3 +export CI_RUN_URL +echo "### debug CI_RUN_URL: ${CI_RUN_URL}" + +FETCHED_PREVIOUS=$4 +export FETCHED_PREVIOUS +echo "### debug FETCHED_PREVIOUS: ${FETCHED_PREVIOUS}" + +ES_HOST="https://${USER_FROM_VAULT}:${PASS_FROM_VAULT}@${HOST_FROM_VAULT}" +export ES_HOST + +STATIC_SITE_URL_BASE='https://kibana-coverage.elastic.dev' +export STATIC_SITE_URL_BASE + +DELAY=100 +export DELAY + +TEAM_ASSIGN_PATH=$5 + +# Build team assignments dat file +node scripts/generate_team_assignments.js --verbose --src .github/CODEOWNERS --dest $TEAM_ASSIGN_PATH + +for x in jest functional; do + echo "### Ingesting coverage for ${x}" + + COVERAGE_SUMMARY_FILE=target/kibana-coverage/${x}-combined/coverage-summary.json + + node scripts/ingest_coverage.js --verbose --path ${COVERAGE_SUMMARY_FILE} --vcsInfoPath ./VCS_INFO.txt --teamAssignmentsPath $TEAM_ASSIGN_PATH +done + +# Need to override COVERAGE_INGESTION_KIBANA_ROOT since mocha json file has original intake worker path +COVERAGE_SUMMARY_FILE=target/kibana-coverage/mocha-combined/coverage-summary.json +export COVERAGE_INGESTION_KIBANA_ROOT=/dev/shm/workspace/kibana + +node scripts/ingest_coverage.js --verbose --path ${COVERAGE_SUMMARY_FILE} --vcsInfoPath ./VCS_INFO.txt --teamAssignmentsPath $TEAM_ASSIGN_PATH + +echo "### Ingesting Code Coverage - Complete" +echo "" diff --git a/src/dev/code_coverage/shell_scripts/ingest_coverage.sh b/src/dev/code_coverage/shell_scripts/ingest_coverage.sh deleted file mode 100644 index 0b67dac307473..0000000000000 --- a/src/dev/code_coverage/shell_scripts/ingest_coverage.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -echo "### Ingesting Code Coverage" -echo "" - -COVERAGE_JOB_NAME=$1 -export COVERAGE_JOB_NAME -echo "### debug COVERAGE_JOB_NAME: ${COVERAGE_JOB_NAME}" - -BUILD_ID=$2 -export BUILD_ID - -CI_RUN_URL=$3 -export CI_RUN_URL -echo "### debug CI_RUN_URL: ${CI_RUN_URL}" - -FETCHED_PREVIOUS=$4 -export FETCHED_PREVIOUS -echo "### debug FETCHED_PREVIOUS: ${FETCHED_PREVIOUS}" - -ES_HOST="https://${USER_FROM_VAULT}:${PASS_FROM_VAULT}@${HOST_FROM_VAULT}" -export ES_HOST - -STATIC_SITE_URL_BASE='https://kibana-coverage.elastic.dev' -export STATIC_SITE_URL_BASE - -DELAY=100 -export DELAY - -for x in jest functional; do - echo "### Ingesting coverage for ${x}" - - COVERAGE_SUMMARY_FILE=target/kibana-coverage/${x}-combined/coverage-summary.json - - node scripts/ingest_coverage.js --verbose --path ${COVERAGE_SUMMARY_FILE} --vcsInfoPath ./VCS_INFO.txt -done - -# Need to override COVERAGE_INGESTION_KIBANA_ROOT since mocha json file has original intake worker path -COVERAGE_SUMMARY_FILE=target/kibana-coverage/mocha-combined/coverage-summary.json -export COVERAGE_INGESTION_KIBANA_ROOT=/dev/shm/workspace/kibana - -node scripts/ingest_coverage.js --verbose --path ${COVERAGE_SUMMARY_FILE} --vcsInfoPath ./VCS_INFO.txt - -echo "### Ingesting Code Coverage - Complete" -echo "" diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 5d31db63773fa..3c556a4f1ba3c 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -55,7 +55,6 @@ export default { '@elastic/eui$': '/node_modules/@elastic/eui/test-env', '@elastic/eui/lib/(.*)?': '/node_modules/@elastic/eui/test-env/$1', '^src/plugins/(.*)': '/src/plugins/$1', - '^uiExports/(.*)': '/src/dev/jest/mocks/file_mock.js', '^test_utils/(.*)': '/src/test_utils/public/$1', '^fixtures/(.*)': '/src/fixtures/$1', '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': diff --git a/src/dev/jest/setup/react_testing_library.js b/src/dev/jest/setup/react_testing_library.js index 41f58354844a3..84b5b6096e79b 100644 --- a/src/dev/jest/setup/react_testing_library.js +++ b/src/dev/jest/setup/react_testing_library.js @@ -29,4 +29,4 @@ import '@testing-library/jest-dom'; import { configure } from '@testing-library/react/pure'; // instead of default 'data-testid', use kibana's 'data-test-subj' -configure({ testIdAttribute: 'data-test-subj' }); +configure({ testIdAttribute: 'data-test-subj', asyncUtilTimeout: 4500 }); diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index ba58dcdfa4d58..9cd6ca6801bc3 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -50,14 +50,12 @@ export const IGNORE_FILE_GLOBS = [ 'vars/*', '.ci/pipeline-library/**/*', - // Files in this directory must match a pre-determined name in some cases. - 'x-pack/plugins/canvas/storybook/*', - // filename must match language code which requires capital letters '**/translations/*.json', - // filename is required by storybook - 'packages/kbn-storybook/storybook_config/preview-head.html', + // Storybook has predetermined filesnames + '**/preview-body.html', + '**/preview-head.html', // filename required by api-extractor 'api-documenter.json', diff --git a/src/dev/run_check_published_api_changes.ts b/src/dev/run_check_published_api_changes.ts index 28e8570812915..aaac706f991c2 100644 --- a/src/dev/run_check_published_api_changes.ts +++ b/src/dev/run_check_published_api_changes.ts @@ -230,6 +230,9 @@ async function run(folder: string, { opts }: { opts: Options }): Promise`, diff --git a/src/dev/typescript/build_refs.ts b/src/dev/typescript/build_refs.ts index cbb596c185f8b..de006bd674e87 100644 --- a/src/dev/typescript/build_refs.ts +++ b/src/dev/typescript/build_refs.ts @@ -18,12 +18,18 @@ */ import execa from 'execa'; +import Path from 'path'; import { run, ToolingLog } from '@kbn/dev-utils'; -export async function buildRefs(log: ToolingLog) { +export async function buildAllRefs(log: ToolingLog) { + await buildRefs(log, 'tsconfig.refs.json'); + await buildRefs(log, Path.join('x-pack', 'tsconfig.refs.json')); +} + +async function buildRefs(log: ToolingLog, projectPath: string) { try { - log.info('Building TypeScript projects refs...'); - await execa(require.resolve('typescript/bin/tsc'), ['-b', 'tsconfig.refs.json']); + log.debug(`Building TypeScript projects refs for ${projectPath}...`); + await execa(require.resolve('typescript/bin/tsc'), ['-b', projectPath]); } catch (e) { log.error(e); process.exit(1); @@ -31,7 +37,18 @@ export async function buildRefs(log: ToolingLog) { } export async function runBuildRefs() { - run(async ({ log }) => { - await buildRefs(log); - }); + run( + async ({ log, flags }) => { + await buildRefs(log, flags.project as string); + }, + { + description: 'Build TypeScript projects', + flags: { + string: ['project'], + help: ` +--project Required, path to the tsconfig.refs.file + `, + }, + } + ); } diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 4d1e549e192b6..9891e9fa02c82 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -24,11 +24,11 @@ import { Project } from './project'; export const PROJECTS = [ new Project(resolve(REPO_ROOT, 'tsconfig.json')), - new Project(resolve(REPO_ROOT, 'src/test_utils/tsconfig.json')), - new Project(resolve(REPO_ROOT, 'src/core/tsconfig.json')), new Project(resolve(REPO_ROOT, 'test/tsconfig.json'), { name: 'kibana/test' }), new Project(resolve(REPO_ROOT, 'x-pack/tsconfig.json')), new Project(resolve(REPO_ROOT, 'x-pack/test/tsconfig.json'), { name: 'x-pack/test' }), + new Project(resolve(REPO_ROOT, 'src/test_utils/tsconfig.json')), + new Project(resolve(REPO_ROOT, 'src/core/tsconfig.json')), new Project(resolve(REPO_ROOT, 'x-pack/plugins/security_solution/cypress/tsconfig.json'), { name: 'security_solution/cypress', }), @@ -47,6 +47,12 @@ export const PROJECTS = [ ...glob .sync('packages/*/tsconfig.json', { cwd: REPO_ROOT }) .map((path) => new Project(resolve(REPO_ROOT, path))), + ...glob + .sync('src/plugins/*/tsconfig.json', { cwd: REPO_ROOT }) + .map((path) => new Project(resolve(REPO_ROOT, path))), + ...glob + .sync('x-pack/plugins/*/tsconfig.json', { cwd: REPO_ROOT }) + .map((path) => new Project(resolve(REPO_ROOT, path))), ...glob .sync('examples/*/tsconfig.json', { cwd: REPO_ROOT }) .map((path) => new Project(resolve(REPO_ROOT, path))), diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index 00968b7259a30..c52b9454c28be 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -24,7 +24,7 @@ import getopts from 'getopts'; import { execInProjects } from './exec_in_projects'; import { filterProjectsByFlag } from './projects'; -import { buildRefs } from './build_refs'; +import { buildAllRefs } from './build_refs'; export async function runTypeCheckCli() { const extraFlags: string[] = []; @@ -80,7 +80,7 @@ export async function runTypeCheckCli() { process.exit(); } - await buildRefs(log); + await buildAllRefs(log); const tscArgs = [ // composite project cannot be used with --noEmit diff --git a/src/fixtures/stubbed_logstash_index_pattern.js b/src/fixtures/stubbed_logstash_index_pattern.js index 5735b01eb3db4..5d6d254b9a000 100644 --- a/src/fixtures/stubbed_logstash_index_pattern.js +++ b/src/fixtures/stubbed_logstash_index_pattern.js @@ -17,10 +17,10 @@ * under the License. */ -import StubIndexPattern from 'test_utils/stub_index_pattern'; import stubbedLogstashFields from 'fixtures/logstash_fields'; import { getKbnFieldType } from '../plugins/data/common'; +import { getStubIndexPattern } from '../plugins/data/public/test_utils'; import { uiSettingsServiceMock } from '../core/public/ui_settings/ui_settings_service.mock'; const uiSettingSetupMock = uiSettingsServiceMock.createSetupContract(); @@ -46,7 +46,7 @@ export default function stubbedLogstashIndexPatternService() { }; }); - const indexPattern = new StubIndexPattern('logstash-*', (cfg) => cfg, 'time', fields, { + const indexPattern = getStubIndexPattern('logstash-*', (cfg) => cfg, 'time', fields, { uiSettings: uiSettingSetupMock, }); diff --git a/src/fixtures/stubbed_saved_object_index_pattern.ts b/src/fixtures/stubbed_saved_object_index_pattern.ts index 44b391f14cf9c..261e451db5452 100644 --- a/src/fixtures/stubbed_saved_object_index_pattern.ts +++ b/src/fixtures/stubbed_saved_object_index_pattern.ts @@ -28,10 +28,10 @@ export function stubbedSavedObjectIndexPattern(id: string | null = null) { type: 'index-pattern', attributes: { timeFieldName: 'timestamp', - customFormats: '{}', + customFormats: {}, fields: mockLogstashFields, title: 'title', }, - version: 2, + version: '2', }; } diff --git a/src/fixtures/telemetry_collectors/constants.ts b/src/fixtures/telemetry_collectors/constants.ts index 4aac9e66cdbdb..60df05bac2aeb 100644 --- a/src/fixtures/telemetry_collectors/constants.ts +++ b/src/fixtures/telemetry_collectors/constants.ts @@ -51,3 +51,18 @@ export const externallyDefinedSchema: MakeSchemaFrom<{ locale: string }> = { type: 'keyword', }, }; + +export type TypeAliasWithUnion = Usage & WithUnion; + +export type TypeAliasWithRecord = Usage & Record; + +export type MappedTypeProps = 'prop1' | 'prop2'; + +export interface MappedTypes { + mappedTypeWithExternallyDefinedProps: { + [key in MappedTypeProps]: number; + }; + mappedTypeWithOneInlineProp: { + [key in 'prop3']: number; + }; +} diff --git a/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts b/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts index 0ec8d2e15c34a..b925696c96563 100644 --- a/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts +++ b/src/fixtures/telemetry_collectors/indexed_interface_with_not_matching_schema.ts @@ -41,8 +41,9 @@ export const myCollector = makeUsageCollector({ return { something: { count_2: 2 } }; }, schema: { + // @ts-expect-error Intentionally missing count_2 something: { - count_1: { type: 'long' }, // Intentionally missing count_2 + count_1: { type: 'long' }, }, }, }); diff --git a/src/fixtures/telemetry_collectors/schema_defined_with_spreads_collector.ts b/src/fixtures/telemetry_collectors/schema_defined_with_spreads_collector.ts new file mode 100644 index 0000000000000..af9fef0bbd297 --- /dev/null +++ b/src/fixtures/telemetry_collectors/schema_defined_with_spreads_collector.ts @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { CollectorSet, MakeSchemaFrom } from '../../plugins/usage_collection/server/collector'; +import { loggerMock } from '../../core/server/logging/logger.mock'; + +const { makeUsageCollector } = new CollectorSet({ + logger: loggerMock.create(), + maximumWaitTimeForAllCollectorsInS: 0, +}); + +interface MyObject { + total: number; + type: boolean; +} + +interface Usage { + flat?: string; + my_str?: string; + my_objects: MyObject; +} + +const SOME_NUMBER: number = 123; + +const someSchema: MakeSchemaFrom> = { + flat: { + type: 'keyword', + }, + my_str: { + type: 'text', + }, +}; + +const someOtherSchema: MakeSchemaFrom> = { + my_objects: { + total: { + type: 'number', + }, + type: { type: 'boolean' }, + }, +}; + +export const myCollector = makeUsageCollector({ + type: 'schema_defined_with_spreads', + isReady: () => true, + fetch() { + const testString = '123'; + + return { + flat: 'hello', + my_str: testString, + my_objects: { + total: SOME_NUMBER, + type: true, + }, + }; + }, + schema: { + ...someSchema, + ...someOtherSchema, + }, +}); diff --git a/src/fixtures/telemetry_collectors/working_collector.ts b/src/fixtures/telemetry_collectors/working_collector.ts index bdf10b5e54919..0a3bf49638a7b 100644 --- a/src/fixtures/telemetry_collectors/working_collector.ts +++ b/src/fixtures/telemetry_collectors/working_collector.ts @@ -90,12 +90,15 @@ export const myCollector = makeUsageCollector({ type: { type: 'boolean' }, }, my_array: { - total: { - type: 'number', + type: 'array', + items: { + total: { + type: 'number', + }, + type: { type: 'boolean' }, }, - type: { type: 'boolean' }, }, - my_str_array: { type: 'keyword' }, + my_str_array: { type: 'array', items: { type: 'keyword' } }, my_index_signature_prop: { count: { type: 'number' }, avg: { type: 'number' }, diff --git a/src/legacy/plugin_discovery/README.md b/src/legacy/plugin_discovery/README.md deleted file mode 100644 index 83e7c10d16fff..0000000000000 --- a/src/legacy/plugin_discovery/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# Plugin Discovery - -The plugin discovery module defines the core plugin loading logic used by the Kibana server. It exports functions for - - -## `findPluginSpecs(settings, [config])` - -Finds [`PluginSpec`][PluginSpec] objects - -### params - - `settings`: the same settings object accepted by [`KbnServer`][KbnServer] - - `[config]`: Optional - a [`Config`][Config] service. Using this param causes `findPluginSpecs()` to modify `config`'s schema to support the configuration for each discovered [`PluginSpec`][PluginSpec]. If you can, please use the [`Config`][Config] service produced by `extendedConfig$` rather than passing in an existing service so that `findPluginSpecs()` is side-effect free. - -### return value - -`findPluginSpecs()` returns an object of Observables which produce values at different parts of the process. Since the Observables are all aware of their own dependencies you can subscribe to any combination (within the same tick) and only the necessary plugin logic will be executed. - -If you *never* subscribe to any of the Observables then plugin discovery won't actually run. - - - `pack$`: emits every [`PluginPack`][PluginPack] found - - `invalidDirectoryError$: Observable`: emits [`InvalidDirectoryError`][Errors]s caused by `settings.plugins.scanDirs` values that don't point to actual directories. `findPluginSpecs()` will not abort when this error is encountered. - - `invalidPackError$: Observable`: emits [`InvalidPackError`][Errors]s caused by children of `settings.plugins.scanDirs` or `settings.plugins.paths` values which don't meet the requirements of a [`PluginPack`][PluginPack] (probably missing a `package.json`). `findPluginSpecs()` will not abort when this error is encountered. - - `deprecation$: Observable`: emits deprecation warnings that are produces when reading each [`PluginPack`][PluginPack]'s configuration - - `extendedConfig$: Observable`: emits the [`Config`][Config] service that was passed to `findPluginSpecs()` (or created internally if none was passed) after it has been extended with the configuration from each plugin - - `spec$: Observable`: emits every *enabled* [`PluginSpec`][PluginSpec] defined by the discovered [`PluginPack`][PluginPack]s - - `disabledSpec$: Observable`: emits every *disabled* [`PluginSpec`][PluginSpec] defined by the discovered [`PluginPack`][PluginPack]s - - `invalidVersionSpec$: Observable`: emits every [`PluginSpec`][PluginSpec] who's required kibana version does not match the version exposed by `config.get('pkg.version')` - -### example - -Just get the plugin specs, only fail if there is an uncaught error of some sort: -```js -const { pack$ } = findPluginSpecs(settings); -const packs = await pack$.pipe(toArray()).toPromise() -``` - -Just log the deprecation messages: -```js -const { deprecation$ } = findPluginSpecs(settings); -for (const warning of await deprecation$.pipe(toArray()).toPromise()) { - console.log('DEPRECATION:', warning) -} -``` - -Get the packs but fail if any packs are invalid: -```js -const { pack$, invalidDirectoryError$ } = findPluginSpecs(settings); -const packs = await Rx.merge( - pack$.pipe(toArray()), - - // if we ever get an InvalidDirectoryError, throw it - // into the stream so that all streams are unsubscribed, - // the discovery process is aborted, and the promise rejects - invalidDirectoryError$.pipe( - map(error => { throw error }) - ), -).toPromise() -``` - -Handle everything -```js -const { - pack$, - invalidDirectoryError$, - invalidPackError$, - deprecation$, - extendedConfig$, - spec$, - disabledSpecs$, - invalidVersionSpec$, -} = findPluginSpecs(settings); - -Rx.merge( - pack$.pipe( - tap(pluginPack => console.log('Found plugin pack', pluginPack)) - ), - - invalidDirectoryError$.pipe( - tap(error => console.log('Invalid directory error', error)) - ), - - invalidPackError$.pipe( - tap(error => console.log('Invalid plugin pack error', error)) - ), - - deprecation$.pipe( - tap(msg => console.log('DEPRECATION:', msg)) - ), - - extendedConfig$.pipe( - tap(config => console.log('config service extended by plugins', config)) - ), - - spec$.pipe( - tap(pluginSpec => console.log('enabled plugin spec found', spec)) - ), - - disabledSpec$.pipe( - tap(pluginSpec => console.log('disabled plugin spec found', spec)) - ), - - invalidVersionSpec$.pipe( - tap(pluginSpec => console.log('plugin spec with invalid version found', spec)) - ), -) -.toPromise() -.then(() => { - console.log('plugin discovery complete') -}) -.catch((error) => { - console.log('plugin discovery failed', error) -}) - -``` - -## `reduceExportSpecs(pluginSpecs, reducers, [defaults={}])` - -Reduces every value exported by the [`PluginSpec`][PluginSpec]s to produce a single value. If an exported value is an array each item in the array will be reduced individually. If the exported value is `undefined` it will be ignored. The reducer is called with the signature: - -```js -reducer( - // the result of the previous reducer call, or `defaults` - acc: any, - // the exported value, found at `uiExports[type]` or `uiExports[type][i]` - // in the PluginSpec config. - spec: any, - // the key in `uiExports` where this export was found - type: string, - // the PluginSpec which exported this spec - pluginSpec: PluginSpec -) -``` - -## `new PluginPack(options)` class - -Only exported so that `PluginPack` instances can be created in tests and used in place of on-disk plugin fixtures. Use `findPluginSpecs()`, or the cached result of a call to `findPluginSpecs()` (like `kbnServer.pluginSpecs`) any time you might need access to `PluginPack` objects in distributed code. - -### params - - - `options.path`: absolute path to where this plugin pack was found, this is normally a direct child of `./src/legacy/core_plugins` or `./plugins` - - `options.pkg`: the parsed `package.json` for this pack, used for defaults in `PluginSpec` objects defined by this pack - - `options.provider`: the default export of the pack, a function which is called with the `PluginSpec` class which should return one or more `PluginSpec` objects. - -[PluginPack]: ./plugin_pack/plugin_pack.js "PluginPath class definition" -[PluginSpec]: ./plugin_spec/plugin_spec.js "PluginSpec class definition" -[Errors]: ./errors.js "PluginDiscover specific error types" -[KbnServer]: ../server/kbn_server.js "KbnServer class definition" -[Config]: ../server/config/config.js "KbnServer/Config class definition" diff --git a/src/legacy/plugin_discovery/__tests__/find_plugin_specs.js b/src/legacy/plugin_discovery/__tests__/find_plugin_specs.js deleted file mode 100644 index e6af23d69c549..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/find_plugin_specs.js +++ /dev/null @@ -1,219 +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 { resolve } from 'path'; -import { toArray } from 'rxjs/operators'; - -import expect from '@kbn/expect'; -import { isEqual } from 'lodash'; -import { findPluginSpecs } from '../find_plugin_specs'; -import { PluginSpec } from '../plugin_spec'; - -const PLUGIN_FIXTURES = resolve(__dirname, 'fixtures/plugins'); -const CONFLICT_FIXTURES = resolve(__dirname, 'fixtures/conflicts'); - -describe('plugin discovery', () => { - describe('findPluginSpecs()', function () { - this.timeout(10000); - - describe('spec$', () => { - it('finds specs for specified plugin paths', async () => { - const { spec$ } = findPluginSpecs({ - plugins: { - paths: [ - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'broken'), - ], - }, - }); - - const specs = await spec$.pipe(toArray()).toPromise(); - expect(specs).to.have.length(3); - specs.forEach((spec) => { - expect(spec).to.be.a(PluginSpec); - }); - expect(specs.map((s) => s.getId()).sort()).to.eql(['bar:one', 'bar:two', 'foo']); - }); - - it('finds all specs in scanDirs', async () => { - const { spec$ } = findPluginSpecs({ - // used to ensure the dev_mode plugin is enabled - env: 'development', - - plugins: { - scanDirs: [PLUGIN_FIXTURES], - }, - }); - - const specs = await spec$.pipe(toArray()).toPromise(); - expect(specs).to.have.length(3); - specs.forEach((spec) => { - expect(spec).to.be.a(PluginSpec); - }); - expect(specs.map((s) => s.getId()).sort()).to.eql(['bar:one', 'bar:two', 'foo']); - }); - - it('does not find disabled plugins', async () => { - const { spec$ } = findPluginSpecs({ - 'bar:one': { - enabled: false, - }, - - plugins: { - paths: [ - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'broken'), - ], - }, - }); - - const specs = await spec$.pipe(toArray()).toPromise(); - expect(specs).to.have.length(2); - specs.forEach((spec) => { - expect(spec).to.be.a(PluginSpec); - }); - expect(specs.map((s) => s.getId()).sort()).to.eql(['bar:two', 'foo']); - }); - - it('dedupes duplicate packs', async () => { - const { spec$ } = findPluginSpecs({ - plugins: { - scanDirs: [PLUGIN_FIXTURES], - paths: [ - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'broken'), - resolve(PLUGIN_FIXTURES, 'broken'), - ], - }, - }); - - const specs = await spec$.pipe(toArray()).toPromise(); - expect(specs).to.have.length(3); - specs.forEach((spec) => { - expect(spec).to.be.a(PluginSpec); - }); - expect(specs.map((s) => s.getId()).sort()).to.eql(['bar:one', 'bar:two', 'foo']); - }); - - describe('conflicting plugin spec ids', () => { - it('fails with informative message', async () => { - const { spec$ } = findPluginSpecs({ - plugins: { - scanDirs: [], - paths: [resolve(CONFLICT_FIXTURES, 'foo')], - }, - }); - - try { - await spec$.pipe(toArray()).toPromise(); - throw new Error('expected spec$ to throw an error'); - } catch (error) { - expect(error.message).to.contain('Multiple plugins found with the id "foo"'); - expect(error.message).to.contain(CONFLICT_FIXTURES); - } - }); - }); - }); - - describe('packageJson$', () => { - const checkPackageJsons = (packageJsons) => { - expect(packageJsons).to.have.length(2); - const package1 = packageJsons.find((packageJson) => - isEqual( - { - directoryPath: resolve(PLUGIN_FIXTURES, 'foo'), - contents: { - name: 'foo', - version: 'kibana', - }, - }, - packageJson - ) - ); - expect(package1).to.be.an(Object); - const package2 = packageJsons.find((packageJson) => - isEqual( - { - directoryPath: resolve(PLUGIN_FIXTURES, 'bar'), - contents: { - name: 'foo', - version: 'kibana', - }, - }, - packageJson - ) - ); - expect(package2).to.be.an(Object); - }; - - it('finds packageJson for specified plugin paths', async () => { - const { packageJson$ } = findPluginSpecs({ - plugins: { - paths: [ - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'broken'), - ], - }, - }); - - const packageJsons = await packageJson$.pipe(toArray()).toPromise(); - checkPackageJsons(packageJsons); - }); - - it('finds all packageJsons in scanDirs', async () => { - const { packageJson$ } = findPluginSpecs({ - // used to ensure the dev_mode plugin is enabled - env: 'development', - - plugins: { - scanDirs: [PLUGIN_FIXTURES], - }, - }); - - const packageJsons = await packageJson$.pipe(toArray()).toPromise(); - checkPackageJsons(packageJsons); - }); - - it('dedupes duplicate packageJson', async () => { - const { packageJson$ } = findPluginSpecs({ - plugins: { - scanDirs: [PLUGIN_FIXTURES], - paths: [ - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'foo'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'bar'), - resolve(PLUGIN_FIXTURES, 'broken'), - resolve(PLUGIN_FIXTURES, 'broken'), - ], - }, - }); - - const packageJsons = await packageJson$.pipe(toArray()).toPromise(); - checkPackageJsons(packageJsons); - }); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/index.js b/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/index.js deleted file mode 100644 index fcbe3487463b7..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/index.js +++ /dev/null @@ -1,27 +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 default function (kibana) { - return [ - // two plugins exported without ids will both inherit - // the id of the pack and conflict - new kibana.Plugin({}), - new kibana.Plugin({}), - ]; -} diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/package.json b/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/conflicts/foo/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/index.js b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/index.js deleted file mode 100644 index 0eef126f2255a..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/index.js +++ /dev/null @@ -1,29 +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 default function (kibana) { - return [ - new kibana.Plugin({ - id: 'bar:one', - }), - new kibana.Plugin({ - id: 'bar:two', - }), - ]; -} diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/package.json b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/bar/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/index.js b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/index.js deleted file mode 100644 index 59f4a2649f019..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/index.js +++ /dev/null @@ -1,22 +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 default { - foo: 'bar', -}; diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/package1.json b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/package1.json deleted file mode 100644 index 81ddb6221d515..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/broken/package1.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "baz", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/index.js b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/index.js deleted file mode 100644 index e43a1dcedb372..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -module.exports = function (kibana) { - return new kibana.Plugin({ - id: 'foo', - }); -}; diff --git a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/package.json b/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/__tests__/fixtures/plugins/foo/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/errors.js b/src/legacy/plugin_discovery/errors.js deleted file mode 100644 index 02d81b32d1fd1..0000000000000 --- a/src/legacy/plugin_discovery/errors.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const errorCodeProperty = Symbol('pluginDiscovery/errorCode'); - -/** - * Thrown when reading a plugin directory fails, wraps failure - * @type {String} - */ -const ERROR_INVALID_DIRECTORY = 'ERROR_INVALID_DIRECTORY'; -export function createInvalidDirectoryError(sourceError, path) { - sourceError[errorCodeProperty] = ERROR_INVALID_DIRECTORY; - sourceError.path = path; - return sourceError; -} -export function isInvalidDirectoryError(error) { - return error && error[errorCodeProperty] === ERROR_INVALID_DIRECTORY; -} - -/** - * Thrown when trying to create a PluginPack for a path that - * is not a valid plugin definition - * @type {String} - */ -const ERROR_INVALID_PACK = 'ERROR_INVALID_PACK'; -export function createInvalidPackError(path, reason) { - const error = new Error(`PluginPack${path ? ` at "${path}"` : ''} ${reason}`); - error[errorCodeProperty] = ERROR_INVALID_PACK; - error.path = path; - return error; -} -export function isInvalidPackError(error) { - return error && error[errorCodeProperty] === ERROR_INVALID_PACK; -} - -/** - * Thrown when trying to load a PluginSpec that is invalid for some reason - * @type {String} - */ -const ERROR_INVALID_PLUGIN = 'ERROR_INVALID_PLUGIN'; -export function createInvalidPluginError(spec, reason) { - const error = new Error( - `Plugin from ${spec.getId()} at ${spec.getPack().getPath()} is invalid because ${reason}` - ); - error[errorCodeProperty] = ERROR_INVALID_PLUGIN; - error.spec = spec; - return error; -} -export function isInvalidPluginError(error) { - return error && error[errorCodeProperty] === ERROR_INVALID_PLUGIN; -} - -/** - * Thrown when trying to load a PluginSpec whose version is incompatible - * @type {String} - */ -const ERROR_INCOMPATIBLE_PLUGIN_VERSION = 'ERROR_INCOMPATIBLE_PLUGIN_VERSION'; -export function createIncompatiblePluginVersionError(spec) { - const error = new Error( - `Plugin ${spec.getId()} is only compatible with Kibana version ${spec.getExpectedKibanaVersion()}` - ); - error[errorCodeProperty] = ERROR_INCOMPATIBLE_PLUGIN_VERSION; - error.spec = spec; - return error; -} -export function isIncompatiblePluginVersionError(error) { - return error && error[errorCodeProperty] === ERROR_INCOMPATIBLE_PLUGIN_VERSION; -} diff --git a/src/legacy/plugin_discovery/find_plugin_specs.js b/src/legacy/plugin_discovery/find_plugin_specs.js deleted file mode 100644 index b97476bb456a5..0000000000000 --- a/src/legacy/plugin_discovery/find_plugin_specs.js +++ /dev/null @@ -1,234 +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 * as Rx from 'rxjs'; -import { - distinct, - toArray, - mergeMap, - share, - shareReplay, - filter, - last, - map, - tap, -} from 'rxjs/operators'; -import { realpathSync } from 'fs'; - -import { Config } from '../server/config'; - -import { extendConfigService, disableConfigExtension } from './plugin_config'; - -import { - createPack$, - createPackageJsonAtPath$, - createPackageJsonsInDirectory$, -} from './plugin_pack'; - -import { isInvalidDirectoryError, isInvalidPackError } from './errors'; - -export function defaultConfig(settings) { - return Config.withDefaultSchema(settings); -} - -function bufferAllResults(observable) { - return observable.pipe( - // buffer all results into a single array - toArray(), - // merge the array back into the stream when complete - mergeMap((array) => array) - ); -} - -/** - * Determine a distinct value for each result from find$ - * so they can be deduplicated - * @param {{error?,pack?}} result - * @return {Any} - */ -function getDistinctKeyForFindResult(result) { - // errors are distinct by their message - if (result.error) { - return result.error.message; - } - - // packs are distinct by their absolute and real path - if (result.packageJson) { - return realpathSync(result.packageJson.directoryPath); - } - - // non error/pack results shouldn't exist, but if they do they are all unique - return result; -} - -function groupSpecsById(specs) { - const specsById = new Map(); - for (const spec of specs) { - const id = spec.getId(); - if (specsById.has(id)) { - specsById.get(id).push(spec); - } else { - specsById.set(id, [spec]); - } - } - return specsById; -} - -/** - * Creates a collection of observables for discovering pluginSpecs - * using Kibana's defaults, settings, and config service - * - * @param {Object} settings - * @param {ConfigService} [configToMutate] when supplied **it is mutated** to - * include the config from discovered plugin specs - * @return {Object} - */ -export function findPluginSpecs(settings, configToMutate) { - const config$ = Rx.defer(async () => { - if (configToMutate) { - return configToMutate; - } - - return defaultConfig(settings); - }).pipe(shareReplay()); - - // find plugin packs in configured paths/dirs - const packageJson$ = config$.pipe( - mergeMap((config) => - Rx.merge( - ...config.get('plugins.paths').map(createPackageJsonAtPath$), - ...config.get('plugins.scanDirs').map(createPackageJsonsInDirectory$) - ) - ), - distinct(getDistinctKeyForFindResult), - share() - ); - - const pack$ = createPack$(packageJson$).pipe(share()); - - const extendConfig$ = config$.pipe( - mergeMap((config) => - pack$.pipe( - // get the specs for each found plugin pack - mergeMap(({ pack }) => (pack ? pack.getPluginSpecs() : [])), - // make sure that none of the plugin specs have conflicting ids, fail - // early if conflicts detected or merge the specs back into the stream - toArray(), - mergeMap((allSpecs) => { - for (const [id, specs] of groupSpecsById(allSpecs)) { - if (specs.length > 1) { - throw new Error( - `Multiple plugins found with the id "${id}":\n${specs - .map((spec) => ` - ${id} at ${spec.getPath()}`) - .join('\n')}` - ); - } - } - - return allSpecs; - }), - mergeMap(async (spec) => { - // extend the config service with this plugin spec and - // collect its deprecations messages if some of its - // settings are outdated - const deprecations = []; - await extendConfigService(spec, config, settings, (message) => { - deprecations.push({ spec, message }); - }); - - return { - spec, - deprecations, - }; - }), - // extend the config with all plugins before determining enabled status - bufferAllResults, - map(({ spec, deprecations }) => { - const isRightVersion = spec.isVersionCompatible(config.get('pkg.version')); - const enabled = isRightVersion && spec.isEnabled(config); - return { - config, - spec, - deprecations, - enabledSpecs: enabled ? [spec] : [], - disabledSpecs: enabled ? [] : [spec], - invalidVersionSpecs: isRightVersion ? [] : [spec], - }; - }), - // determine which plugins are disabled before actually removing things from the config - bufferAllResults, - tap((result) => { - for (const spec of result.disabledSpecs) { - disableConfigExtension(spec, config); - } - }) - ) - ), - share() - ); - - return { - // package JSONs found when searching configure paths - packageJson$: packageJson$.pipe( - mergeMap((result) => (result.packageJson ? [result.packageJson] : [])) - ), - - // plugin packs found when searching configured paths - pack$: pack$.pipe(mergeMap((result) => (result.pack ? [result.pack] : []))), - - // errors caused by invalid directories of plugin directories - invalidDirectoryError$: pack$.pipe( - mergeMap((result) => (isInvalidDirectoryError(result.error) ? [result.error] : [])) - ), - - // errors caused by directories that we expected to be plugin but were invalid - invalidPackError$: pack$.pipe( - mergeMap((result) => (isInvalidPackError(result.error) ? [result.error] : [])) - ), - - otherError$: pack$.pipe( - mergeMap((result) => (isUnhandledError(result.error) ? [result.error] : [])) - ), - - // { spec, message } objects produced when transforming deprecated - // settings for a plugin spec - deprecation$: extendConfig$.pipe(mergeMap((result) => result.deprecations)), - - // the config service we extended with all of the plugin specs, - // only emitted once it is fully extended by all - extendedConfig$: extendConfig$.pipe( - mergeMap((result) => result.config), - filter(Boolean), - last() - ), - - // all enabled PluginSpec objects - spec$: extendConfig$.pipe(mergeMap((result) => result.enabledSpecs)), - - // all disabled PluginSpec objects - disabledSpec$: extendConfig$.pipe(mergeMap((result) => result.disabledSpecs)), - - // all PluginSpec objects that were disabled because their version was incompatible - invalidVersionSpec$: extendConfig$.pipe(mergeMap((result) => result.invalidVersionSpecs)), - }; -} - -function isUnhandledError(error) { - return error != null && !isInvalidDirectoryError(error) && !isInvalidPackError(error); -} diff --git a/src/legacy/plugin_discovery/index.js b/src/legacy/plugin_discovery/index.js deleted file mode 100644 index b60806f6cbc23..0000000000000 --- a/src/legacy/plugin_discovery/index.js +++ /dev/null @@ -1,22 +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 { findPluginSpecs } from './find_plugin_specs'; -export { reduceExportSpecs } from './plugin_exports'; -export { PluginPack } from './plugin_pack'; diff --git a/src/legacy/plugin_discovery/plugin_config/__tests__/extend_config_service.js b/src/legacy/plugin_discovery/plugin_config/__tests__/extend_config_service.js deleted file mode 100644 index 40f84f6f54b3b..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/__tests__/extend_config_service.js +++ /dev/null @@ -1,162 +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 sinon from 'sinon'; -import expect from '@kbn/expect'; - -import { Config } from '../../../server/config'; -import { PluginPack } from '../../plugin_pack'; -import { extendConfigService, disableConfigExtension } from '../extend_config_service'; -import * as SchemaNS from '../schema'; -import * as SettingsNS from '../settings'; - -describe('plugin discovery/extend config service', () => { - const sandbox = sinon.createSandbox(); - afterEach(() => sandbox.restore()); - - const pluginSpec = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'test', - version: 'kibana', - }, - provider: ({ Plugin }) => - new Plugin({ - configPrefix: 'foo.bar.baz', - - config: (Joi) => - Joi.object({ - enabled: Joi.boolean().default(true), - test: Joi.string().default('bonk'), - }).default(), - }), - }) - .getPluginSpecs() - .pop(); - - describe('extendConfigService()', () => { - it('calls getSettings, getSchema, and Config.extendSchema() correctly', async () => { - const rootSettings = { - foo: { - bar: { - enabled: false, - }, - }, - }; - const schema = { - validate: () => {}, - }; - const configPrefix = 'foo.bar'; - const config = { - extendSchema: sandbox.stub(), - }; - const pluginSpec = { - getConfigPrefix: sandbox.stub().returns(configPrefix), - }; - - const getSettings = sandbox.stub(SettingsNS, 'getSettings').returns(rootSettings.foo.bar); - - const getSchema = sandbox.stub(SchemaNS, 'getSchema').returns(schema); - - await extendConfigService(pluginSpec, config, rootSettings); - - sinon.assert.calledOnce(getSettings); - sinon.assert.calledWithExactly(getSettings, pluginSpec, rootSettings); - - sinon.assert.calledOnce(getSchema); - sinon.assert.calledWithExactly(getSchema, pluginSpec); - - sinon.assert.calledOnce(config.extendSchema); - sinon.assert.calledWithExactly( - config.extendSchema, - schema, - rootSettings.foo.bar, - configPrefix - ); - }); - - it('adds the schema for a plugin spec to its config prefix', async () => { - const config = Config.withDefaultSchema(); - expect(config.has('foo.bar.baz')).to.be(false); - await extendConfigService(pluginSpec, config); - expect(config.has('foo.bar.baz')).to.be(true); - }); - - it('initializes it with the default settings', async () => { - const config = Config.withDefaultSchema(); - await extendConfigService(pluginSpec, config); - expect(config.get('foo.bar.baz.enabled')).to.be(true); - expect(config.get('foo.bar.baz.test')).to.be('bonk'); - }); - - it('initializes it with values from root settings if defined', async () => { - const config = Config.withDefaultSchema(); - await extendConfigService(pluginSpec, config, { - foo: { - bar: { - baz: { - test: 'hello world', - }, - }, - }, - }); - - expect(config.get('foo.bar.baz.test')).to.be('hello world'); - }); - - it('throws if root settings are invalid', async () => { - const config = Config.withDefaultSchema(); - try { - await extendConfigService(pluginSpec, config, { - foo: { - bar: { - baz: { - test: { - 'not a string': true, - }, - }, - }, - }, - }); - throw new Error('Expected extendConfigService() to throw because of bad settings'); - } catch (error) { - expect(error.message).to.contain('"test" must be a string'); - } - }); - }); - - describe('disableConfigExtension()', () => { - it('removes added config', async () => { - const config = Config.withDefaultSchema(); - await extendConfigService(pluginSpec, config); - expect(config.has('foo.bar.baz.test')).to.be(true); - await disableConfigExtension(pluginSpec, config); - expect(config.has('foo.bar.baz.test')).to.be(false); - }); - - it('leaves {configPrefix}.enabled config', async () => { - const config = Config.withDefaultSchema(); - expect(config.has('foo.bar.baz.enabled')).to.be(false); - await extendConfigService(pluginSpec, config); - expect(config.get('foo.bar.baz.enabled')).to.be(true); - await disableConfigExtension(pluginSpec, config); - expect(config.get('foo.bar.baz.enabled')).to.be(false); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_config/__tests__/schema.js b/src/legacy/plugin_discovery/plugin_config/__tests__/schema.js deleted file mode 100644 index 78adb1e680e20..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/__tests__/schema.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 expect from '@kbn/expect'; - -import { PluginPack } from '../../plugin_pack'; -import { getSchema, getStubSchema } from '../schema'; - -describe('plugin discovery/schema', () => { - function createPluginSpec(configProvider) { - return new PluginPack({ - path: '/dev/null', - pkg: { - name: 'test', - version: 'kibana', - }, - provider: ({ Plugin }) => - new Plugin({ - configPrefix: 'foo.bar.baz', - config: configProvider, - }), - }) - .getPluginSpecs() - .pop(); - } - - describe('getSchema()', () => { - it('calls the config provider and returns its return value', async () => { - const pluginSpec = createPluginSpec(() => 'foo'); - expect(await getSchema(pluginSpec)).to.be('foo'); - }); - - it('supports config provider that returns a promise', async () => { - const pluginSpec = createPluginSpec(() => Promise.resolve('foo')); - expect(await getSchema(pluginSpec)).to.be('foo'); - }); - - it('uses default schema when no config provider', async () => { - const schema = await getSchema(createPluginSpec()); - expect(schema).to.be.an('object'); - expect(schema).to.have.property('validate').a('function'); - expect(schema.validate({}).value).to.eql({ - enabled: true, - }); - }); - - it('uses default schema when config returns falsy value', async () => { - const schema = await getSchema(createPluginSpec(() => null)); - expect(schema).to.be.an('object'); - expect(schema).to.have.property('validate').a('function'); - expect(schema.validate({}).value).to.eql({ - enabled: true, - }); - }); - - it('uses default schema when config promise resolves to falsy value', async () => { - const schema = await getSchema(createPluginSpec(() => Promise.resolve(null))); - expect(schema).to.be.an('object'); - expect(schema).to.have.property('validate').a('function'); - expect(schema.validate({}).value).to.eql({ - enabled: true, - }); - }); - }); - - describe('getStubSchema()', () => { - it('returns schema with enabled: false', async () => { - const schema = await getStubSchema(); - expect(schema).to.be.an('object'); - expect(schema).to.have.property('validate').a('function'); - expect(schema.validate({}).value).to.eql({ - enabled: false, - }); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_config/__tests__/settings.js b/src/legacy/plugin_discovery/plugin_config/__tests__/settings.js deleted file mode 100644 index 750c5ee6c6f50..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/__tests__/settings.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 expect from '@kbn/expect'; - -import { PluginPack } from '../../plugin_pack'; -import { getSettings } from '../settings'; - -describe('plugin_discovery/settings', () => { - const pluginSpec = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'test', - version: 'kibana', - }, - provider: ({ Plugin }) => - new Plugin({ - configPrefix: 'a.b.c', - }), - }) - .getPluginSpecs() - .pop(); - - describe('getSettings()', () => { - it('reads settings from config prefix', async () => { - const rootSettings = { - a: { - b: { - c: { - enabled: false, - }, - }, - }, - }; - - expect(await getSettings(pluginSpec, rootSettings)).to.eql({ - enabled: false, - }); - }); - - it('allows rootSettings to be undefined', async () => { - expect(await getSettings(pluginSpec)).to.eql(undefined); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_config/extend_config_service.js b/src/legacy/plugin_discovery/plugin_config/extend_config_service.js deleted file mode 100644 index a6d5d4ae5f990..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/extend_config_service.js +++ /dev/null @@ -1,50 +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 { getSettings } from './settings'; -import { getSchema, getStubSchema } from './schema'; - -/** - * Extend a config service with the schema and settings for a - * plugin spec and optionally call logDeprecation with warning - * messages about deprecated settings that are used - * @param {PluginSpec} spec - * @param {Server.Config} config - * @param {Object} rootSettings - * @param {Function} [logDeprecation] - * @return {Promise} - */ -export async function extendConfigService(spec, config, rootSettings) { - const settings = await getSettings(spec, rootSettings); - const schema = await getSchema(spec); - config.extendSchema(schema, settings, spec.getConfigPrefix()); -} - -/** - * Disable the schema and settings applied to a config service for - * a plugin spec - * @param {PluginSpec} spec - * @param {Server.Config} config - * @return {undefined} - */ -export function disableConfigExtension(spec, config) { - const prefix = spec.getConfigPrefix(); - config.removeSchema(prefix); - config.extendSchema(getStubSchema(), { enabled: false }, prefix); -} diff --git a/src/legacy/plugin_discovery/plugin_config/index.js b/src/legacy/plugin_discovery/plugin_config/index.js deleted file mode 100644 index a27463bc9c7f5..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { extendConfigService, disableConfigExtension } from './extend_config_service'; diff --git a/src/legacy/plugin_discovery/plugin_config/schema.js b/src/legacy/plugin_discovery/plugin_config/schema.js deleted file mode 100644 index 14d10aa5568da..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/schema.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 Joi from 'joi'; - -const STUB_CONFIG_SCHEMA = Joi.object() - .keys({ - enabled: Joi.valid(false).default(false), - }) - .default(); - -const DEFAULT_CONFIG_SCHEMA = Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - }) - .default(); - -/** - * Get the config schema for a plugin spec - * @param {PluginSpec} spec - * @return {Promise} - */ -export async function getSchema(spec) { - const provider = spec.getConfigSchemaProvider(); - return (provider && (await provider(Joi))) || DEFAULT_CONFIG_SCHEMA; -} - -export function getStubSchema() { - return STUB_CONFIG_SCHEMA; -} diff --git a/src/legacy/plugin_discovery/plugin_config/settings.js b/src/legacy/plugin_discovery/plugin_config/settings.js deleted file mode 100644 index e6a4741d76eca..0000000000000 --- a/src/legacy/plugin_discovery/plugin_config/settings.js +++ /dev/null @@ -1,34 +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 } from 'lodash'; - -/** - * Get the settings for a pluginSpec from the raw root settings while - * optionally calling logDeprecation() with warnings about deprecated - * settings that were used - * @param {PluginSpec} spec - * @param {Object} rootSettings - * @return {Promise} - */ -export async function getSettings(spec, rootSettings) { - const prefix = spec.getConfigPrefix(); - const rawSettings = get(rootSettings, prefix); - return rawSettings; -} diff --git a/src/legacy/plugin_discovery/plugin_exports/__tests__/reduce_export_specs.js b/src/legacy/plugin_discovery/plugin_exports/__tests__/reduce_export_specs.js deleted file mode 100644 index 3beaacc1a8293..0000000000000 --- a/src/legacy/plugin_discovery/plugin_exports/__tests__/reduce_export_specs.js +++ /dev/null @@ -1,75 +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 expect from '@kbn/expect'; - -import { PluginPack } from '../../plugin_pack'; -import { reduceExportSpecs } from '../reduce_export_specs'; - -const PLUGIN = new PluginPack({ - path: __dirname, - pkg: { - name: 'foo', - version: 'kibana', - }, - provider: ({ Plugin }) => - new Plugin({ - uiExports: { - concatNames: { - name: 'export1', - }, - - concat: ['export2', 'export3'], - }, - }), -}); - -const REDUCERS = { - concatNames(acc, spec, type, pluginSpec) { - return { - names: [].concat(acc.names || [], `${pluginSpec.getId()}:${spec.name}`), - }; - }, - concat(acc, spec, type, pluginSpec) { - return { - names: [].concat(acc.names || [], `${pluginSpec.getId()}:${spec}`), - }; - }, -}; - -const PLUGIN_SPECS = PLUGIN.getPluginSpecs(); - -describe('reduceExportSpecs', () => { - it('combines ui exports from a list of plugin definitions', () => { - const exports = reduceExportSpecs(PLUGIN_SPECS, REDUCERS); - expect(exports).to.eql({ - names: ['foo:export1', 'foo:export2', 'foo:export3'], - }); - }); - - it('starts with the defaults', () => { - const exports = reduceExportSpecs(PLUGIN_SPECS, REDUCERS, { - names: ['default'], - }); - - expect(exports).to.eql({ - names: ['default', 'foo:export1', 'foo:export2', 'foo:export3'], - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_exports/index.js b/src/legacy/plugin_discovery/plugin_exports/index.js deleted file mode 100644 index 0e3511ea85dd4..0000000000000 --- a/src/legacy/plugin_discovery/plugin_exports/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { reduceExportSpecs } from './reduce_export_specs'; diff --git a/src/legacy/plugin_discovery/plugin_exports/reduce_export_specs.js b/src/legacy/plugin_discovery/plugin_exports/reduce_export_specs.js deleted file mode 100644 index a3adc3091085d..0000000000000 --- a/src/legacy/plugin_discovery/plugin_exports/reduce_export_specs.js +++ /dev/null @@ -1,47 +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. - */ - -/** - * Combine the exportSpecs from a list of pluginSpecs - * by calling the reducers for each export type - * @param {Array} pluginSpecs - * @param {Object} reducers - * @param {Object} [defaults={}] - * @return {Object} - */ -export function reduceExportSpecs(pluginSpecs, reducers, defaults = {}) { - return pluginSpecs.reduce((acc, pluginSpec) => { - const specsByType = pluginSpec.getExportSpecs() || {}; - const types = Object.keys(specsByType); - - return types.reduce((acc, type) => { - const reducer = reducers[type] || reducers.unknown; - - if (!reducer) { - throw new Error(`Unknown export type ${type}`); - } - - // convert specs to an array if not already one or - // ignore the spec if it is undefined - const specs = [].concat(specsByType[type] === undefined ? [] : specsByType[type]); - - return specs.reduce((acc, spec) => reducer(acc, spec, type, pluginSpec), acc); - }, acc); - }, defaults); -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/create_pack.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/create_pack.js deleted file mode 100644 index b17bd69479ffa..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/create_pack.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. - */ - -import { resolve } from 'path'; -import * as Rx from 'rxjs'; -import { toArray } from 'rxjs/operators'; -import expect from '@kbn/expect'; - -import { createPack$ } from '../create_pack'; -import { PluginPack } from '../plugin_pack'; - -import { PLUGINS_DIR, assertInvalidPackError } from './utils'; - -describe('plugin discovery/create pack', () => { - it('creates PluginPack', async () => { - const packageJson$ = Rx.from([ - { - packageJson: { - directoryPath: resolve(PLUGINS_DIR, 'prebuilt'), - contents: { - name: 'prebuilt', - }, - }, - }, - ]); - const results = await createPack$(packageJson$).pipe(toArray()).toPromise(); - expect(results).to.have.length(1); - expect(results[0]).to.only.have.keys(['pack']); - const { pack } = results[0]; - expect(pack).to.be.a(PluginPack); - }); - - describe('errors thrown', () => { - async function checkError(path, check) { - const packageJson$ = Rx.from([ - { - packageJson: { - directoryPath: path, - }, - }, - ]); - - const results = await createPack$(packageJson$).pipe(toArray()).toPromise(); - expect(results).to.have.length(1); - expect(results[0]).to.only.have.keys(['error']); - const { error } = results[0]; - await check(error); - } - it('default export is an object', () => - checkError(resolve(PLUGINS_DIR, 'exports_object'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must export a function'); - })); - it('default export is an number', () => - checkError(resolve(PLUGINS_DIR, 'exports_number'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must export a function'); - })); - it('default export is an string', () => - checkError(resolve(PLUGINS_DIR, 'exports_string'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must export a function'); - })); - it('directory with code that fails when required', () => - checkError(resolve(PLUGINS_DIR, 'broken_code'), (error) => { - expect(error.message).to.contain("Cannot find module 'does-not-exist'"); - })); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken/package.json deleted file mode 100644 index f830e8b60c02d..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/index.js deleted file mode 100644 index bdb26504d6b6e..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const brokenRequire = require('does-not-exist'); // eslint-disable-line - -module.exports = function (kibana) { - return new kibana.Plugin({ - id: 'foo', - }); -}; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken_code/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/index.js deleted file mode 100644 index f24fc54e38d9a..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export default 1; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_number/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/index.js deleted file mode 100644 index 59f4a2649f019..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/index.js +++ /dev/null @@ -1,22 +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 default { - foo: 'bar', -}; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_object/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/index.js deleted file mode 100644 index 8900db15321ae..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export default 'foo'; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/exports_string/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/index.js deleted file mode 100644 index e43a1dcedb372..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -module.exports = function (kibana) { - return new kibana.Plugin({ - id: 'foo', - }); -}; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/package.json deleted file mode 100644 index e43c2f0bc984c..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/foo/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "foo", - "version": "kibana" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/index.js deleted file mode 100644 index edb1dd15673da..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -console.log('hello world'); diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/index.js deleted file mode 100644 index 050ffdfbde9ea..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { myLib } from './my_lib'; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/my_lib.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/my_lib.js deleted file mode 100644 index 94e511632d9a6..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/lib/my_lib.js +++ /dev/null @@ -1,22 +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 function myLib() { - console.log('lib'); -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/index.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/index.js deleted file mode 100644 index 89744b2dd3fd9..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/index.js +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint-disable */ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -exports.default = function (_ref) { - var Plugin = _ref.Plugin; - - return new Plugin({ - id: 'foo' - }); -}; diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/package.json b/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/package.json deleted file mode 100644 index b1b74e0e76b12..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/prebuilt/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "prebuilt" -} diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/package_json_at_path.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/package_json_at_path.js deleted file mode 100644 index fa1033180954e..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/package_json_at_path.js +++ /dev/null @@ -1,88 +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 { resolve } from 'path'; -import { toArray } from 'rxjs/operators'; - -import expect from '@kbn/expect'; - -import { createPackageJsonAtPath$ } from '../package_json_at_path'; -import { PLUGINS_DIR, assertInvalidPackError, assertInvalidDirectoryError } from './utils'; - -describe('plugin discovery/plugin_pack', () => { - describe('createPackageJsonAtPath$()', () => { - it('returns an observable', () => { - expect(createPackageJsonAtPath$()).to.have.property('subscribe').a('function'); - }); - it('gets the default provider from prebuilt babel modules', async () => { - const results = await createPackageJsonAtPath$(resolve(PLUGINS_DIR, 'prebuilt')) - .pipe(toArray()) - .toPromise(); - expect(results).to.have.length(1); - expect(results[0]).to.only.have.keys(['packageJson']); - expect(results[0].packageJson).to.be.an(Object); - expect(results[0].packageJson.directoryPath).to.be(resolve(PLUGINS_DIR, 'prebuilt')); - expect(results[0].packageJson.contents).to.eql({ name: 'prebuilt' }); - }); - describe('errors emitted as { error } results', () => { - async function checkError(path, check) { - const results = await createPackageJsonAtPath$(path).pipe(toArray()).toPromise(); - expect(results).to.have.length(1); - expect(results[0]).to.only.have.keys(['error']); - const { error } = results[0]; - await check(error); - } - it('undefined path', () => - checkError(undefined, (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be a string'); - })); - it('relative path', () => - checkError('plugins/foo', (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be absolute'); - })); - it('./relative path', () => - checkError('./plugins/foo', (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be absolute'); - })); - it('non-existent path', () => - checkError(resolve(PLUGINS_DIR, 'baz'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must be a directory'); - })); - it('path to a file', () => - checkError(resolve(PLUGINS_DIR, 'index.js'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must be a directory'); - })); - it('directory without a package.json', () => - checkError(resolve(PLUGINS_DIR, 'lib'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must have a package.json file'); - })); - it('directory with an invalid package.json', () => - checkError(resolve(PLUGINS_DIR, 'broken'), (error) => { - assertInvalidPackError(error); - expect(error.message).to.contain('must have a valid package.json file'); - })); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/package_jsons_in_directory.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/package_jsons_in_directory.js deleted file mode 100644 index 37cb4cc064da7..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/package_jsons_in_directory.js +++ /dev/null @@ -1,83 +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 { resolve } from 'path'; - -import { toArray } from 'rxjs/operators'; -import expect from '@kbn/expect'; - -import { createPackageJsonsInDirectory$ } from '../package_jsons_in_directory'; - -import { PLUGINS_DIR, assertInvalidDirectoryError } from './utils'; - -describe('plugin discovery/packs in directory', () => { - describe('createPackageJsonsInDirectory$()', () => { - describe('errors emitted as { error } results', () => { - async function checkError(path, check) { - const results = await createPackageJsonsInDirectory$(path).pipe(toArray()).toPromise(); - expect(results).to.have.length(1); - expect(results[0]).to.only.have.keys('error'); - const { error } = results[0]; - await check(error); - } - - it('undefined path', () => - checkError(undefined, (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be a string'); - })); - it('relative path', () => - checkError('my/plugins', (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be absolute'); - })); - it('./relative path', () => - checkError('./my/pluginsd', (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('path must be absolute'); - })); - it('non-existent path', () => - checkError(resolve(PLUGINS_DIR, 'notreal'), (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('no such file or directory'); - })); - it('path to a file', () => - checkError(resolve(PLUGINS_DIR, 'index.js'), (error) => { - assertInvalidDirectoryError(error); - expect(error.message).to.contain('not a directory'); - })); - }); - - it('includes child errors for invalid packageJsons within a valid directory', async () => { - const results = await createPackageJsonsInDirectory$(PLUGINS_DIR).pipe(toArray()).toPromise(); - - const errors = results.map((result) => result.error).filter(Boolean); - - const packageJsons = results.map((result) => result.packageJson).filter(Boolean); - - packageJsons.forEach((pack) => expect(pack).to.be.an(Object)); - // there should be one result for each item in PLUGINS_DIR - expect(results).to.have.length(8); - // three of the fixtures are errors of some sort - expect(errors).to.have.length(2); - // six of them are valid - expect(packageJsons).to.have.length(6); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/plugin_pack.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/plugin_pack.js deleted file mode 100644 index 769fcd74ce6fb..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/plugin_pack.js +++ /dev/null @@ -1,126 +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 expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { PluginPack } from '../plugin_pack'; -import { PluginSpec } from '../../plugin_spec'; - -describe('plugin discovery/plugin pack', () => { - describe('constructor', () => { - it('requires an object', () => { - expect(() => { - new PluginPack(); - }).to.throwError(); - }); - }); - describe('#getPkg()', () => { - it('returns the `pkg` constructor argument', () => { - const pkg = {}; - const pack = new PluginPack({ pkg }); - expect(pack.getPkg()).to.be(pkg); - }); - }); - describe('#getPath()', () => { - it('returns the `path` constructor argument', () => { - const path = {}; - const pack = new PluginPack({ path }); - expect(pack.getPath()).to.be(path); - }); - }); - describe('#getPluginSpecs()', () => { - it('calls the `provider` constructor argument with an api including a single sub class of PluginSpec', () => { - const provider = sinon.stub(); - const pack = new PluginPack({ provider }); - sinon.assert.notCalled(provider); - pack.getPluginSpecs(); - sinon.assert.calledOnce(provider); - sinon.assert.calledWithExactly(provider, { - Plugin: sinon.match((Class) => { - return Class.prototype instanceof PluginSpec; - }, 'Subclass of PluginSpec'), - }); - }); - - it('casts undefined return value to array', () => { - const pack = new PluginPack({ provider: () => undefined }); - expect(pack.getPluginSpecs()).to.eql([]); - }); - - it('casts single PluginSpec to an array', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { name: 'foo', version: 'kibana' }, - provider: ({ Plugin }) => new Plugin({}), - }); - - const specs = pack.getPluginSpecs(); - expect(specs).to.be.an('array'); - expect(specs).to.have.length(1); - expect(specs[0]).to.be.a(PluginSpec); - }); - - it('returns an array of PluginSpec', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { name: 'foo', version: 'kibana' }, - provider: ({ Plugin }) => [new Plugin({}), new Plugin({})], - }); - - const specs = pack.getPluginSpecs(); - expect(specs).to.be.an('array'); - expect(specs).to.have.length(2); - expect(specs[0]).to.be.a(PluginSpec); - expect(specs[1]).to.be.a(PluginSpec); - }); - - it('throws if non-undefined return value is not an instance of api.Plugin', () => { - let OtherPluginSpecClass; - const otherPack = new PluginPack({ - path: '/dev/null', - pkg: { name: 'foo', version: 'kibana' }, - provider: (api) => { - OtherPluginSpecClass = api.Plugin; - }, - }); - - // call getPluginSpecs() on other pack to get it's api.Plugin class - otherPack.getPluginSpecs(); - - const badPacks = [ - new PluginPack({ provider: () => false }), - new PluginPack({ provider: () => null }), - new PluginPack({ provider: () => 1 }), - new PluginPack({ provider: () => 'true' }), - new PluginPack({ provider: () => true }), - new PluginPack({ provider: () => new Date() }), - new PluginPack({ provider: () => /foo.*bar/ }), - new PluginPack({ provider: () => function () {} }), - new PluginPack({ provider: () => new OtherPluginSpecClass({}) }), - ]; - - for (const pack of badPacks) { - expect(() => pack.getPluginSpecs()).to.throwError((error) => { - expect(error.message).to.contain('unexpected plugin export'); - }); - } - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_pack/__tests__/utils.js b/src/legacy/plugin_discovery/plugin_pack/__tests__/utils.js deleted file mode 100644 index adcf60d809ff7..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/__tests__/utils.js +++ /dev/null @@ -1,37 +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 { resolve } from 'path'; -import { inspect } from 'util'; - -import { isInvalidPackError, isInvalidDirectoryError } from '../../errors'; - -export const PLUGINS_DIR = resolve(__dirname, 'fixtures/plugins'); - -export function assertInvalidDirectoryError(error) { - if (!isInvalidDirectoryError(error)) { - throw new Error(`Expected ${inspect(error)} to be an 'InvalidDirectoryError'`); - } -} - -export function assertInvalidPackError(error) { - if (!isInvalidPackError(error)) { - throw new Error(`Expected ${inspect(error)} to be an 'InvalidPackError'`); - } -} diff --git a/src/legacy/plugin_discovery/plugin_pack/create_pack.js b/src/legacy/plugin_discovery/plugin_pack/create_pack.js deleted file mode 100644 index 189c2ea324103..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/create_pack.js +++ /dev/null @@ -1,54 +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 { PluginPack } from './plugin_pack'; -import { map, catchError } from 'rxjs/operators'; -import { createInvalidPackError } from '../errors'; - -function createPack(packageJson) { - let provider = require(packageJson.directoryPath); // eslint-disable-line import/no-dynamic-require - if (provider.__esModule) { - provider = provider.default; - } - if (typeof provider !== 'function') { - throw createInvalidPackError(packageJson.directoryPath, 'must export a function'); - } - - return new PluginPack({ path: packageJson.directoryPath, pkg: packageJson.contents, provider }); -} - -export const createPack$ = (packageJson$) => - packageJson$.pipe( - map(({ error, packageJson }) => { - if (error) { - return { error }; - } - - if (!packageJson) { - throw new Error('packageJson is required to create the pack'); - } - - return { - pack: createPack(packageJson), - }; - }), - // createPack can throw errors, and we want them to be represented - // like the errors we consume from createPackageJsonAtPath/Directory - catchError((error) => [{ error }]) - ); diff --git a/src/legacy/plugin_discovery/plugin_pack/index.js b/src/legacy/plugin_discovery/plugin_pack/index.js deleted file mode 100644 index 69e55baee660b..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/index.js +++ /dev/null @@ -1,23 +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 { createPack$ } from './create_pack'; -export { createPackageJsonAtPath$ } from './package_json_at_path'; -export { createPackageJsonsInDirectory$ } from './package_jsons_in_directory'; -export { PluginPack } from './plugin_pack'; diff --git a/src/legacy/plugin_discovery/plugin_pack/lib/fs.js b/src/legacy/plugin_discovery/plugin_pack/lib/fs.js deleted file mode 100644 index 2b531e314df52..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/lib/fs.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. - */ - -import { stat, readdir } from 'fs'; -import { resolve, isAbsolute } from 'path'; - -import { fromNode as fcb } from 'bluebird'; -import * as Rx from 'rxjs'; -import { catchError, mergeAll, filter, map, mergeMap } from 'rxjs/operators'; - -import { createInvalidDirectoryError } from '../../errors'; - -function assertAbsolutePath(path) { - if (typeof path !== 'string') { - throw createInvalidDirectoryError(new TypeError('path must be a string'), path); - } - - if (!isAbsolute(path)) { - throw createInvalidDirectoryError(new TypeError('path must be absolute'), path); - } -} - -async function statTest(path, test) { - try { - const stats = await fcb((cb) => stat(path, cb)); - return Boolean(test(stats)); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } - } - return false; -} - -/** - * Determine if a path currently points to a directory - * @param {String} path - * @return {Promise} - */ -export async function isDirectory(path) { - assertAbsolutePath(path); - return await statTest(path, (stat) => stat.isDirectory()); -} - -/** - * Get absolute paths for child directories within a path - * @param {string} path - * @return {Promise>} - */ -export const createChildDirectory$ = (path) => - Rx.defer(() => { - assertAbsolutePath(path); - return fcb((cb) => readdir(path, cb)); - }).pipe( - catchError((error) => { - throw createInvalidDirectoryError(error, path); - }), - mergeAll(), - filter((name) => !name.startsWith('.')), - map((name) => resolve(path, name)), - mergeMap(async (absolute) => { - if (await isDirectory(absolute)) { - return [absolute]; - } else { - return []; - } - }), - mergeAll() - ); diff --git a/src/legacy/plugin_discovery/plugin_pack/lib/index.js b/src/legacy/plugin_discovery/plugin_pack/lib/index.js deleted file mode 100644 index 491deeda27516..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/lib/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { isDirectory, createChildDirectory$ } from './fs'; diff --git a/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js b/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js deleted file mode 100644 index 18629ef3ea802..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/package_json_at_path.js +++ /dev/null @@ -1,62 +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 { readFileSync } from 'fs'; -import * as Rx from 'rxjs'; -import { map, mergeMap, catchError } from 'rxjs/operators'; -import { resolve } from 'path'; -import { createInvalidPackError } from '../errors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { isNewPlatformPlugin } from '../../../core/server/plugins'; - -import { isDirectory } from './lib'; - -async function createPackageJsonAtPath(path) { - if (!(await isDirectory(path))) { - throw createInvalidPackError(path, 'must be a directory'); - } - - let str; - try { - str = readFileSync(resolve(path, 'package.json')); - } catch (err) { - throw createInvalidPackError(path, 'must have a package.json file'); - } - - let pkg; - try { - pkg = JSON.parse(str); - } catch (err) { - throw createInvalidPackError(path, 'must have a valid package.json file'); - } - - return { - directoryPath: path, - contents: pkg, - }; -} - -export const createPackageJsonAtPath$ = (path) => - // If plugin directory contains manifest file, we should skip it since it - // should have been handled by the core plugin system already. - Rx.defer(() => isNewPlatformPlugin(path)).pipe( - mergeMap((isNewPlatformPlugin) => (isNewPlatformPlugin ? [] : createPackageJsonAtPath(path))), - map((packageJson) => ({ packageJson })), - catchError((error) => [{ error }]) - ); diff --git a/src/legacy/plugin_discovery/plugin_pack/package_jsons_in_directory.js b/src/legacy/plugin_discovery/plugin_pack/package_jsons_in_directory.js deleted file mode 100644 index 5f0977f4829b8..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/package_jsons_in_directory.js +++ /dev/null @@ -1,52 +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 { mergeMap, catchError } from 'rxjs/operators'; -import { isInvalidDirectoryError } from '../errors'; - -import { createChildDirectory$ } from './lib'; -import { createPackageJsonAtPath$ } from './package_json_at_path'; - -/** - * Finds the plugins within a directory. Results are - * an array of objects with either `pack` or `error` - * keys. - * - * - `{ error }` results are provided when the path is not - * a directory, or one of the child directories is not a - * valid plugin pack. - * - `{ pack }` results are for discovered plugins defs - * - * @param {String} path - * @return {Array<{pack}|{error}>} - */ -export const createPackageJsonsInDirectory$ = (path) => - createChildDirectory$(path).pipe( - mergeMap(createPackageJsonAtPath$), - catchError((error) => { - // this error is produced by createChildDirectory$() when the path - // is invalid, we return them as an error result similar to how - // createPackAtPath$ works when it finds invalid packs in a directory - if (isInvalidDirectoryError(error)) { - return [{ error }]; - } - - throw error; - }) - ); diff --git a/src/legacy/plugin_discovery/plugin_pack/plugin_pack.js b/src/legacy/plugin_discovery/plugin_pack/plugin_pack.js deleted file mode 100644 index 1baf3d104ca84..0000000000000 --- a/src/legacy/plugin_discovery/plugin_pack/plugin_pack.js +++ /dev/null @@ -1,74 +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 { inspect } from 'util'; - -import { PluginSpec } from '../plugin_spec'; - -export class PluginPack { - constructor({ path, pkg, provider }) { - this._path = path; - this._pkg = pkg; - this._provider = provider; - } - - /** - * Get the contents of this plugin pack's package.json file - * @return {Object} - */ - getPkg() { - return this._pkg; - } - - /** - * Get the absolute path to this plugin pack on disk - * @return {String} - */ - getPath() { - return this._path; - } - - /** - * Invoke the plugin pack's provider to get the list - * of specs defined in this plugin. - * @return {Array} - */ - getPluginSpecs() { - const pack = this; - const api = { - Plugin: class ScopedPluginSpec extends PluginSpec { - constructor(options) { - super(pack, options); - } - }, - }; - - const result = this._provider(api); - const specs = [].concat(result === undefined ? [] : result); - - // verify that all specs are instances of passed "Plugin" class - specs.forEach((spec) => { - if (!(spec instanceof api.Plugin)) { - throw new TypeError('unexpected plugin export ' + inspect(spec)); - } - }); - - return specs; - } -} diff --git a/src/legacy/plugin_discovery/plugin_spec/__tests__/is_version_compatible.js b/src/legacy/plugin_discovery/plugin_spec/__tests__/is_version_compatible.js deleted file mode 100644 index 897184496af37..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/__tests__/is_version_compatible.js +++ /dev/null @@ -1,48 +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 expect from '@kbn/expect'; - -import { isVersionCompatible } from '../is_version_compatible'; - -describe('plugin discovery/plugin spec', () => { - describe('isVersionCompatible()', () => { - const tests = [ - ['kibana', '6.0.0', true], - ['kibana', '6.0.0-rc1', true], - ['6.0.0-rc1', '6.0.0', true], - ['6.0.0', '6.0.0-rc1', true], - ['6.0.0-rc2', '6.0.0-rc1', true], - ['6.0.0-rc2', '6.0.0-rc3', true], - ['foo', 'bar', false], - ['6.0.0', '5.1.4', false], - ['5.1.4', '6.0.0', false], - ['5.1.4-SNAPSHOT', '6.0.0-rc2-SNAPSHOT', false], - ['5.1.4', '6.0.0-rc2-SNAPSHOT', false], - ['5.1.4-SNAPSHOT', '6.0.0', false], - ['5.1.4-SNAPSHOT', '6.0.0-rc2', false], - ]; - - for (const [plugin, kibana, shouldPass] of tests) { - it(`${shouldPass ? 'should' : `shouldn't`} allow plugin: ${plugin} kibana: ${kibana}`, () => { - expect(isVersionCompatible(plugin, kibana)).to.be(shouldPass); - }); - } - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_spec/__tests__/plugin_spec.js b/src/legacy/plugin_discovery/plugin_spec/__tests__/plugin_spec.js deleted file mode 100644 index 02675f0bd60f8..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/__tests__/plugin_spec.js +++ /dev/null @@ -1,496 +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 { resolve } from 'path'; - -import expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { PluginPack } from '../../plugin_pack'; -import { PluginSpec } from '../plugin_spec'; -import * as IsVersionCompatibleNS from '../is_version_compatible'; - -const fooPack = new PluginPack({ - path: '/dev/null', - pkg: { name: 'foo', version: 'kibana' }, -}); - -describe('plugin discovery/plugin spec', () => { - describe('PluginSpec', () => { - describe('validation', () => { - it('throws if missing spec.id AND Pack has no name', () => { - const pack = new PluginPack({ pkg: {} }); - expect(() => new PluginSpec(pack, {})).to.throwError((error) => { - expect(error.message).to.contain('Unable to determine plugin id'); - }); - }); - - it('throws if missing spec.kibanaVersion AND Pack has no version', () => { - const pack = new PluginPack({ pkg: { name: 'foo' } }); - expect(() => new PluginSpec(pack, {})).to.throwError((error) => { - expect(error.message).to.contain('Unable to determine plugin version'); - }); - }); - - it('throws if spec.require is defined, but not an array', () => { - function assert(require) { - expect(() => new PluginSpec(fooPack, { require })).to.throwError((error) => { - expect(error.message).to.contain('"plugin.require" must be an array of plugin ids'); - }); - } - - assert(null); - assert(''); - assert('kibana'); - assert(1); - assert(0); - assert(/a.*b/); - }); - - it('throws if spec.publicDir is truthy and not a string', () => { - function assert(publicDir) { - expect(() => new PluginSpec(fooPack, { publicDir })).to.throwError((error) => { - expect(error.message).to.contain( - `The "path" argument must be of type string. Received type ${typeof publicDir}` - ); - }); - } - - assert(1); - assert(function () {}); - assert([]); - assert(/a.*b/); - }); - - it('throws if spec.publicDir is not an absolute path', () => { - function assert(publicDir) { - expect(() => new PluginSpec(fooPack, { publicDir })).to.throwError((error) => { - expect(error.message).to.contain('plugin.publicDir must be an absolute path'); - }); - } - - assert('relative/path'); - assert('./relative/path'); - }); - - it('throws if spec.publicDir basename is not `public`', () => { - function assert(publicDir) { - expect(() => new PluginSpec(fooPack, { publicDir })).to.throwError((error) => { - expect(error.message).to.contain('must end with a "public" directory'); - }); - } - - assert('/www'); - assert('/www/'); - assert('/www/public/my_plugin'); - assert('/www/public/my_plugin/'); - }); - }); - - describe('#getPack()', () => { - it('returns the pack', () => { - const spec = new PluginSpec(fooPack, {}); - expect(spec.getPack()).to.be(fooPack); - }); - }); - - describe('#getPkg()', () => { - it('returns the pkg from the pack', () => { - const spec = new PluginSpec(fooPack, {}); - expect(spec.getPkg()).to.be(fooPack.getPkg()); - }); - }); - - describe('#getPath()', () => { - it('returns the path from the pack', () => { - const spec = new PluginSpec(fooPack, {}); - expect(spec.getPath()).to.be(fooPack.getPath()); - }); - }); - - describe('#getId()', () => { - it('uses spec.id', () => { - const spec = new PluginSpec(fooPack, { - id: 'bar', - }); - - expect(spec.getId()).to.be('bar'); - }); - - it('defaults to pack.pkg.name', () => { - const spec = new PluginSpec(fooPack, {}); - - expect(spec.getId()).to.be('foo'); - }); - }); - - describe('#getVersion()', () => { - it('uses spec.version', () => { - const spec = new PluginSpec(fooPack, { - version: 'bar', - }); - - expect(spec.getVersion()).to.be('bar'); - }); - - it('defaults to pack.pkg.version', () => { - const spec = new PluginSpec(fooPack, {}); - - expect(spec.getVersion()).to.be('kibana'); - }); - }); - - describe('#isEnabled()', () => { - describe('spec.isEnabled is not defined', () => { - function setup(configPrefix, configGetImpl) { - const spec = new PluginSpec(fooPack, { configPrefix }); - const config = { - get: sinon.spy(configGetImpl), - has: sinon.stub(), - }; - - return { spec, config }; - } - - it('throws if not passed a config service', () => { - const { spec } = setup('a.b.c', () => true); - - expect(() => spec.isEnabled()).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - expect(() => spec.isEnabled(null)).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - expect(() => spec.isEnabled({ get: () => {} })).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - }); - - it('returns true when config.get([...configPrefix, "enabled"]) returns true', () => { - const { spec, config } = setup('d.e.f', () => true); - - expect(spec.isEnabled(config)).to.be(true); - sinon.assert.calledOnce(config.get); - sinon.assert.calledWithExactly(config.get, ['d', 'e', 'f', 'enabled']); - }); - - it('returns false when config.get([...configPrefix, "enabled"]) returns false', () => { - const { spec, config } = setup('g.h.i', () => false); - - expect(spec.isEnabled(config)).to.be(false); - sinon.assert.calledOnce(config.get); - sinon.assert.calledWithExactly(config.get, ['g', 'h', 'i', 'enabled']); - }); - }); - - describe('spec.isEnabled is defined', () => { - function setup(isEnabledImpl) { - const isEnabled = sinon.spy(isEnabledImpl); - const spec = new PluginSpec(fooPack, { isEnabled }); - const config = { - get: sinon.stub(), - has: sinon.stub(), - }; - - return { isEnabled, spec, config }; - } - - it('throws if not passed a config service', () => { - const { spec } = setup(() => true); - - expect(() => spec.isEnabled()).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - expect(() => spec.isEnabled(null)).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - expect(() => spec.isEnabled({ get: () => {} })).to.throwError((error) => { - expect(error.message).to.contain('must be called with a config service'); - }); - }); - - it('does not check config if spec.isEnabled returns true', () => { - const { spec, isEnabled, config } = setup(() => true); - - expect(spec.isEnabled(config)).to.be(true); - sinon.assert.calledOnce(isEnabled); - sinon.assert.notCalled(config.get); - }); - - it('does not check config if spec.isEnabled returns false', () => { - const { spec, isEnabled, config } = setup(() => false); - - expect(spec.isEnabled(config)).to.be(false); - sinon.assert.calledOnce(isEnabled); - sinon.assert.notCalled(config.get); - }); - }); - }); - - describe('#getExpectedKibanaVersion()', () => { - describe('has: spec.kibanaVersion,pkg.kibana.version,spec.version,pkg.version', () => { - it('uses spec.kibanaVersion', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'expkv', - version: '1.0.0', - kibana: { - version: '6.0.0', - }, - }, - }); - - const spec = new PluginSpec(pack, { - version: '2.0.0', - kibanaVersion: '5.0.0', - }); - - expect(spec.getExpectedKibanaVersion()).to.be('5.0.0'); - }); - }); - describe('missing: spec.kibanaVersion, has: pkg.kibana.version,spec.version,pkg.version', () => { - it('uses pkg.kibana.version', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'expkv', - version: '1.0.0', - kibana: { - version: '6.0.0', - }, - }, - }); - - const spec = new PluginSpec(pack, { - version: '2.0.0', - }); - - expect(spec.getExpectedKibanaVersion()).to.be('6.0.0'); - }); - }); - describe('missing: spec.kibanaVersion,pkg.kibana.version, has: spec.version,pkg.version', () => { - it('uses spec.version', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'expkv', - version: '1.0.0', - }, - }); - - const spec = new PluginSpec(pack, { - version: '2.0.0', - }); - - expect(spec.getExpectedKibanaVersion()).to.be('2.0.0'); - }); - }); - describe('missing: spec.kibanaVersion,pkg.kibana.version,spec.version, has: pkg.version', () => { - it('uses pkg.version', () => { - const pack = new PluginPack({ - path: '/dev/null', - pkg: { - name: 'expkv', - version: '1.0.0', - }, - }); - - const spec = new PluginSpec(pack, {}); - - expect(spec.getExpectedKibanaVersion()).to.be('1.0.0'); - }); - }); - }); - - describe('#isVersionCompatible()', () => { - it('passes this.getExpectedKibanaVersion() and arg to isVersionCompatible(), returns its result', () => { - const spec = new PluginSpec(fooPack, { version: '1.0.0' }); - sinon.stub(spec, 'getExpectedKibanaVersion').returns('foo'); - const isVersionCompatible = sinon - .stub(IsVersionCompatibleNS, 'isVersionCompatible') - .returns('bar'); - expect(spec.isVersionCompatible('baz')).to.be('bar'); - - sinon.assert.calledOnce(spec.getExpectedKibanaVersion); - sinon.assert.calledWithExactly(spec.getExpectedKibanaVersion); - - sinon.assert.calledOnce(isVersionCompatible); - sinon.assert.calledWithExactly(isVersionCompatible, 'foo', 'baz'); - }); - }); - - describe('#getRequiredPluginIds()', () => { - it('returns spec.require', () => { - const spec = new PluginSpec(fooPack, { require: [1, 2, 3] }); - expect(spec.getRequiredPluginIds()).to.eql([1, 2, 3]); - }); - }); - - describe('#getPublicDir()', () => { - describe('spec.publicDir === false', () => { - it('returns null', () => { - const spec = new PluginSpec(fooPack, { publicDir: false }); - expect(spec.getPublicDir()).to.be(null); - }); - }); - - describe('spec.publicDir is falsy', () => { - it('returns public child of pack path', () => { - function assert(publicDir) { - const spec = new PluginSpec(fooPack, { publicDir }); - expect(spec.getPublicDir()).to.be(resolve('/dev/null/public')); - } - - assert(0); - assert(''); - assert(null); - assert(undefined); - assert(NaN); - }); - }); - - describe('spec.publicDir is an absolute path', () => { - it('returns the path', () => { - const spec = new PluginSpec(fooPack, { - publicDir: '/var/www/public', - }); - - expect(spec.getPublicDir()).to.be('/var/www/public'); - }); - }); - - // NOTE: see constructor tests for other truthy-tests that throw in constructor - }); - - describe('#getExportSpecs()', () => { - it('returns spec.uiExports', () => { - const spec = new PluginSpec(fooPack, { - uiExports: 'foo', - }); - - expect(spec.getExportSpecs()).to.be('foo'); - }); - }); - - describe('#getPreInitHandler()', () => { - it('returns spec.preInit', () => { - const spec = new PluginSpec(fooPack, { - preInit: 'foo', - }); - - expect(spec.getPreInitHandler()).to.be('foo'); - }); - }); - - describe('#getInitHandler()', () => { - it('returns spec.init', () => { - const spec = new PluginSpec(fooPack, { - init: 'foo', - }); - - expect(spec.getInitHandler()).to.be('foo'); - }); - }); - - describe('#getConfigPrefix()', () => { - describe('spec.configPrefix is truthy', () => { - it('returns spec.configPrefix', () => { - const spec = new PluginSpec(fooPack, { - configPrefix: 'foo.bar.baz', - }); - - expect(spec.getConfigPrefix()).to.be('foo.bar.baz'); - }); - }); - describe('spec.configPrefix is falsy', () => { - it('returns spec.getId()', () => { - function assert(configPrefix) { - const spec = new PluginSpec(fooPack, { configPrefix }); - sinon.stub(spec, 'getId').returns('foo'); - expect(spec.getConfigPrefix()).to.be('foo'); - sinon.assert.calledOnce(spec.getId); - } - - assert(false); - assert(null); - assert(undefined); - assert(''); - assert(0); - }); - }); - }); - - describe('#getConfigSchemaProvider()', () => { - it('returns spec.config', () => { - const spec = new PluginSpec(fooPack, { - config: 'foo', - }); - - expect(spec.getConfigSchemaProvider()).to.be('foo'); - }); - }); - - describe('#readConfigValue()', () => { - const spec = new PluginSpec(fooPack, { - configPrefix: 'foo.bar', - }); - - const config = { - get: sinon.stub(), - }; - - afterEach(() => config.get.resetHistory()); - - describe('key = "foo"', () => { - it('passes key as own array item', () => { - spec.readConfigValue(config, 'foo'); - sinon.assert.calledOnce(config.get); - sinon.assert.calledWithExactly(config.get, ['foo', 'bar', 'foo']); - }); - }); - - describe('key = "foo.bar"', () => { - it('passes key as two array items', () => { - spec.readConfigValue(config, 'foo.bar'); - sinon.assert.calledOnce(config.get); - sinon.assert.calledWithExactly(config.get, ['foo', 'bar', 'foo', 'bar']); - }); - }); - - describe('key = ["foo", "bar"]', () => { - it('merged keys into array', () => { - spec.readConfigValue(config, ['foo', 'bar']); - sinon.assert.calledOnce(config.get); - sinon.assert.calledWithExactly(config.get, ['foo', 'bar', 'foo', 'bar']); - }); - }); - }); - - describe('#getDeprecationsProvider()', () => { - it('returns spec.deprecations', () => { - const spec = new PluginSpec(fooPack, { - deprecations: 'foo', - }); - - expect(spec.getDeprecationsProvider()).to.be('foo'); - }); - }); - }); -}); diff --git a/src/legacy/plugin_discovery/plugin_spec/index.js b/src/legacy/plugin_discovery/plugin_spec/index.js deleted file mode 100644 index 671d311b152e2..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { PluginSpec } from './plugin_spec'; diff --git a/src/legacy/plugin_discovery/plugin_spec/is_version_compatible.js b/src/legacy/plugin_discovery/plugin_spec/is_version_compatible.js deleted file mode 100644 index 6822c168f368d..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/is_version_compatible.js +++ /dev/null @@ -1,30 +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 { cleanVersion, versionSatisfies } from '../../utils/version'; - -export function isVersionCompatible(version, compatibleWith) { - // the special "kibana" version can be used to always be compatible, - // but is intentionally not supported by the plugin installer - if (version === 'kibana') { - return true; - } - - return versionSatisfies(cleanVersion(version), cleanVersion(compatibleWith)); -} diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec.js b/src/legacy/plugin_discovery/plugin_spec/plugin_spec.js deleted file mode 100644 index db1ec425f2ce5..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec.js +++ /dev/null @@ -1,210 +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 { resolve, basename, isAbsolute as isAbsolutePath } from 'path'; - -import { get, toPath } from 'lodash'; - -import { createInvalidPluginError } from '../errors'; -import { isVersionCompatible } from './is_version_compatible'; - -export class PluginSpec { - /** - * @param {PluginPack} pack The plugin pack that produced this spec - * @param {Object} opts the options for this plugin - * @param {String} [opts.id=pkg.name] the id for this plugin. - * @param {Object} [opts.uiExports] a mapping of UiExport types to - * UI modules or metadata about the UI module - * @param {Array} [opts.require] the other plugins that this plugin - * requires. These plugins must exist and be enabled for this plugin - * to function. The require'd plugins will also be initialized first, - * in order to make sure that dependencies provided by these plugins - * are available - * @param {String} [opts.version=pkg.version] the version of this plugin - * @param {Function} [opts.init] A function that will be called to initialize - * this plugin at the appropriate time. - * @param {Function} [opts.configPrefix=this.id] The prefix to use for - * configuration values in the main configuration service - * @param {Function} [opts.config] A function that produces a configuration - * schema using Joi, which is passed as its first argument. - * @param {String|False} [opts.publicDir=path + '/public'] the public - * directory for this plugin. The final directory must have the name "public", - * though it can be located somewhere besides the root of the plugin. Set - * this to false to disable exposure of a public directory - */ - constructor(pack, options) { - const { - id, - require, - version, - kibanaVersion, - uiExports, - uiCapabilities, - publicDir, - configPrefix, - config, - deprecations, - preInit, - init, - postInit, - isEnabled, - } = options; - - this._id = id; - this._pack = pack; - this._version = version; - this._kibanaVersion = kibanaVersion; - this._require = require; - - this._publicDir = publicDir; - this._uiExports = uiExports; - this._uiCapabilities = uiCapabilities; - - this._configPrefix = configPrefix; - this._configSchemaProvider = config; - this._configDeprecationsProvider = deprecations; - - this._isEnabled = isEnabled; - this._preInit = preInit; - this._init = init; - this._postInit = postInit; - - if (!this.getId()) { - throw createInvalidPluginError(this, 'Unable to determine plugin id'); - } - - if (!this.getVersion()) { - throw createInvalidPluginError(this, 'Unable to determine plugin version'); - } - - if (this.getRequiredPluginIds() !== undefined && !Array.isArray(this.getRequiredPluginIds())) { - throw createInvalidPluginError(this, '"plugin.require" must be an array of plugin ids'); - } - - if (this._publicDir) { - if (!isAbsolutePath(this._publicDir)) { - throw createInvalidPluginError(this, 'plugin.publicDir must be an absolute path'); - } - if (basename(this._publicDir) !== 'public') { - throw createInvalidPluginError( - this, - `publicDir for plugin ${this.getId()} must end with a "public" directory.` - ); - } - } - } - - getPack() { - return this._pack; - } - - getPkg() { - return this._pack.getPkg(); - } - - getPath() { - return this._pack.getPath(); - } - - getId() { - return this._id || this.getPkg().name; - } - - getVersion() { - return this._version || this.getPkg().version; - } - - isEnabled(config) { - if (!config || typeof config.get !== 'function' || typeof config.has !== 'function') { - throw new TypeError('PluginSpec#isEnabled() must be called with a config service'); - } - - if (this._isEnabled) { - return this._isEnabled(config); - } - - return Boolean(this.readConfigValue(config, 'enabled')); - } - - getExpectedKibanaVersion() { - // Plugins must specify their version, and by default that version should match - // the version of kibana down to the patch level. If these two versions need - // to diverge, they can specify a kibana.version in the package to indicate the - // version of kibana the plugin is intended to work with. - return ( - this._kibanaVersion || get(this.getPack().getPkg(), 'kibana.version') || this.getVersion() - ); - } - - isVersionCompatible(actualKibanaVersion) { - return isVersionCompatible(this.getExpectedKibanaVersion(), actualKibanaVersion); - } - - getRequiredPluginIds() { - return this._require; - } - - getPublicDir() { - if (this._publicDir === false) { - return null; - } - - if (!this._publicDir) { - return resolve(this.getPack().getPath(), 'public'); - } - - return this._publicDir; - } - - getExportSpecs() { - return this._uiExports; - } - - getUiCapabilitiesProvider() { - return this._uiCapabilities; - } - - getPreInitHandler() { - return this._preInit; - } - - getInitHandler() { - return this._init; - } - - getPostInitHandler() { - return this._postInit; - } - - getConfigPrefix() { - return this._configPrefix || this.getId(); - } - - getConfigSchemaProvider() { - return this._configSchemaProvider; - } - - readConfigValue(config, key) { - return config.get([...toPath(this.getConfigPrefix()), ...toPath(key)]); - } - - getDeprecationsProvider() { - return this._configDeprecationsProvider; - } -} diff --git a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts b/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts deleted file mode 100644 index e1ed2f57375a4..0000000000000 --- a/src/legacy/plugin_discovery/plugin_spec/plugin_spec_options.d.ts +++ /dev/null @@ -1,35 +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 { Server } from '../../server/kbn_server'; -import { Capabilities } from '../../../core/server'; - -export type InitPluginFunction = (server: Server) => void; -export interface UiExports { - injectDefaultVars?: (server: Server) => { [key: string]: any }; -} - -export interface PluginSpecOptions { - id: string; - require?: string[]; - publicDir?: string; - uiExports?: UiExports; - uiCapabilities?: Capabilities; - init?: InitPluginFunction; - config?: any; -} diff --git a/src/legacy/plugin_discovery/types.ts b/src/legacy/plugin_discovery/types.ts deleted file mode 100644 index 700ca6fa68c95..0000000000000 --- a/src/legacy/plugin_discovery/types.ts +++ /dev/null @@ -1,107 +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 { Server } from '../server/kbn_server'; -import { Capabilities } from '../../core/server'; -import { AppCategory } from '../../core/types'; - -/** - * Usage - * - * ``` - * const apmOss: LegacyPlugin = (kibana) => { - * return new kibana.Plugin({ - * id: 'apm_oss', - * // ... - * }); - * }; - * ``` - */ -export type LegacyPluginInitializer = (kibana: LegacyPluginApi) => ArrayOrItem; - -export type ArrayOrItem = T | T[]; - -export interface LegacyPluginApi { - Plugin: new (options: Partial) => LegacyPluginSpec; -} - -export interface LegacyPluginOptions { - id: string; - require: string[]; - version: string; - kibanaVersion: 'kibana'; - uiExports: Partial<{ - app: Partial<{ - title: string; - category?: AppCategory; - description: string; - main: string; - icon: string; - euiIconType: string; - order: number; - listed: boolean; - }>; - apps: any; - hacks: string[]; - visualize: string[]; - devTools: string[]; - injectDefaultVars: (server: Server) => Record; - home: string[]; - mappings: any; - migrations: any; - visTypes: string[]; - embeddableActions?: string[]; - embeddableFactories?: string[]; - uiSettingDefaults?: Record; - interpreter: string | string[]; - }>; - uiCapabilities?: Capabilities; - publicDir: any; - configPrefix: any; - config: any; - deprecations: any; - preInit: any; - init: InitPluginFunction; - postInit: any; - isEnabled: boolean; -} - -export type InitPluginFunction = (server: Server) => void; - -export interface LegacyPluginSpec { - getPack(): any; - getPkg(): any; - getPath(): string; - getId(): string; - getVersion(): string; - isEnabled(config: any): boolean; - getExpectedKibanaVersion(): string; - isVersionCompatible(actualKibanaVersion: any): boolean; - getRequiredPluginIds(): string[]; - getPublicDir(): string | null; - getExportSpecs(): any; - getUiCapabilitiesProvider(): any; - getPreInitHandler(): any; - getInitHandler(): any; - getPostInitHandler(): any; - getConfigPrefix(): string; - getConfigSchemaProvider(): any; - readConfigValue(config: any, key: string): any; - getDeprecationsProvider(): any; -} diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index f8736fb30f90e..feeb8e0bf6e4c 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -38,12 +38,8 @@ export default () => prod: Joi.boolean().default(Joi.ref('$prod')), }).default(), - dev: Joi.object({ - basePathProxyTarget: Joi.number().default(5603), - }).default(), - + dev: HANDLED_IN_NEW_PLATFORM, pid: HANDLED_IN_NEW_PLATFORM, - csp: HANDLED_IN_NEW_PLATFORM, server: Joi.object({ @@ -125,12 +121,10 @@ export default () => ops: Joi.object({ interval: Joi.number().default(5000), - cGroupOverrides: Joi.object().keys({ - cpuPath: Joi.string().default(), - cpuAcctPath: Joi.string().default(), - }), + cGroupOverrides: HANDLED_IN_NEW_PLATFORM, }).default(), + // still used by the legacy i18n mixin plugins: Joi.object({ paths: Joi.array().items(Joi.string()).default([]), scanDirs: Joi.array().items(Joi.string()).default([]), @@ -138,79 +132,9 @@ export default () => }).default(), path: HANDLED_IN_NEW_PLATFORM, - - stats: Joi.object({ - maximumWaitTimeForAllCollectorsInS: Joi.number().default(60), - }).default(), - - status: Joi.object({ - allowAnonymous: Joi.boolean().default(false), - }).default(), - map: Joi.object({ - includeElasticMapsService: Joi.boolean().default(true), - proxyElasticMapsServiceInMaps: Joi.boolean().default(false), - tilemap: Joi.object({ - url: Joi.string(), - options: Joi.object({ - attribution: Joi.string(), - minZoom: Joi.number().min(0, 'Must be 0 or higher').default(0), - maxZoom: Joi.number().default(10), - tileSize: Joi.number(), - subdomains: Joi.array().items(Joi.string()).single(), - errorTileUrl: Joi.string().uri(), - tms: Joi.boolean(), - reuseTiles: Joi.boolean(), - bounds: Joi.array().items(Joi.array().items(Joi.number()).min(2).required()).min(2), - default: Joi.boolean(), - }).default({ - default: true, - }), - }).default(), - regionmap: Joi.object({ - includeElasticMapsService: Joi.boolean().default(true), - layers: Joi.array() - .items( - Joi.object({ - url: Joi.string(), - format: Joi.object({ - type: Joi.string().default('geojson'), - }).default({ - type: 'geojson', - }), - meta: Joi.object({ - feature_collection_path: Joi.string().default('data'), - }).default({ - feature_collection_path: 'data', - }), - attribution: Joi.string(), - name: Joi.string(), - fields: Joi.array().items( - Joi.object({ - name: Joi.string(), - description: Joi.string(), - }) - ), - }) - ) - .default([]), - }).default(), - manifestServiceUrl: Joi.string().default('').allow(''), - emsFileApiUrl: Joi.string().default('https://vector.maps.elastic.co'), - emsTileApiUrl: Joi.string().default('https://tiles.maps.elastic.co'), - emsLandingPageUrl: Joi.string().default('https://maps.elastic.co/v7.9'), - emsFontLibraryUrl: Joi.string().default( - 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf' - ), - emsTileLayerId: Joi.object({ - bright: Joi.string().default('road_map'), - desaturated: Joi.string().default('road_map_desaturated'), - dark: Joi.string().default('dark_map'), - }).default({ - bright: 'road_map', - desaturated: 'road_map_desaturated', - dark: 'dark_map', - }), - }).default(), + stats: HANDLED_IN_NEW_PLATFORM, + status: HANDLED_IN_NEW_PLATFORM, + map: HANDLED_IN_NEW_PLATFORM, i18n: Joi.object({ locale: Joi.string().default('en'), @@ -225,8 +149,5 @@ export default () => autocompleteTimeout: Joi.number().integer().min(1).default(1000), }).default(), - savedObjects: Joi.object({ - maxImportPayloadBytes: Joi.number().default(10485760), - maxImportExportSize: Joi.number().default(10000), - }).default(), + savedObjects: HANDLED_IN_NEW_PLATFORM, }).default(); diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 3cfda0e0696bb..1718a9a8f55da 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -26,11 +26,10 @@ import { LoggerFactory, PackageInfo, LegacyServiceSetupDeps, - LegacyServiceDiscoverPlugins, } from '../../core/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LegacyConfig, ILegacyInternals } from '../../core/server/legacy'; +import { LegacyConfig } from '../../core/server/legacy'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { UiPlugins } from '../../core/server/plugins'; @@ -58,9 +57,7 @@ export interface PluginsSetup { export interface KibanaCore { __internals: { - elasticsearch: LegacyServiceSetupDeps['core']['elasticsearch']; hapiServer: LegacyServiceSetupDeps['core']['http']['server']; - legacy: ILegacyInternals; rendering: LegacyServiceSetupDeps['core']['rendering']; uiPlugins: UiPlugins; }; @@ -90,31 +87,18 @@ export interface NewPlatform { stop: null; } -export type LegacyPlugins = Pick< - LegacyServiceDiscoverPlugins, - 'pluginSpecs' | 'disabledPluginSpecs' | 'uiExports' ->; - // eslint-disable-next-line import/no-default-export export default class KbnServer { public readonly newPlatform: NewPlatform; public server: Server; public inject: Server['inject']; - public pluginSpecs: any[]; - public uiBundles: any; - constructor( - settings: Record, - config: KibanaConfig, - core: KibanaCore, - legacyPlugins: LegacyPlugins - ); + constructor(settings: Record, config: KibanaConfig, core: KibanaCore); public ready(): Promise; public mixin(...fns: KbnMixinFunc[]): Promise; public listen(): Promise; public close(): Promise; - public afterPluginsInit(callback: () => void): void; public applyLoggingConfiguration(settings: any): void; public config: KibanaConfig; } diff --git a/src/legacy/server/kbn_server.js b/src/legacy/server/kbn_server.js index 107e5f6387833..e29563a7c6266 100644 --- a/src/legacy/server/kbn_server.js +++ b/src/legacy/server/kbn_server.js @@ -30,7 +30,6 @@ import { loggingMixin } from './logging'; import warningsMixin from './warnings'; import configCompleteMixin from './config/complete'; import { optimizeMixin } from '../../optimize'; -import * as Plugins from './plugins'; import { uiMixin } from '../ui'; import { i18nMixin } from './i18n'; @@ -47,9 +46,8 @@ export default class KbnServer { * @param {Record} settings * @param {KibanaConfig} config * @param {KibanaCore} core - * @param {LegacyPlugins} legacyPlugins */ - constructor(settings, config, core, legacyPlugins) { + constructor(settings, config, core) { this.name = pkg.name; this.version = pkg.version; this.build = pkg.build || false; @@ -74,14 +72,8 @@ export default class KbnServer { stop: null, }; - this.uiExports = legacyPlugins.uiExports; - this.pluginSpecs = legacyPlugins.pluginSpecs; - this.disabledPluginSpecs = legacyPlugins.disabledPluginSpecs; - this.ready = constant( this.mixin( - Plugins.waitForInitSetupMixin, - // Sets global HTTP behaviors httpMixin, @@ -93,22 +85,13 @@ export default class KbnServer { // scan translations dirs, register locale files and initialize i18n engine. i18nMixin, - // find plugins and set this.plugins and this.pluginSpecs - Plugins.scanMixin, - // tell the config we are done loading plugins configCompleteMixin, uiMixin, // setup routes that serve the @kbn/optimizer output - optimizeMixin, - - // initialize the plugins - Plugins.initializeMixin, - - // notify any deferred setup logic that plugins have initialized - Plugins.waitForInitResolveMixin + optimizeMixin ) ); diff --git a/src/legacy/server/plugins/index.js b/src/legacy/server/plugins/index.js deleted file mode 100644 index 1511b63b519ae..0000000000000 --- a/src/legacy/server/plugins/index.js +++ /dev/null @@ -1,22 +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 { scanMixin } from './scan_mixin'; -export { initializeMixin } from './initialize_mixin'; -export { waitForInitSetupMixin, waitForInitResolveMixin } from './wait_for_plugins_init'; diff --git a/src/legacy/server/plugins/initialize_mixin.js b/src/legacy/server/plugins/initialize_mixin.js deleted file mode 100644 index ccf4cd1c1a404..0000000000000 --- a/src/legacy/server/plugins/initialize_mixin.js +++ /dev/null @@ -1,47 +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 { callPluginHook } from './lib'; - -/** - * KbnServer mixin that initializes all plugins found in ./scan mixin - * @param {KbnServer} kbnServer - * @param {Hapi.Server} server - * @param {Config} config - * @return {Promise} - */ -export async function initializeMixin(kbnServer, server, config) { - if (!config.get('plugins.initialize')) { - server.log(['info'], 'Plugin initialization disabled.'); - return; - } - - async function callHookOnPlugins(hookName) { - const { plugins } = kbnServer; - const ids = plugins.map((p) => p.id); - - for (const id of ids) { - await callPluginHook(hookName, plugins, id, []); - } - } - - await callHookOnPlugins('preInit'); - await callHookOnPlugins('init'); - await callHookOnPlugins('postInit'); -} diff --git a/src/legacy/server/plugins/lib/call_plugin_hook.js b/src/legacy/server/plugins/lib/call_plugin_hook.js deleted file mode 100644 index b665869f5d25f..0000000000000 --- a/src/legacy/server/plugins/lib/call_plugin_hook.js +++ /dev/null @@ -1,50 +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 { last } from 'lodash'; - -export async function callPluginHook(hookName, plugins, id, history) { - const plugin = plugins.find((plugin) => plugin.id === id); - - // make sure this is a valid plugin id - if (!plugin) { - if (history.length) { - throw new Error(`Unmet requirement "${id}" for plugin "${last(history)}"`); - } else { - throw new Error(`Unknown plugin "${id}"`); - } - } - - const circleStart = history.indexOf(id); - const path = [...history, id]; - - // make sure we are not trying to load a dependency within itself - if (circleStart > -1) { - const circle = path.slice(circleStart); - throw new Error(`circular dependency found: "${circle.join(' -> ')}"`); - } - - // call hook on all dependencies - for (const req of plugin.requiredIds) { - await callPluginHook(hookName, plugins, req, path); - } - - // call hook on this plugin - await plugin[hookName](); -} diff --git a/src/legacy/server/plugins/lib/call_plugin_hook.test.js b/src/legacy/server/plugins/lib/call_plugin_hook.test.js deleted file mode 100644 index 30dc2d91a9ab2..0000000000000 --- a/src/legacy/server/plugins/lib/call_plugin_hook.test.js +++ /dev/null @@ -1,101 +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 sinon from 'sinon'; -import { callPluginHook } from './call_plugin_hook'; - -describe('server/plugins/callPluginHook', () => { - it('should call in correct order based on requirements', async () => { - const plugins = [ - { - id: 'foo', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['bar', 'baz'], - }, - { - id: 'bar', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: [], - }, - { - id: 'baz', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['bar'], - }, - ]; - - await callPluginHook('init', plugins, 'foo', []); - const [foo, bar, baz] = plugins; - sinon.assert.calledOnce(foo.init); - sinon.assert.calledTwice(bar.init); - sinon.assert.calledOnce(baz.init); - sinon.assert.callOrder(bar.init, baz.init, foo.init); - }); - - it('throws meaningful error when required plugin is missing', async () => { - const plugins = [ - { - id: 'foo', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['bar'], - }, - ]; - - try { - await callPluginHook('init', plugins, 'foo', []); - throw new Error('expected callPluginHook to throw'); - } catch (error) { - expect(error.message).toContain('"bar" for plugin "foo"'); - } - }); - - it('throws meaningful error when dependencies are circular', async () => { - const plugins = [ - { - id: 'foo', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['bar'], - }, - { - id: 'bar', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['baz'], - }, - { - id: 'baz', - init: sinon.spy(), - preInit: sinon.spy(), - requiredIds: ['foo'], - }, - ]; - - try { - await callPluginHook('init', plugins, 'foo', []); - throw new Error('expected callPluginHook to throw'); - } catch (error) { - expect(error.message).toContain('foo -> bar -> baz -> foo'); - } - }); -}); diff --git a/src/legacy/server/plugins/lib/index.js b/src/legacy/server/plugins/lib/index.js deleted file mode 100644 index 2329d24498b6b..0000000000000 --- a/src/legacy/server/plugins/lib/index.js +++ /dev/null @@ -1,21 +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 { callPluginHook } from './call_plugin_hook'; -export { Plugin } from './plugin'; diff --git a/src/legacy/server/plugins/lib/plugin.js b/src/legacy/server/plugins/lib/plugin.js deleted file mode 100644 index 48389061199ff..0000000000000 --- a/src/legacy/server/plugins/lib/plugin.js +++ /dev/null @@ -1,114 +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 { once } from 'lodash'; - -/** - * The server plugin class, used to extend the server - * and add custom behavior. A "scoped" plugin class is - * created by the PluginApi class and provided to plugin - * providers that automatically binds all but the `opts` - * arguments. - * - * @class Plugin - * @param {KbnServer} kbnServer - the KbnServer this plugin - * belongs to. - * @param {PluginDefinition} def - * @param {PluginSpec} spec - */ -export class Plugin { - constructor(kbnServer, spec) { - this.kbnServer = kbnServer; - this.spec = spec; - this.pkg = spec.getPkg(); - this.path = spec.getPath(); - this.id = spec.getId(); - this.version = spec.getVersion(); - this.requiredIds = spec.getRequiredPluginIds() || []; - this.externalPreInit = spec.getPreInitHandler(); - this.externalInit = spec.getInitHandler(); - this.externalPostInit = spec.getPostInitHandler(); - this.enabled = spec.isEnabled(kbnServer.config); - this.configPrefix = spec.getConfigPrefix(); - this.publicDir = spec.getPublicDir(); - - this.preInit = once(this.preInit); - this.init = once(this.init); - this.postInit = once(this.postInit); - } - - async preInit() { - if (this.externalPreInit) { - return await this.externalPreInit(this.kbnServer.server); - } - } - - async init() { - const { id, version, kbnServer, configPrefix } = this; - const { config } = kbnServer; - - // setup the hapi register function and get on with it - const register = async (server, options) => { - this._server = server; - this._options = options; - - server.logWithMetadata(['plugins', 'debug'], `Initializing plugin ${this.toString()}`, { - plugin: this, - }); - - if (this.publicDir) { - server.newPlatform.__internals.http.registerStaticDir( - `/plugins/${id}/{path*}`, - this.publicDir - ); - } - - if (this.externalInit) { - await this.externalInit(server, options); - } - }; - - await kbnServer.server.register({ - plugin: { register, name: id, version }, - options: config.has(configPrefix) ? config.get(configPrefix) : null, - }); - } - - async postInit() { - if (this.externalPostInit) { - return await this.externalPostInit(this.kbnServer.server); - } - } - - getServer() { - return this._server; - } - - getOptions() { - return this._options; - } - - toJSON() { - return this.pkg; - } - - toString() { - return `${this.id}@${this.version}`; - } -} diff --git a/src/legacy/server/plugins/scan_mixin.js b/src/legacy/server/plugins/scan_mixin.js deleted file mode 100644 index 89ebaf920d9d1..0000000000000 --- a/src/legacy/server/plugins/scan_mixin.js +++ /dev/null @@ -1,23 +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 { Plugin } from './lib'; - -export async function scanMixin(kbnServer) { - kbnServer.plugins = kbnServer.pluginSpecs.map((spec) => new Plugin(kbnServer, spec)); -} diff --git a/src/legacy/server/plugins/wait_for_plugins_init.js b/src/legacy/server/plugins/wait_for_plugins_init.js deleted file mode 100644 index 144eb5ef803cc..0000000000000 --- a/src/legacy/server/plugins/wait_for_plugins_init.js +++ /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. - */ - -/** - * Tracks the individual queue for each kbnServer, rather than attaching - * it to the kbnServer object via a property or something - * @type {WeakMap} - */ -const queues = new WeakMap(); - -export function waitForInitSetupMixin(kbnServer) { - queues.set(kbnServer, []); - - kbnServer.afterPluginsInit = function (callback) { - const queue = queues.get(kbnServer); - - if (!queue) { - throw new Error( - 'Plugins have already initialized. Only use this method for setup logic that must wait for plugins to initialize.' - ); - } - - queue.push(callback); - }; -} - -export async function waitForInitResolveMixin(kbnServer, server, config) { - const queue = queues.get(kbnServer); - queues.set(kbnServer, null); - - // only actually call the callbacks if we are really initializing - if (config.get('plugins.initialize')) { - for (const cb of queue) { - await cb(); - } - } -} diff --git a/src/legacy/types.ts b/src/legacy/types.ts deleted file mode 100644 index 43c9ac79538b1..0000000000000 --- a/src/legacy/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export * from './plugin_discovery/types'; diff --git a/src/legacy/ui/__tests__/fixtures/plugin_async_foo/index.js b/src/legacy/ui/__tests__/fixtures/plugin_async_foo/index.js deleted file mode 100644 index afe618c6d3d9c..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_async_foo/index.js +++ /dev/null @@ -1,40 +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 Bluebird from 'bluebird'; - -export default (kibana) => - new kibana.Plugin({ - config(Joi) { - return Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - delay: Joi.number().required(), - shared: Joi.string(), - }) - .default(); - }, - - uiExports: { - async injectDefaultVars(server, options) { - await Bluebird.delay(options.delay); - return { shared: options.shared }; - }, - }, - }); diff --git a/src/legacy/ui/__tests__/fixtures/plugin_async_foo/package.json b/src/legacy/ui/__tests__/fixtures/plugin_async_foo/package.json deleted file mode 100644 index fc1c8d8088f1b..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_async_foo/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "plugin_async_foo", - "version": "kibana" -} diff --git a/src/legacy/ui/__tests__/fixtures/plugin_bar/index.js b/src/legacy/ui/__tests__/fixtures/plugin_bar/index.js deleted file mode 100644 index 975a1dc7c92e7..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_bar/index.js +++ /dev/null @@ -1,36 +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 default (kibana) => - new kibana.Plugin({ - config(Joi) { - return Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - shared: Joi.string(), - }) - .default(); - }, - - uiExports: { - injectDefaultVars(server, options) { - return { shared: options.shared }; - }, - }, - }); diff --git a/src/legacy/ui/__tests__/fixtures/plugin_bar/package.json b/src/legacy/ui/__tests__/fixtures/plugin_bar/package.json deleted file mode 100644 index f79b807990dca..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_bar/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "plugin_bar", - "version": "kibana" -} diff --git a/src/legacy/ui/__tests__/fixtures/plugin_foo/index.js b/src/legacy/ui/__tests__/fixtures/plugin_foo/index.js deleted file mode 100644 index 975a1dc7c92e7..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_foo/index.js +++ /dev/null @@ -1,36 +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 default (kibana) => - new kibana.Plugin({ - config(Joi) { - return Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - shared: Joi.string(), - }) - .default(); - }, - - uiExports: { - injectDefaultVars(server, options) { - return { shared: options.shared }; - }, - }, - }); diff --git a/src/legacy/ui/__tests__/fixtures/plugin_foo/package.json b/src/legacy/ui/__tests__/fixtures/plugin_foo/package.json deleted file mode 100644 index c1b7ddd35c9a2..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/plugin_foo/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "plugin_foo", - "version": "kibana" -} diff --git a/src/legacy/ui/__tests__/fixtures/test_app/index.js b/src/legacy/ui/__tests__/fixtures/test_app/index.js deleted file mode 100644 index 3eddefd618ce0..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/test_app/index.js +++ /dev/null @@ -1,39 +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 default (kibana) => - new kibana.Plugin({ - uiExports: { - app: { - name: 'test_app', - main: 'plugins/test_app/index.js', - }, - - injectDefaultVars() { - return { - from_defaults: true, - }; - }, - }, - init(server) { - server.injectUiAppVars('test_app', () => ({ - from_test_app: true, - })); - }, - }); diff --git a/src/legacy/ui/__tests__/fixtures/test_app/package.json b/src/legacy/ui/__tests__/fixtures/test_app/package.json deleted file mode 100644 index 3aeb029e4f4cc..0000000000000 --- a/src/legacy/ui/__tests__/fixtures/test_app/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "test_app", - "version": "kibana" -} diff --git a/src/legacy/ui/index.js b/src/legacy/ui/index.js index 05373fa5d1964..5c06cb4677347 100644 --- a/src/legacy/ui/index.js +++ b/src/legacy/ui/index.js @@ -18,4 +18,3 @@ */ export { uiMixin } from './ui_mixin'; -export { collectUiExports } from './ui_exports'; diff --git a/src/legacy/ui/ui_exports/README.md b/src/legacy/ui/ui_exports/README.md deleted file mode 100644 index 7fb117b1c25b9..0000000000000 --- a/src/legacy/ui/ui_exports/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# UI Exports - -When defining a Plugin, the `uiExports` key can be used to define a map of export types to values that will be used to configure the UI system. A common use for `uiExports` is `uiExports.app`, which defines the configuration of a [`UiApp`][UiApp] and teaches the UI System how to render, bundle and tell the user about an application. - - -## `collectUiExports(pluginSpecs): { [type: string]: any }` - -This function produces the object commonly found at `kbnServer.uiExports`. This object is created by calling `collectPluginExports()` with a standard set of export type reducers and defaults for the UI System. - -### export type reducers - -The [`ui_export_types` module][UiExportTypes] defines the reducer used for each uiExports key (or `type`). The name of every export in [./ui_export_types/index.js][UiExportTypes] is a key that plugins can define in their `uiExports` specification and the value of those exports are reducers that `collectPluginExports()` will call to produce the merged result of all export specs. - -### example - UiApps - -Plugin authors can define a new UiApp in their plugin specification like so: - -```js -// a single app export -export default function (kibana) { - return new kibana.Plugin({ - //... - uiExports: { - app: { - // uiApp spec options go here - } - } - }) -} - -// apps can also export multiple apps -export default function (kibana) { - return new kibana.Plugin({ - //... - uiExports: { - apps: [ - { /* uiApp spec options */ }, - { /* second uiApp spec options */ }, - ] - } - }) -} -``` - -To handle this export type, the [ui_export_types][UiExportTypes] module exports two reducers, one named `app` and the other `apps`. - -```js -export const app = ... -export const apps = ... -``` - -These reducers are defined in [`ui_export_types/ui_apps`][UiAppExportType] and have the exact same definition: - -```js -// `wrap()` produces a reducer by wrapping a base reducer with modifiers. -// All but the last argument are modifiers that take a reducer and return -// an alternate reducer to use in it's place. -// -// Most wrappers call their target reducer with slightly different -// arguments. This allows composing standard reducer modifications for -// reuse, consistency, and easy reference (once you get the hang of it). -wrap( - // calls the next reducer with the `type` set to `uiAppSpecs`, ignoring - // the key the plugin author used to define this spec ("app" or "apps" - // in this example) - alias('uiAppSpecs'), - - // calls the next reducer with the `spec` set to the result of calling - // `applySpecDefaults(spec, type, pluginSpec)` which merges some defaults - // from the `PluginSpec` because we want uiAppSpecs to be useful individually - mapSpec(applySpecDefaults), - - // writes this spec to `acc[type]` (`acc.uiAppSpecs` in this example since - // the type was set to `uiAppSpecs` by `alias()`). It does this by concatenating - // the current value and the spec into an array. If either item is already - // an array its items are added to the result individually. If either item - // is undefined it is ignored. - // - // NOTE: since flatConcatAtType is last it isn't a wrapper, it's - // just a normal reducer - flatConcatAtType -) -``` - -This reducer format was chosen so that it will be easier to look back at these reducers and see that `app` and `apps` export specs are written to `kbnServer.uiExports.uiAppSpecs`, with defaults applied, in an array. - -### defaults - -The [`ui_exports/ui_export_defaults`][UiExportDefaults] module defines the default shape of the uiExports object produced by `collectUiExports()`. The defaults generally describe the `uiExports` from the UI System itself, like default visTypes and such. - -[UiExportDefaults]: ./ui_export_defaults.js "uiExport defaults definition" -[UiExportTypes]: ./ui_export_types/index.js "Index of default ui_export_types module" -[UiAppExportType]: ./ui_export_types/ui_apps.js "UiApp extension type definition" -[PluginSpec]: ../../plugin_discovery/plugin_spec/plugin_spec.js "PluginSpec class definition" -[PluginDiscovery]: '../../plugin_discovery' "plugin_discovery module" \ No newline at end of file diff --git a/src/legacy/ui/ui_exports/collect_ui_exports.ts b/src/legacy/ui/ui_exports/collect_ui_exports.ts deleted file mode 100644 index edb2a11dc0527..0000000000000 --- a/src/legacy/ui/ui_exports/collect_ui_exports.ts +++ /dev/null @@ -1,31 +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 { LegacyUiExports } from '../../../core/server'; - -// @ts-ignore -import { UI_EXPORT_DEFAULTS } from './ui_export_defaults'; -// @ts-ignore -import * as uiExportTypeReducers from './ui_export_types'; -// @ts-ignore -import { reduceExportSpecs } from '../../plugin_discovery'; - -export function collectUiExports(pluginSpecs: unknown[]): LegacyUiExports { - return reduceExportSpecs(pluginSpecs, uiExportTypeReducers, UI_EXPORT_DEFAULTS); -} diff --git a/src/legacy/ui/ui_exports/index.js b/src/legacy/ui/ui_exports/index.js deleted file mode 100644 index 56db698dc7b03..0000000000000 --- a/src/legacy/ui/ui_exports/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { collectUiExports } from './collect_ui_exports'; diff --git a/src/legacy/ui/ui_exports/ui_export_defaults.js b/src/legacy/ui/ui_exports/ui_export_defaults.js deleted file mode 100644 index 227954155ce88..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_defaults.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export const UI_EXPORT_DEFAULTS = {}; diff --git a/src/legacy/ui/ui_exports/ui_export_types/index.js b/src/legacy/ui/ui_exports/ui_export_types/index.js deleted file mode 100644 index 9ff6a53f4afb9..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/index.js +++ /dev/null @@ -1,36 +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 { injectDefaultVars, replaceInjectedVars } from './modify_injected_vars'; - -export { - mappings, - migrations, - savedObjectSchemas, - savedObjectsManagement, - validations, -} from './saved_object'; - -export { taskDefinitions } from './task_definitions'; - -export { link, links } from './ui_nav_links'; - -export { uiSettingDefaults } from './ui_settings'; - -export { unknown } from './unknown'; diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_injected_vars.js b/src/legacy/ui/ui_exports/ui_export_types/modify_injected_vars.js deleted file mode 100644 index 4bb9f350bd959..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_injected_vars.js +++ /dev/null @@ -1,32 +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 { flatConcatAtType } from './reduce'; -import { wrap, alias, mapSpec } from './modify_reduce'; - -export const replaceInjectedVars = wrap(alias('injectedVarsReplacers'), flatConcatAtType); - -export const injectDefaultVars = wrap( - alias('defaultInjectedVarProviders'), - mapSpec((spec, type, pluginSpec) => ({ - pluginSpec, - fn: spec, - })), - flatConcatAtType -); diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/alias.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/alias.js deleted file mode 100644 index a894e59a03c81..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/alias.js +++ /dev/null @@ -1,28 +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 a reducer wrapper which, when called with a reducer, creates a new - * reducer that replaces the `type` value with `newType` before delegating to - * the wrapped reducer - * @param {String} newType - * @return {Function} - */ -export const alias = (newType) => (next) => (acc, spec, type, pluginSpec) => - next(acc, spec, newType, pluginSpec); diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/debug.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/debug.js deleted file mode 100644 index c40bca59fe14c..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/debug.js +++ /dev/null @@ -1,31 +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 { mapSpec } from './map_spec'; - -/** - * Reducer wrapper which, replaces the `spec` with the details about the definition - * of that spec - * @type {Function} - */ -export const debug = mapSpec((spec, type, pluginSpec) => ({ - spec, - type, - pluginSpec, -})); diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/index.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/index.js deleted file mode 100644 index 54c81fefdd08a..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/index.js +++ /dev/null @@ -1,24 +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 { alias } from './alias'; -export { debug } from './debug'; -export { mapSpec } from './map_spec'; -export { wrap } from './wrap'; -export { uniqueKeys } from './unique_keys'; diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/map_spec.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/map_spec.js deleted file mode 100644 index 5970c45e7445e..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/map_spec.js +++ /dev/null @@ -1,29 +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 a reducer wrapper which, when called with a reducer, creates a new - * reducer that replaces the `specs` value with the result of calling - * `mapFn(spec, type, pluginSpec)` before delegating to the wrapped - * reducer - * @param {Function} mapFn receives `(specs, type, pluginSpec)` - * @return {Function} - */ -export const mapSpec = (mapFn) => (next) => (acc, spec, type, pluginSpec) => - next(acc, mapFn(spec, type, pluginSpec), type, pluginSpec); diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/unique_keys.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/unique_keys.js deleted file mode 100644 index dedcd057b09e3..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/unique_keys.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const pluginId = (pluginSpec) => (pluginSpec.id ? pluginSpec.id() : pluginSpec.getId()); - -export const uniqueKeys = (sourceType) => (next) => (acc, spec, type, pluginSpec) => { - const duplicates = Object.keys(spec).filter((key) => acc[type] && acc[type].hasOwnProperty(key)); - - if (duplicates.length) { - throw new Error( - `${pluginId(pluginSpec)} defined duplicate ${sourceType || type} values: ${duplicates}` - ); - } - - return next(acc, spec, type, pluginSpec); -}; diff --git a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/wrap.js b/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/wrap.js deleted file mode 100644 index f84d83ed7c845..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/modify_reduce/wrap.js +++ /dev/null @@ -1,45 +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. - */ - -/** - * Wrap a function with any number of wrappers. Wrappers - * are functions that take a reducer and return a reducer - * that should be called in its place. The wrappers will - * be called in reverse order for setup and then in the - * order they are defined when the resulting reducer is - * executed. - * - * const reducer = wrap( - * next => (acc) => acc[1] = 'a', - * next => (acc) => acc[1] = 'b', - * next => (acc) => acc[1] = 'c' - * ) - * - * reducer('foo') //=> 'fco' - * - * @param {Function} ...wrappers - * @param {Function} reducer - * @return {Function} - */ -export function wrap(...args) { - const reducer = args[args.length - 1]; - const wrappers = args.slice(0, -1); - - return wrappers.reverse().reduce((acc, wrapper) => wrapper(acc), reducer); -} diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_at_type.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_at_type.js deleted file mode 100644 index 5fcbcac463392..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_at_type.js +++ /dev/null @@ -1,28 +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 { createTypeReducer, flatConcat } from './lib'; - -/** - * Reducer that merges two values concatenating all values - * into a flattened array - * @param {Any} [initial] - * @return {Function} - */ -export const flatConcatAtType = createTypeReducer(flatConcat); diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_values_at_type.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_values_at_type.js deleted file mode 100644 index 229c5be24aac5..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/flat_concat_values_at_type.js +++ /dev/null @@ -1,30 +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 { createTypeReducer, flatConcat, mergeWith } from './lib'; - -/** - * Reducer that merges specs by concatenating the values of - * all keys in accumulator and spec with the same logic as concat - * @param {[type]} initial [description] - * @return {[type]} [description] - */ -export const flatConcatValuesAtType = createTypeReducer((objectA, objectB) => - mergeWith(objectA || {}, objectB || {}, flatConcat) -); diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/index.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/index.js deleted file mode 100644 index 7dc1ba60fb3cb..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/index.js +++ /dev/null @@ -1,22 +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 { mergeAtType } from './merge_at_type'; -export { flatConcatValuesAtType } from './flat_concat_values_at_type'; -export { flatConcatAtType } from './flat_concat_at_type'; diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/create_type_reducer.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/create_type_reducer.js deleted file mode 100644 index bf4793c208308..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/create_type_reducer.js +++ /dev/null @@ -1,32 +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 a reducer that reduces the values within `acc[type]` by calling - * reducer with signature: - * - * reducer(acc[type], spec, type, pluginSpec) - * - * @param {Function} reducer - * @return {Function} - */ -export const createTypeReducer = (reducer) => (acc, spec, type, pluginSpec) => ({ - ...acc, - [type]: reducer(acc[type], spec, type, pluginSpec), -}); diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/flat_concat.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/flat_concat.js deleted file mode 100644 index 1337c8a85d5b4..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/flat_concat.js +++ /dev/null @@ -1,27 +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. - */ - -/** - * Concatenate two values into a single array, ignoring either - * value if it is undefined and flattening the value if it is an array - * @param {Array|T} a - * @param {Array} b - * @return {Array} - */ -export const flatConcat = (a, b) => [].concat(a === undefined ? [] : a, b === undefined ? [] : b); diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/index.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/index.js deleted file mode 100644 index e4281caebe245..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/index.js +++ /dev/null @@ -1,22 +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 { flatConcat } from './flat_concat'; -export { mergeWith } from './merge_with'; -export { createTypeReducer } from './create_type_reducer'; diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/merge_with.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/merge_with.js deleted file mode 100644 index 6c7d31e6fd74d..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/lib/merge_with.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const uniqueConcat = (arrayA, arrayB) => - arrayB.reduce((acc, key) => (acc.includes(key) ? acc : acc.concat(key)), arrayA); - -/** - * Assign the keys from both objA and objB to target after passing the - * current and new value through merge as `(target[key], source[key])` - * @param {Object} objA - * @param {Object} objB - * @param {Function} merge - * @return {Object} target - */ -export function mergeWith(objA, objB, merge) { - const target = {}; - const keys = uniqueConcat(Object.keys(objA), Object.keys(objB)); - for (const key of keys) { - target[key] = merge(objA[key], objB[key]); - } - return target; -} diff --git a/src/legacy/ui/ui_exports/ui_export_types/reduce/merge_at_type.js b/src/legacy/ui/ui_exports/ui_export_types/reduce/merge_at_type.js deleted file mode 100644 index 4f5a501253851..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/reduce/merge_at_type.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. - */ - -import { createTypeReducer } from './lib'; - -export const mergeAtType = createTypeReducer((a, b) => ({ - ...a, - ...b, -})); diff --git a/src/legacy/ui/ui_exports/ui_export_types/saved_object.js b/src/legacy/ui/ui_exports/ui_export_types/saved_object.js deleted file mode 100644 index be6898d3e642c..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/saved_object.js +++ /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 { flatConcatAtType, mergeAtType } from './reduce'; -import { alias, mapSpec, uniqueKeys, wrap } from './modify_reduce'; - -// mapping types -export const mappings = wrap( - alias('savedObjectMappings'), - mapSpec((spec, type, pluginSpec) => ({ - pluginId: pluginSpec.getId(), - properties: spec, - })), - flatConcatAtType -); - -const pluginId = (pluginSpec) => (pluginSpec.id ? pluginSpec.id() : pluginSpec.getId()); - -// Combines the `migrations` property of each plugin, -// ensuring that properties are unique across plugins -// and has migrations defined where the mappings are defined. -// See saved_objects/migrations for more details. -export const migrations = wrap( - alias('savedObjectMigrations'), - (next) => (acc, spec, type, pluginSpec) => { - const mappings = pluginSpec.getExportSpecs().mappings || {}; - const invalidMigrationTypes = Object.keys(spec).filter((type) => !mappings[type]); - if (invalidMigrationTypes.length) { - throw new Error( - 'Migrations and mappings must be defined together in the uiExports of a single plugin. ' + - `${pluginId(pluginSpec)} defines migrations for types ${invalidMigrationTypes.join( - ', ' - )} but does not define their mappings.` - ); - } - return next(acc, spec, type, pluginSpec); - }, - uniqueKeys(), - mergeAtType -); - -export const savedObjectSchemas = wrap(uniqueKeys(), mergeAtType); - -export const savedObjectsManagement = wrap(uniqueKeys(), mergeAtType); - -// Combines the `validations` property of each plugin, -// ensuring that properties are unique across plugins. -// See saved_objects/validation for more details. -export const validations = wrap(alias('savedObjectValidations'), uniqueKeys(), mergeAtType); diff --git a/src/legacy/ui/ui_exports/ui_export_types/task_definitions.js b/src/legacy/ui/ui_exports/ui_export_types/task_definitions.js deleted file mode 100644 index 8a0ed85d86f3e..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/task_definitions.js +++ /dev/null @@ -1,24 +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 { mergeAtType } from './reduce'; -import { alias, wrap, uniqueKeys } from './modify_reduce'; - -// How plugins define tasks that the task manager can run. -export const taskDefinitions = wrap(alias('taskDefinitions'), uniqueKeys(), mergeAtType); diff --git a/src/legacy/ui/ui_exports/ui_export_types/ui_nav_links.js b/src/legacy/ui/ui_exports/ui_export_types/ui_nav_links.js deleted file mode 100644 index 34aff7463a249..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/ui_nav_links.js +++ /dev/null @@ -1,24 +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 { flatConcatAtType } from './reduce'; -import { wrap, alias } from './modify_reduce'; - -export const links = wrap(alias('navLinkSpecs'), flatConcatAtType); -export const link = wrap(alias('navLinkSpecs'), flatConcatAtType); diff --git a/src/legacy/ui/ui_exports/ui_export_types/ui_settings.js b/src/legacy/ui/ui_exports/ui_export_types/ui_settings.js deleted file mode 100644 index 8d88490579c21..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/ui_settings.js +++ /dev/null @@ -1,23 +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 { mergeAtType } from './reduce'; -import { wrap, uniqueKeys } from './modify_reduce'; - -export const uiSettingDefaults = wrap(uniqueKeys(), mergeAtType); diff --git a/src/legacy/ui/ui_exports/ui_export_types/unknown.js b/src/legacy/ui/ui_exports/ui_export_types/unknown.js deleted file mode 100644 index a12a514d2e6bf..0000000000000 --- a/src/legacy/ui/ui_exports/ui_export_types/unknown.js +++ /dev/null @@ -1,23 +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 { flatConcatAtType } from './reduce'; -import { wrap, alias, debug } from './modify_reduce'; - -export const unknown = wrap(debug, alias('unknown'), flatConcatAtType); diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index e3b7c1e0c3ff9..2983dbbc28667 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -67,115 +67,108 @@ export function uiRenderMixin(kbnServer, server, config) { }, }); - // register the bootstrap.js route after plugins are initialized so that we can - // detect if any default auth strategies were registered - kbnServer.afterPluginsInit(() => { - const authEnabled = !!server.auth.settings.default; - - server.route({ - path: '/bootstrap.js', - method: 'GET', - config: { - tags: ['api'], - auth: authEnabled ? { mode: 'try' } : false, - }, - async handler(request, h) { - const soClient = kbnServer.newPlatform.start.core.savedObjects.getScopedClient( - KibanaRequest.from(request) - ); - const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient(soClient); - - const darkMode = - !authEnabled || request.auth.isAuthenticated - ? await uiSettings.get('theme:darkMode') - : false; - - const themeVersion = - !authEnabled || request.auth.isAuthenticated - ? await uiSettings.get('theme:version') - : 'v7'; - - const themeTag = `${themeVersion === 'v7' ? 'v7' : 'v8'}${darkMode ? 'dark' : 'light'}`; - - const buildHash = server.newPlatform.env.packageInfo.buildNum; - const basePath = config.get('server.basePath'); - - const regularBundlePath = `${basePath}/${buildHash}/bundles`; - - const styleSheetPaths = [ - `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, - ...(darkMode - ? [ - themeVersion === 'v7' - ? `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}` - : `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkV8CssDistFilename}`, - `${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`, - `${basePath}/ui/legacy_dark_theme.css`, - ] - : [ - themeVersion === 'v7' - ? `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}` - : `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightV8CssDistFilename}`, - `${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`, - `${basePath}/ui/legacy_light_theme.css`, - ]), - ]; - - const kpUiPlugins = kbnServer.newPlatform.__internals.uiPlugins; - const kpPluginPublicPaths = new Map(); - const kpPluginBundlePaths = new Set(); - - // recursively iterate over the kpUiPlugin ids and their required bundles - // to populate kpPluginPublicPaths and kpPluginBundlePaths - (function readKpPlugins(ids) { - for (const id of ids) { - if (kpPluginPublicPaths.has(id)) { - continue; - } - - kpPluginPublicPaths.set(id, `${regularBundlePath}/plugin/${id}/`); - kpPluginBundlePaths.add(`${regularBundlePath}/plugin/${id}/${id}.plugin.js`); - readKpPlugins(kpUiPlugins.internal.get(id).requiredBundles); + const authEnabled = !!server.auth.settings.default; + server.route({ + path: '/bootstrap.js', + method: 'GET', + config: { + tags: ['api'], + auth: authEnabled ? { mode: 'try' } : false, + }, + async handler(request, h) { + const soClient = kbnServer.newPlatform.start.core.savedObjects.getScopedClient( + KibanaRequest.from(request) + ); + const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient(soClient); + + const darkMode = + !authEnabled || request.auth.isAuthenticated + ? await uiSettings.get('theme:darkMode') + : false; + + const themeVersion = + !authEnabled || request.auth.isAuthenticated ? await uiSettings.get('theme:version') : 'v7'; + + const themeTag = `${themeVersion === 'v7' ? 'v7' : 'v8'}${darkMode ? 'dark' : 'light'}`; + + const buildHash = server.newPlatform.env.packageInfo.buildNum; + const basePath = config.get('server.basePath'); + + const regularBundlePath = `${basePath}/${buildHash}/bundles`; + + const styleSheetPaths = [ + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, + ...(darkMode + ? [ + themeVersion === 'v7' + ? `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}` + : `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkV8CssDistFilename}`, + `${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`, + `${basePath}/ui/legacy_dark_theme.css`, + ] + : [ + themeVersion === 'v7' + ? `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}` + : `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightV8CssDistFilename}`, + `${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`, + `${basePath}/ui/legacy_light_theme.css`, + ]), + ]; + + const kpUiPlugins = kbnServer.newPlatform.__internals.uiPlugins; + const kpPluginPublicPaths = new Map(); + const kpPluginBundlePaths = new Set(); + + // recursively iterate over the kpUiPlugin ids and their required bundles + // to populate kpPluginPublicPaths and kpPluginBundlePaths + (function readKpPlugins(ids) { + for (const id of ids) { + if (kpPluginPublicPaths.has(id)) { + continue; } - })(kpUiPlugins.public.keys()); - - const jsDependencyPaths = [ - ...UiSharedDeps.jsDepFilenames.map( - (filename) => `${regularBundlePath}/kbn-ui-shared-deps/${filename}` - ), - `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, - - `${regularBundlePath}/core/core.entry.js`, - ...kpPluginBundlePaths, - ]; - - // These paths should align with the bundle routes configured in - // src/optimize/bundles_route/bundles_route.ts - const publicPathMap = JSON.stringify({ - core: `${regularBundlePath}/core/`, - 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`, - ...Object.fromEntries(kpPluginPublicPaths), - }); - - const bootstrap = new AppBootstrap({ - templateData: { - themeTag, - jsDependencyPaths, - styleSheetPaths, - publicPathMap, - }, - }); - - const body = await bootstrap.getJsFile(); - const etag = await bootstrap.getJsFileHash(); - - return h - .response(body) - .header('cache-control', 'must-revalidate') - .header('content-type', 'application/javascript') - .etag(etag); - }, - }); + + kpPluginPublicPaths.set(id, `${regularBundlePath}/plugin/${id}/`); + kpPluginBundlePaths.add(`${regularBundlePath}/plugin/${id}/${id}.plugin.js`); + readKpPlugins(kpUiPlugins.internal.get(id).requiredBundles); + } + })(kpUiPlugins.public.keys()); + + const jsDependencyPaths = [ + ...UiSharedDeps.jsDepFilenames.map( + (filename) => `${regularBundlePath}/kbn-ui-shared-deps/${filename}` + ), + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, + + `${regularBundlePath}/core/core.entry.js`, + ...kpPluginBundlePaths, + ]; + + // These paths should align with the bundle routes configured in + // src/optimize/bundles_route/bundles_route.ts + const publicPathMap = JSON.stringify({ + core: `${regularBundlePath}/core/`, + 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`, + ...Object.fromEntries(kpPluginPublicPaths), + }); + + const bootstrap = new AppBootstrap({ + templateData: { + themeTag, + jsDependencyPaths, + styleSheetPaths, + publicPathMap, + }, + }); + + const body = await bootstrap.getJsFile(); + const etag = await bootstrap.getJsFileHash(); + + return h + .response(body) + .header('cache-control', 'must-revalidate') + .header('content-type', 'application/javascript') + .etag(etag); + }, }); server.route({ @@ -191,19 +184,17 @@ export function uiRenderMixin(kbnServer, server, config) { }); async function renderApp(h) { - const app = { getId: () => 'core' }; const { http } = kbnServer.newPlatform.setup.core; const { savedObjects } = kbnServer.newPlatform.start.core; - const { rendering, legacy } = kbnServer.newPlatform.__internals; + const { rendering } = kbnServer.newPlatform.__internals; const req = KibanaRequest.from(h.request); const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient( savedObjects.getScopedClient(req) ); - const vars = await legacy.getVars(app.getId(), h.request, { + const vars = { apmConfig: getApmConfig(h.request.path), - }); + }; const content = await rendering.render(h.request, uiSettings, { - app, includeUserSettings: true, vars, }); diff --git a/src/plugins/advanced_settings/public/index.ts b/src/plugins/advanced_settings/public/index.ts index db478fa1579e6..0e621e7cd7d1a 100644 --- a/src/plugins/advanced_settings/public/index.ts +++ b/src/plugins/advanced_settings/public/index.ts @@ -17,11 +17,19 @@ * under the License. */ +import React from 'react'; import { PluginInitializerContext } from 'kibana/public'; import { AdvancedSettingsPlugin } from './plugin'; export { AdvancedSettingsSetup, AdvancedSettingsStart } from './types'; export { ComponentRegistry } from './component_registry'; -export { Field } from './management_app/components/field'; + +/** + * Exports the field component as a React.lazy component. We're explicitly naming it lazy here + * so any plugin that would import that can clearly see it's lazy loaded and can only be used + * inside a suspense context. + */ +const LazyField = React.lazy(() => import('./management_app/components/field')); +export { LazyField }; export function plugin(initializerContext: PluginInitializerContext) { return new AdvancedSettingsPlugin(); diff --git a/src/plugins/advanced_settings/public/management_app/components/advanced_settings_voice_announcement/__snapshots__/advanced_settings_voice_announcement.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/advanced_settings_voice_announcement/__snapshots__/advanced_settings_voice_announcement.test.tsx.snap index 490e105c18a7d..82c8dcd7f7ea1 100644 --- a/src/plugins/advanced_settings/public/management_app/components/advanced_settings_voice_announcement/__snapshots__/advanced_settings_voice_announcement.test.tsx.snap +++ b/src/plugins/advanced_settings/public/management_app/components/advanced_settings_voice_announcement/__snapshots__/advanced_settings_voice_announcement.test.tsx.snap @@ -3,6 +3,7 @@ exports[`Advanced Settings: Voice Announcement should render announcement 1`] = `
@@ -28,6 +29,7 @@ exports[`Advanced Settings: Voice Announcement should render announcement 1`] = exports[`Advanced Settings: Voice Announcement should render nothing 1`] = `
@@ -35,12 +37,11 @@ exports[`Advanced Settings: Voice Announcement should render nothing 1`] = ` delay={500} > { const filteredOptions = [...filteredSections]; return ( -
+
- + {this.props.queryText ? ( + + ) : ( + + )}
diff --git a/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap b/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap index 2aabacb061667..19bf9e6d73757 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap +++ b/src/plugins/advanced_settings/public/management_app/components/field/__snapshots__/field.test.tsx.snap @@ -15,7 +15,7 @@ exports[`Field for array setting should render as read only if saving is disable } fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

} fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

} fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

} fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

} fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

Setting is currently not saved.

@@ -364,7 +364,7 @@ exports[`Field for array setting should render user value if there is user value } fullWidth={true} - id="array:test:setting" + id="array:test:setting-group" title={

} fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

} fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

} fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

} fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

} fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

Setting is currently not saved.

@@ -807,7 +807,7 @@ exports[`Field for boolean setting should render user value if there is user val } fullWidth={true} - id="boolean:test:setting" + id="boolean:test:setting-group" title={

} fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

} fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

} fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

} fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

} fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

Setting is currently not saved.

@@ -1231,7 +1235,7 @@ exports[`Field for image setting should render user value if there is user value } fullWidth={true} - id="image:test:setting" + id="image:test:setting-group" title={

} fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

} fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

} fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

} fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

} fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

Setting is currently not saved.

@@ -1832,7 +1836,7 @@ exports[`Field for json setting should render user value if there is user value } fullWidth={true} - id="json:test:setting" + id="json:test:setting-group" title={

} fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

} fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

} fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

} fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

} fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

Setting is currently not saved.

@@ -2365,7 +2369,7 @@ exports[`Field for markdown setting should render user value if there is user va } fullWidth={true} - id="markdown:test:setting" + id="markdown:test:setting-group" title={

} fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

} fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

} fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

} fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

} fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

Setting is currently not saved.

@@ -2798,7 +2802,7 @@ exports[`Field for number setting should render user value if there is user valu } fullWidth={true} - id="number:test:setting" + id="number:test:setting-group" title={

} fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

} fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

} fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

} fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

} fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

Setting is currently not saved.

@@ -3291,7 +3295,7 @@ exports[`Field for select setting should render user value if there is user valu } fullWidth={true} - id="select:test:setting" + id="select:test:setting-group" title={

} fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

} fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

} fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

} fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

} fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

Setting is currently not saved.

@@ -3720,7 +3724,7 @@ exports[`Field for string setting should render user value if there is user valu } fullWidth={true} - id="string:test:setting" + id="string:test:setting-group" title={

} fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

} fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

} fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

} fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

} fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

Setting is currently not saved.

@@ -4133,7 +4137,7 @@ exports[`Field for stringWithValidation setting should render user value if ther } fullWidth={true} - id="string:test-validation:setting" + id="string:test-validation:setting-group" title={

{ } }; - renderField(id: string, setting: FieldSetting) { + renderField(setting: FieldSetting, ariaDescribedBy?: string) { const { enableSaving, unsavedChanges, loading } = this.props; const { name, @@ -301,10 +299,10 @@ export class Field extends PureComponent { defVal, ariaName, } = setting; - const a11yProps: { [key: string]: string } = unsavedChanges + const a11yProps: { [key: string]: string } = ariaDescribedBy ? { 'aria-label': ariaName, - 'aria-describedby': id, + 'aria-describedby': ariaDescribedBy, } : { 'aria-label': ariaName, @@ -370,6 +368,7 @@ export class Field extends PureComponent { ref={this.changeImageForm} fullWidth data-test-subj={`advancedSetting-editField-${name}`} + aria-label={name} /> ); } @@ -669,11 +668,12 @@ export class Field extends PureComponent { // eslint-disable-next-line @typescript-eslint/naming-convention 'mgtAdvancedSettings__field--invalid': isInvalid, }); - const id = setting.name; + const groupId = `${setting.name}-group`; + const unsavedId = `${setting.name}-unsaved`; return ( { fullWidth > <> - {this.renderField(id, setting)} + {this.renderField(setting, unsavedChanges ? `${groupId} ${unsavedId}` : undefined)} {unsavedChanges && ( -

+

{unsavedChanges.error ? unsavedChanges.error : i18n.translate('advancedSettings.field.settingIsUnsaved', { diff --git a/src/plugins/advanced_settings/public/management_app/components/field/index.ts b/src/plugins/advanced_settings/public/management_app/components/field/index.ts index d1b9b34515532..c486dc96bfc33 100644 --- a/src/plugins/advanced_settings/public/management_app/components/field/index.ts +++ b/src/plugins/advanced_settings/public/management_app/components/field/index.ts @@ -18,3 +18,6 @@ */ export { Field, getEditableValue } from './field'; + +// eslint-disable-next-line import/no-default-export +export { Field as default } from './field'; diff --git a/src/plugins/apm_oss/server/tutorial/index_pattern.json b/src/plugins/apm_oss/server/tutorial/index_pattern.json index bb42b223c85ed..dbceaacfb4a27 100644 --- a/src/plugins/apm_oss/server/tutorial/index_pattern.json +++ b/src/plugins/apm_oss/server/tutorial/index_pattern.json @@ -1,11 +1,11 @@ { "attributes": { - "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.stats.inactive_file.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.stats.inactive_file.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, "id": "apm-*", "type": "index-pattern", "version": "1" -} +} \ No newline at end of file diff --git a/src/plugins/apm_oss/server/tutorial/instructions/apm_agent_instructions.ts b/src/plugins/apm_oss/server/tutorial/instructions/apm_agent_instructions.ts index d2a4ee8297a11..a74223f28dd03 100644 --- a/src/plugins/apm_oss/server/tutorial/instructions/apm_agent_instructions.ts +++ b/src/plugins/apm_oss/server/tutorial/instructions/apm_agent_instructions.ts @@ -37,7 +37,7 @@ export const createNodeAgentInstructions = (apmServerUrl = '', secretToken = '') defaultMessage: 'Agents are libraries that run inside of your application process. \ APM services are created programmatically based on the `serviceName`. \ -This agent supports a vararity of frameworks but can also be used with your custom stack.', +This agent supports a variety of frameworks but can also be used with your custom stack.', }), commands: `// ${i18n.translate( 'apmOss.tutorial.nodeClient.configure.commands.addThisToTheFileTopComment', diff --git a/src/plugins/charts/public/services/colors/color_palette.ts b/src/plugins/charts/public/services/colors/color_palette.ts index 464e9e3a66101..e1c32fe68da12 100644 --- a/src/plugins/charts/public/services/colors/color_palette.ts +++ b/src/plugins/charts/public/services/colors/color_palette.ts @@ -17,8 +17,8 @@ * under the License. */ -import d3 from 'd3'; import _ from 'lodash'; +import { hsl } from 'color'; import { seedColors } from './seed_colors'; @@ -49,7 +49,7 @@ const fraction = function (goal: number) { * If the number is greater than the length of seed colors available, * new colors are generated up to the value of the input number. */ -export function createColorPalette(num?: any): string[] { +export function createColorPalette(num: number): string[] { if (!_.isNumber(num)) { throw new TypeError('ColorPaletteUtilService expects a number'); } @@ -58,7 +58,7 @@ export function createColorPalette(num?: any): string[] { const seedLength = seedColors.length; _.times(num - seedLength, function (i) { - colors.push(d3.hsl((fraction(i + seedLength + 1) * 360 + offset) % 360, 0.5, 0.5).toString()); + colors.push(hsl((fraction(i + seedLength + 1) * 360 + offset) % 360, 0.5, 0.5).hex()); }); return colors; diff --git a/src/plugins/charts/public/services/colors/colors_palette.test.ts b/src/plugins/charts/public/services/colors/colors_palette.test.ts index 6612447cefe9e..02ff5a6056d54 100644 --- a/src/plugins/charts/public/services/colors/colors_palette.test.ts +++ b/src/plugins/charts/public/services/colors/colors_palette.test.ts @@ -37,26 +37,32 @@ describe('Color Palette', () => { it('should throw an error if input is not a number', () => { expect(() => { + // @ts-expect-error createColorPalette(string); }).toThrowError(); expect(() => { + // @ts-expect-error createColorPalette(bool); }).toThrowError(); expect(() => { + // @ts-expect-error createColorPalette(nullValue); }).toThrowError(); expect(() => { + // @ts-expect-error createColorPalette(emptyArr); }).toThrowError(); expect(() => { + // @ts-expect-error createColorPalette(emptyObject); }).toThrowError(); expect(() => { + // @ts-expect-error createColorPalette(); }).toThrowError(); }); diff --git a/src/plugins/charts/public/services/colors/mapped_colors.test.ts b/src/plugins/charts/public/services/colors/mapped_colors.test.ts index e97ca8ac257b4..9d00bf098de4c 100644 --- a/src/plugins/charts/public/services/colors/mapped_colors.test.ts +++ b/src/plugins/charts/public/services/colors/mapped_colors.test.ts @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import d3 from 'd3'; +import Color from 'color'; import { coreMock } from '../../../../../core/public/mocks'; import { COLOR_MAPPING_SETTING } from '../../../common'; @@ -61,7 +61,7 @@ describe('Mapped Colors', () => { mappedColors.mapKeys(arr); const colorValues = _(mappedColors.mapping).values(); - expect(colorValues.includes(seedColors[0])).toBe(false); + expect(colorValues).not.toContain(seedColors[0]); expect(colorValues.uniq().size()).toBe(arr.length); }); @@ -78,8 +78,8 @@ describe('Mapped Colors', () => { }); it('should treat different formats of colors as equal', () => { - const color = d3.rgb(seedColors[0]); - const rgb = `rgb(${color.r}, ${color.g}, ${color.b})`; + const color = new Color(seedColors[0]); + const rgb = `rgb(${color.red()}, ${color.green()}, ${color.blue()})`; const newConfig = { bar: rgb }; config.set(COLOR_MAPPING_SETTING, newConfig); diff --git a/src/plugins/charts/public/services/colors/mapped_colors.ts b/src/plugins/charts/public/services/colors/mapped_colors.ts index 3b9e1501d638d..15f9be32b829c 100644 --- a/src/plugins/charts/public/services/colors/mapped_colors.ts +++ b/src/plugins/charts/public/services/colors/mapped_colors.ts @@ -18,14 +18,14 @@ */ import _ from 'lodash'; -import d3 from 'd3'; +import Color from 'color'; import { CoreSetup } from 'kibana/public'; import { COLOR_MAPPING_SETTING } from '../../../common'; import { createColorPalette } from './color_palette'; -const standardizeColor = (color: string) => d3.rgb(color).toString(); +const standardizeColor = (color: string) => new Color(color).hex().toLowerCase(); /** * Maintains a lookup table that associates the value (key) with a hex color (value) diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index fc88b31711b23..abef8afcc3985 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -182,6 +182,9 @@ function EditorUI({ initialTextValue }: EditorProps) { unsubscribeResizer(); clearSubscriptions(); window.removeEventListener('hashchange', onHashChange); + if (editorInstanceRef.current) { + editorInstanceRef.current.getCoreEditor().destroy(); + } }; }, [saveCurrentTextObject, initialTextValue, history, setInputEditor, settingsService]); diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx index dd5ef5209a244..44ed5f4b8051e 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx @@ -20,7 +20,7 @@ import { EuiScreenReaderOnly } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useRef } from 'react'; -import { expandLiteralStrings } from '../../../../../../../es_ui_shared/public'; +import { expandLiteralStrings } from '../../../../../shared_imports'; import { useEditorReadContext, useRequestReadContext, diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts index cfbd5691bc22b..d01adf332e24a 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts @@ -18,7 +18,8 @@ */ import { extractDeprecationMessages } from '../../../lib/utils'; -import { collapseLiteralStrings } from '../../../../../es_ui_shared/public'; +import { XJson } from '../../../../../es_ui_shared/public'; +const { collapseLiteralStrings } = XJson; // @ts-ignore import * as es from '../../../lib/es/es'; import { BaseResponseType } from '../../../types'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 469ef6d79fae5..393b7eee346f5 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -408,4 +408,8 @@ export class LegacyCoreEditor implements CoreEditor { }, ]); } + + destroy() { + this.editor.destroy(); + } } diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js index 1558cf0cb5554..bc0129850f299 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js @@ -18,7 +18,7 @@ */ import ace from 'brace'; -import { addXJsonToRules } from '../../../../../../es_ui_shared/public'; +import { addXJsonToRules } from '@kbn/ace'; export function addEOL(tokens, reg, nextIfEOL, normalNext) { if (typeof reg === 'object') { diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js index 448fd847aeacd..2f39689319389 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js @@ -19,7 +19,7 @@ import ace from 'brace'; import 'brace/mode/json'; -import { addXJsonToRules } from '../../../../../../es_ui_shared/public'; +import { addXJsonToRules } from '@kbn/ace'; const oop = ace.acequire('ace/lib/oop'); const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules').JsonHighlightRules; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js index 6079c9db40eef..03d5b10f82d01 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js @@ -18,7 +18,7 @@ */ import ace from 'brace'; -import { ScriptHighlightRules } from '../../../../../../es_ui_shared/public'; +import { ScriptHighlightRules } from '@kbn/ace'; const oop = ace.acequire('ace/lib/oop'); const TextMode = ace.acequire('ace/mode/text').Mode; diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js index c3fb879f2eeeb..04d3cd1a724e1 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js @@ -22,9 +22,11 @@ import $ from 'jquery'; import _ from 'lodash'; import { create } from '../create'; -import { collapseLiteralStrings } from '../../../../../../es_ui_shared/public'; +import { XJson } from '../../../../../../es_ui_shared/public'; import editorInput1 from './editor_input1.txt'; +const { collapseLiteralStrings } = XJson; + describe('Editor', () => { let input; diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index dbf4f1adcba0a..66324050bc2fa 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; import RowParser from '../../../lib/row_parser'; -import { collapseLiteralStrings } from '../../../../../es_ui_shared/public'; +import { XJson } from '../../../../../es_ui_shared/public'; import * as utils from '../../../lib/utils'; // @ts-ignore @@ -30,6 +30,8 @@ import { createTokenIterator } from '../../factories'; import Autocomplete from '../../../lib/autocomplete/autocomplete'; +const { collapseLiteralStrings } = XJson; + export class SenseEditor { currentReqRange: (Range & { markerRef: any }) | null; parser: any; diff --git a/src/plugins/console/public/lib/utils/index.ts b/src/plugins/console/public/lib/utils/index.ts index 917988e0e811b..b95680e5df47e 100644 --- a/src/plugins/console/public/lib/utils/index.ts +++ b/src/plugins/console/public/lib/utils/index.ts @@ -18,7 +18,9 @@ */ import _ from 'lodash'; -import { expandLiteralStrings, collapseLiteralStrings } from '../../../../es_ui_shared/public'; +import { XJson } from '../../../../es_ui_shared/public'; + +const { collapseLiteralStrings, expandLiteralStrings } = XJson; export function textFromRequest(request: any) { let data = request.data; diff --git a/src/plugins/console/public/shared_imports.ts b/src/plugins/console/public/shared_imports.ts index aa64091903fb7..36c50f9c51e0d 100644 --- a/src/plugins/console/public/shared_imports.ts +++ b/src/plugins/console/public/shared_imports.ts @@ -17,6 +17,8 @@ * under the License. */ -import { sendRequest } from '../../es_ui_shared/public'; +import { sendRequest, XJson } from '../../es_ui_shared/public'; -export { sendRequest }; +const { collapseLiteralStrings, expandLiteralStrings } = XJson; + +export { sendRequest, collapseLiteralStrings, expandLiteralStrings }; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index b71f4fff44ca5..d88d8f86b874c 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -268,4 +268,9 @@ export interface CoreEditor { * detects a change */ registerAutocompleter(autocompleter: AutoCompleterFunction): void; + + /** + * Release any resources in use by the editor. + */ + destroy(): void; } diff --git a/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx b/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx index d0442fbc26073..933d2766d13f4 100644 --- a/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx +++ b/src/plugins/dashboard/public/application/actions/expand_panel_action.tsx @@ -57,7 +57,7 @@ export class ExpandPanelAction implements ActionByType { return ( + embeddable.getRoot().isContainer && embeddable.getInput()?.viewMode !== ViewMode.VIEW && isReferenceOrValueEmbeddable(embeddable) && embeddable.inputIsRefType(embeddable.getInput()) diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index dd5eb1ee5ccaa..8ab2a458d78e0 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -66,7 +66,6 @@ import { ViewMode, ContainerOutput, EmbeddableInput, - SavedObjectEmbeddableInput, } from '../../../embeddable/public'; import { NavAction, SavedDashboardPanel } from '../types'; @@ -178,7 +177,7 @@ export class DashboardAppController { chrome.docTitle.change(dash.title); } - const incomingEmbeddable = embeddable + let incomingEmbeddable = embeddable .getStateTransfer(scopedHistory()) .getIncomingEmbeddablePackage(); @@ -344,10 +343,22 @@ export class DashboardAppController { dashboardStateManager.getPanels().forEach((panel: SavedDashboardPanel) => { embeddablesMap[panel.panelIndex] = convertSavedDashboardPanelToPanelState(panel); }); - let expandedPanelId; - if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) { - expandedPanelId = dashboardContainer.getInput().expandedPanelId; + + // If the incoming embeddable state's id already exists in the embeddables map, replace the input, retaining the existing gridData for that panel. + if (incomingEmbeddable?.embeddableId && embeddablesMap[incomingEmbeddable.embeddableId]) { + const originalPanelState = embeddablesMap[incomingEmbeddable.embeddableId]; + embeddablesMap[incomingEmbeddable.embeddableId] = { + gridData: originalPanelState.gridData, + type: incomingEmbeddable.type, + explicitInput: { + ...originalPanelState.explicitInput, + ...incomingEmbeddable.input, + id: incomingEmbeddable.embeddableId, + }, + }; + incomingEmbeddable = undefined; } + const shouldShowEditHelp = getShouldShowEditHelp(); const shouldShowViewHelp = getShouldShowViewHelp(); const isEmptyInReadonlyMode = shouldShowUnauthorizedEmptyState(); @@ -369,7 +380,7 @@ export class DashboardAppController { lastReloadRequestTime, title: dashboardStateManager.getTitle(), description: dashboardStateManager.getDescription(), - expandedPanelId, + expandedPanelId: dashboardStateManager.getExpandedPanelId(), }; }; @@ -482,32 +493,16 @@ export class DashboardAppController { refreshDashboardContainer(); }); - if (incomingEmbeddable) { - if ('id' in incomingEmbeddable) { - container.addOrUpdateEmbeddable( - incomingEmbeddable.type, - { - savedObjectId: incomingEmbeddable.id, - } - ); - } else if ('input' in incomingEmbeddable) { - const input = incomingEmbeddable.input; - // @ts-expect-error - delete input.id; - const explicitInput = { - savedVis: input, - }; - const embeddableId = - 'embeddableId' in incomingEmbeddable - ? incomingEmbeddable.embeddableId - : undefined; - container.addOrUpdateEmbeddable( - incomingEmbeddable.type, - // This ugly solution is temporary - https://github.com/elastic/kibana/pull/70272 fixes this whole section - (explicitInput as unknown) as EmbeddableInput, - embeddableId - ); - } + // If the incomingEmbeddable does not yet exist in the panels listing, create a new panel using the container's addEmbeddable method. + if ( + incomingEmbeddable && + (!incomingEmbeddable.embeddableId || + !container.getInput().panels[incomingEmbeddable.embeddableId]) + ) { + container.addNewEmbeddable( + incomingEmbeddable.type, + incomingEmbeddable.input + ); } } diff --git a/src/plugins/dashboard/public/application/dashboard_state.test.ts b/src/plugins/dashboard/public/application/dashboard_state.test.ts index e32bb9b9ecabe..42425ebf2cacf 100644 --- a/src/plugins/dashboard/public/application/dashboard_state.test.ts +++ b/src/plugins/dashboard/public/application/dashboard_state.test.ts @@ -23,6 +23,9 @@ import { getSavedDashboardMock } from './test_helpers'; import { InputTimeRange, TimefilterContract, TimeRange } from 'src/plugins/data/public'; import { ViewMode } from 'src/plugins/embeddable/public'; import { createKbnUrlStateStorage } from 'src/plugins/kibana_utils/public'; +import { DashboardContainer, DashboardContainerInput } from '.'; +import { DashboardContainerOptions } from './embeddable/dashboard_container'; +import { embeddablePluginMock } from '../../../embeddable/public/mocks'; describe('DashboardState', function () { let dashboardState: DashboardStateManager; @@ -48,6 +51,23 @@ describe('DashboardState', function () { }); } + function initDashboardContainer(initialInput?: Partial) { + const { doStart } = embeddablePluginMock.createInstance(); + const defaultInput: DashboardContainerInput = { + id: '123', + viewMode: ViewMode.EDIT, + filters: [] as DashboardContainerInput['filters'], + query: {} as DashboardContainerInput['query'], + timeRange: {} as DashboardContainerInput['timeRange'], + useMargins: true, + title: 'ultra awesome test dashboard', + isFullScreenMode: false, + panels: {} as DashboardContainerInput['panels'], + }; + const input = { ...defaultInput, ...(initialInput ?? {}) }; + return new DashboardContainer(input, { embeddable: doStart() } as DashboardContainerOptions); + } + describe('syncTimefilterWithDashboard', function () { test('syncs quick time', function () { savedDashboard.timeRestore = true; @@ -95,6 +115,43 @@ describe('DashboardState', function () { }); }); + describe('Dashboard Container Changes', () => { + beforeEach(() => { + initDashboardState(); + }); + + test('expanedPanelId in container input casues state update', () => { + dashboardState.setExpandedPanelId = jest.fn(); + + const dashboardContainer = initDashboardContainer({ + expandedPanelId: 'theCoolestPanelOnThisDashboard', + }); + + dashboardState.handleDashboardContainerChanges(dashboardContainer); + expect(dashboardState.setExpandedPanelId).toHaveBeenCalledWith( + 'theCoolestPanelOnThisDashboard' + ); + }); + + test('expanedPanelId is not updated when it is the same', () => { + dashboardState.setExpandedPanelId = jest + .fn() + .mockImplementation(dashboardState.setExpandedPanelId); + + const dashboardContainer = initDashboardContainer({ + expandedPanelId: 'theCoolestPanelOnThisDashboard', + }); + + dashboardState.handleDashboardContainerChanges(dashboardContainer); + dashboardState.handleDashboardContainerChanges(dashboardContainer); + expect(dashboardState.setExpandedPanelId).toHaveBeenCalledTimes(1); + + dashboardContainer.updateInput({ expandedPanelId: 'woah it changed' }); + dashboardState.handleDashboardContainerChanges(dashboardContainer); + expect(dashboardState.setExpandedPanelId).toHaveBeenCalledTimes(2); + }); + }); + describe('isDirty', function () { beforeAll(() => { initDashboardState(); diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index 910a2b470b2eb..93a63b0535259 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -267,6 +267,10 @@ export class DashboardStateManager { this.setFullScreenMode(input.isFullScreenMode); } + if (input.expandedPanelId !== this.getExpandedPanelId()) { + this.setExpandedPanelId(input.expandedPanelId); + } + if (!_.isEqual(input.query, this.getQuery())) { this.setQuery(input.query); } @@ -282,6 +286,14 @@ export class DashboardStateManager { this.stateContainer.transitions.set('fullScreenMode', fullScreenMode); } + public getExpandedPanelId() { + return this.appState.expandedPanelId; + } + + public setExpandedPanelId(expandedPanelId?: string) { + this.stateContainer.transitions.set('expandedPanelId', expandedPanelId); + } + public setFilters(filters: Filter[]) { this.stateContainer.transitions.set('filters', filters); } diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service_mock.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx similarity index 100% rename from src/plugins/dashboard/public/attribute_service/attribute_service_mock.tsx rename to src/plugins/dashboard/public/attribute_service/attribute_service.mock.tsx diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts b/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts index 06f380ca3862b..ae8f034aec687 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts +++ b/src/plugins/dashboard/public/attribute_service/attribute_service.test.ts @@ -18,7 +18,7 @@ */ import { ATTRIBUTE_SERVICE_KEY } from './attribute_service'; -import { mockAttributeService } from './attribute_service_mock'; +import { mockAttributeService } from './attribute_service.mock'; import { coreMock } from '../../../../core/public/mocks'; interface TestAttributes { diff --git a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx index 84df05154fb63..7499a6fced72a 100644 --- a/src/plugins/dashboard/public/attribute_service/attribute_service.tsx +++ b/src/plugins/dashboard/public/attribute_service/attribute_service.tsx @@ -156,12 +156,8 @@ export class AttributeService< }; public getExplicitInputFromEmbeddable(embeddable: IEmbeddable): ValType | RefType { - return embeddable.getRoot() && - (embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput - ? ((embeddable.getRoot() as Container).getInput().panels[embeddable.id].explicitInput as - | ValType - | RefType) - : (embeddable.getInput() as ValType | RefType); + return ((embeddable.getRoot() as Container).getInput()?.panels?.[embeddable.id] + ?.explicitInput ?? embeddable.getInput()) as ValType | RefType; } getInputAsValueType = async (input: ValType | RefType): Promise => { @@ -204,7 +200,14 @@ export class AttributeService< const newAttributes = { ...input[ATTRIBUTE_SERVICE_KEY] }; newAttributes.title = props.newTitle; const wrappedInput = (await this.wrapAttributes(newAttributes, true)) as RefType; - resolve(wrappedInput); + + // Remove unneeded attributes from the original input. + delete (input as { [ATTRIBUTE_SERVICE_KEY]?: SavedObjectAttributes })[ + ATTRIBUTE_SERVICE_KEY + ]; + + // Combine input and wrapped input to preserve any passed in explicit Input. + resolve({ ...input, ...wrappedInput }); return { id: wrappedInput.savedObjectId }; } catch (error) { reject(error); diff --git a/src/plugins/dashboard/public/attribute_service/index.ts b/src/plugins/dashboard/public/attribute_service/index.ts new file mode 100644 index 0000000000000..84d4c8a13c31e --- /dev/null +++ b/src/plugins/dashboard/public/attribute_service/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service'; diff --git a/src/plugins/dashboard/public/index.ts b/src/plugins/dashboard/public/index.ts index e22d1f038a456..315afd61c7c44 100644 --- a/src/plugins/dashboard/public/index.ts +++ b/src/plugins/dashboard/public/index.ts @@ -31,7 +31,7 @@ export { } from './application'; export { DashboardConstants, createDashboardEditUrl } from './dashboard_constants'; -export { DashboardStart, DashboardUrlGenerator } from './plugin'; +export { DashboardStart, DashboardUrlGenerator, DashboardFeatureFlagConfig } from './plugin'; export { DASHBOARD_APP_URL_GENERATOR, createDashboardUrlGenerator, @@ -40,7 +40,7 @@ export { export { addEmbeddableToDashboardUrl } from './url_utils/url_helper'; export { SavedObjectDashboard } from './saved_dashboards'; export { SavedDashboardPanel } from './types'; -export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service/attribute_service'; +export { AttributeService, ATTRIBUTE_SERVICE_KEY } from './attribute_service'; export function plugin(initializerContext: PluginInitializerContext) { return new DashboardPlugin(initializerContext); diff --git a/src/plugins/dashboard/public/mocks.tsx b/src/plugins/dashboard/public/mocks.tsx index ba30d72594f2a..07f29eca53042 100644 --- a/src/plugins/dashboard/public/mocks.tsx +++ b/src/plugins/dashboard/public/mocks.tsx @@ -20,6 +20,7 @@ import { DashboardStart } from './plugin'; export type Start = jest.Mocked; +export { mockAttributeService } from './attribute_service/attribute_service.mock'; const createStartContract = (): DashboardStart => { // @ts-ignore diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 5a45229a58a7d..eadb3cd207e4d 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -117,7 +117,7 @@ declare module '../../share/public' { export type DashboardUrlGenerator = UrlGeneratorContract; -interface DashboardFeatureFlagConfig { +export interface DashboardFeatureFlagConfig { allowByValueEmbeddables: boolean; } diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts index 21c6bbc1bfc51..2764f4b075579 100644 --- a/src/plugins/dashboard/public/types.ts +++ b/src/plugins/dashboard/public/types.ts @@ -89,6 +89,7 @@ export interface DashboardAppState { query: Query | string; filters: Filter[]; viewMode: ViewMode; + expandedPanelId?: string; savedQuery?: string; } diff --git a/src/plugins/dashboard/public/url_utils/url_helper.test.ts b/src/plugins/dashboard/public/url_utils/url_helper.test.ts index 28d4ab032c33d..d2210e7380667 100644 --- a/src/plugins/dashboard/public/url_utils/url_helper.test.ts +++ b/src/plugins/dashboard/public/url_utils/url_helper.test.ts @@ -24,16 +24,17 @@ describe('', () => { const id = '123eb456cd'; const url = "/pep/app/dashboards#/create?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; - expect(addEmbeddableToDashboardUrl(url, id, 'visualization')).toEqual( - `/pep/app/dashboards#/create?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + + expect(addEmbeddableToDashboardUrl(url, id, 'visualization')).toBe( + '/pep/app/dashboards?addEmbeddableId=123eb456cd&addEmbeddableType=visualization#%2Fcreate%3F_g%3D%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29%26_a%3D%28description%3A%27%27%2Cfilters%3A%21%28%29%29' ); }); it('addEmbeddableToDashboardUrl when dashboard is saved', () => { const id = '123eb456cd'; const url = "/pep/app/dashboards#/view/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_g=(refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))&_a=(description:'',filters:!())"; - expect(addEmbeddableToDashboardUrl(url, id, 'visualization')).toEqual( - `/pep/app/dashboards#/view/9b780cd0-3dd3-11e8-b2b9-5d5dc1715159?_a=%28description%3A%27%27%2Cfilters%3A%21%28%29%29&_g=%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29&addEmbeddableId=${id}&addEmbeddableType=visualization` + expect(addEmbeddableToDashboardUrl(url, id, 'visualization')).toBe( + '/pep/app/dashboards?addEmbeddableId=123eb456cd&addEmbeddableType=visualization#%2Fview%2F9b780cd0-3dd3-11e8-b2b9-5d5dc1715159%3F_g%3D%28refreshInterval%3A%28pause%3A%21t%2Cvalue%3A0%29%2Ctime%3A%28from%3Anow-15m%2Cto%3Anow%29%29%26_a%3D%28description%3A%27%27%2Cfilters%3A%21%28%29%29' ); }); }); diff --git a/src/plugins/dashboard/public/url_utils/url_helper.ts b/src/plugins/dashboard/public/url_utils/url_helper.ts index 61737e81cf24d..1f4706f0b8a4d 100644 --- a/src/plugins/dashboard/public/url_utils/url_helper.ts +++ b/src/plugins/dashboard/public/url_utils/url_helper.ts @@ -17,7 +17,7 @@ * under the License. */ -import { parseUrl, stringify } from 'query-string'; +import { parseUrl, stringifyUrl } from 'query-string'; import { DashboardConstants } from '../index'; /** * @@ -34,12 +34,14 @@ export function addEmbeddableToDashboardUrl( embeddableId: string, embeddableType: string ) { - const { url, query } = parseUrl(dashboardUrl); + const { url, query, fragmentIdentifier } = parseUrl(dashboardUrl, { + parseFragmentIdentifier: true, + }); if (embeddableId) { query[DashboardConstants.ADD_EMBEDDABLE_TYPE] = embeddableType; query[DashboardConstants.ADD_EMBEDDABLE_ID] = embeddableId; } - return `${url}?${stringify(query)}`; + return stringifyUrl({ url, query, fragmentIdentifier }); } diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts index 1e8356a1ef100..ac91c5a92048a 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts @@ -114,4 +114,5 @@ export const dashboardSavedObjectTypeMigrations = { '6.7.2': flow(migrateMatchAllQuery), '7.0.0': flow(migrations700), '7.3.0': flow(migrations730), + '7.9.3': flow(migrateMatchAllQuery), }; diff --git a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts index 8a91c422eed3d..ce7a5ffcd9fe1 100644 --- a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.test.ts @@ -49,4 +49,21 @@ describe('migrate match_all query', () => { }, }); }); + + it('should return original doc if searchSourceJSON cannot be parsed', () => { + const migratedDoc = migrateMatchAllQuery( + { + attributes: { + kibanaSavedObjectMeta: 'kibanaSavedObjectMeta', + }, + } as Parameters[0], + savedObjectMigrationContext + ); + + expect(migratedDoc).toEqual({ + attributes: { + kibanaSavedObjectMeta: 'kibanaSavedObjectMeta', + }, + }); + }); }); diff --git a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts index 452d68aa92394..3d7aadab5602c 100644 --- a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts +++ b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts @@ -21,6 +21,12 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { get } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; +/** + * This migration script is related to: + * @link https://github.com/elastic/kibana/pull/62194 + * @link https://github.com/elastic/kibana/pull/14644 + * This is only a problem when you import an object from 5.x into 6.x but to be sure that all saved objects migrated we should execute it twice in 6.7.2 and 7.9.3 + */ export const migrateMatchAllQuery: SavedObjectMigrationFn = (doc) => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); @@ -31,6 +37,7 @@ export const migrateMatchAllQuery: SavedObjectMigrationFn = (doc) => { searchSource = JSON.parse(searchSourceJSON); } catch (e) { // Let it go, the data is invalid and we'll leave it as is + return doc; } if (searchSource.query?.match_all) { diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts index bc7080e7d450b..153b6a633b66d 100644 --- a/src/plugins/data/common/index.ts +++ b/src/plugins/data/common/index.ts @@ -27,3 +27,10 @@ export * from './query'; export * from './search'; export * from './types'; export * from './utils'; + +/** + * Use data plugin interface instead + * @deprecated + */ + +export { IndexPatternAttributes } from './types'; diff --git a/src/plugins/data/common/index_patterns/errors.ts b/src/plugins/data/common/index_patterns/errors.ts deleted file mode 100644 index 3d92bae1968fb..0000000000000 --- a/src/plugins/data/common/index_patterns/errors.ts +++ /dev/null @@ -1,29 +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 { FieldSpec } from './types'; - -export class FieldTypeUnknownError extends Error { - public readonly fieldSpec: FieldSpec; - constructor(message: string, spec: FieldSpec) { - super(message); - this.name = 'FieldTypeUnknownError'; - this.fieldSpec = spec; - } -} diff --git a/src/plugins/data/common/index_patterns/errors/duplicate_index_pattern.ts b/src/plugins/data/common/index_patterns/errors/duplicate_index_pattern.ts new file mode 100644 index 0000000000000..c42dcc1c6a24d --- /dev/null +++ b/src/plugins/data/common/index_patterns/errors/duplicate_index_pattern.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class DuplicateIndexPatternError extends Error { + constructor(message: string) { + super(message); + this.name = 'DuplicateIndexPatternError'; + } +} diff --git a/src/plugins/data/common/index_patterns/errors/index.ts b/src/plugins/data/common/index_patterns/errors/index.ts new file mode 100644 index 0000000000000..7cc39d93a2a18 --- /dev/null +++ b/src/plugins/data/common/index_patterns/errors/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './duplicate_index_pattern'; diff --git a/src/plugins/data/common/index_patterns/fields/field_list.ts b/src/plugins/data/common/index_patterns/fields/field_list.ts index 4cf6075869851..c0eb55a15fead 100644 --- a/src/plugins/data/common/index_patterns/fields/field_list.ts +++ b/src/plugins/data/common/index_patterns/fields/field_list.ts @@ -20,7 +20,7 @@ import { findIndex } from 'lodash'; import { IFieldType } from './types'; import { IndexPatternField } from './index_pattern_field'; -import { OnNotification, FieldSpec } from '../types'; +import { FieldSpec, IndexPatternFieldMap } from '../types'; import { IndexPattern } from '../index_patterns'; import { shortenDottedString } from '../../utils'; @@ -35,16 +35,11 @@ export interface IIndexPatternFieldList extends Array { removeAll(): void; replaceAll(specs: FieldSpec[]): void; update(field: FieldSpec): void; - toSpec(options?: { getFormatterForField?: IndexPattern['getFormatterForField'] }): FieldSpec[]; + toSpec(options?: { + getFormatterForField?: IndexPattern['getFormatterForField']; + }): IndexPatternFieldMap; } -export type CreateIndexPatternFieldList = ( - indexPattern: IndexPattern, - specs?: FieldSpec[], - shortDotsEnable?: boolean, - onNotification?: OnNotification -) => IIndexPatternFieldList; - // extending the array class and using a constructor doesn't work well // when calling filter and similar so wrapping in a callback. // to be removed in the future @@ -105,7 +100,7 @@ export const fieldList = ( this.groups.clear(); }; - public readonly replaceAll = (spcs: FieldSpec[]) => { + public readonly replaceAll = (spcs: FieldSpec[] = []) => { this.removeAll(); spcs.forEach(this.add); }; @@ -115,7 +110,12 @@ export const fieldList = ( }: { getFormatterForField?: IndexPattern['getFormatterForField']; } = {}) { - return [...this.map((field) => field.toSpec({ getFormatterForField }))]; + return { + ...this.reduce((collector, field) => { + collector[field.name] = field.toSpec({ getFormatterForField }); + return collector; + }, {}), + }; } } diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index 7f72bfe55c7cd..808afc3449c2a 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -17,12 +17,9 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types'; -import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { IFieldType } from './types'; import { FieldSpec, IndexPattern } from '../..'; -import { FieldTypeUnknownError } from '../errors'; export class IndexPatternField implements IFieldType { readonly spec: FieldSpec; @@ -35,16 +32,12 @@ export class IndexPatternField implements IFieldType { this.displayName = displayName; this.kbnFieldType = getKbnFieldType(spec.type); - if (spec.type && this.kbnFieldType?.name === KBN_FIELD_TYPES.UNKNOWN) { - const msg = i18n.translate('data.indexPatterns.unknownFieldTypeErrorMsg', { - values: { type: spec.type, name: spec.name }, - defaultMessage: `Field '{name}' Unknown field type '{type}'`, - }); - throw new FieldTypeUnknownError(msg, spec); - } } // writable attrs + /** + * Count is used for field popularity + */ public get count() { return this.spec.count || 0; } @@ -53,6 +46,9 @@ export class IndexPatternField implements IFieldType { this.spec.count = count; } + /** + * Script field code + */ public get script() { return this.spec.script; } @@ -61,6 +57,9 @@ export class IndexPatternField implements IFieldType { this.spec.script = script; } + /** + * Script field language + */ public get lang() { return this.spec.lang; } @@ -69,6 +68,9 @@ export class IndexPatternField implements IFieldType { this.spec.lang = lang; } + /** + * Description of field type conflicts across different indices in the same index pattern + */ public get conflictDescriptions() { return this.spec.conflictDescriptions; } @@ -152,7 +154,7 @@ export class IndexPatternField implements IFieldType { getFormatterForField, }: { getFormatterForField?: IndexPattern['getFormatterForField']; - } = {}) { + } = {}): FieldSpec { return { count: this.count, script: this.script, diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 1871627da76de..ed84aceb60e5a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -2,13 +2,13 @@ exports[`IndexPattern toSpec should match snapshot 1`] = ` Object { - "fields": Array [ - Object { + "fields": Object { + "@tags": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 10, + "count": 0, "esTypes": Array [ - "long", + "keyword", ], "format": Object { "id": "number", @@ -17,20 +17,20 @@ Object { }, }, "lang": undefined, - "name": "bytes", + "name": "@tags", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "number", + "type": "string", }, - Object { + "@timestamp": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 20, + "count": 30, "esTypes": Array [ - "boolean", + "date", ], "format": Object { "id": "number", @@ -39,20 +39,20 @@ Object { }, }, "lang": undefined, - "name": "ssl", + "name": "@timestamp", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "boolean", + "type": "date", }, - Object { + "_id": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 30, + "count": 0, "esTypes": Array [ - "date", + "_id", ], "format": Object { "id": "number", @@ -61,20 +61,20 @@ Object { }, }, "lang": undefined, - "name": "@timestamp", - "readFromDocValues": true, + "name": "_id", + "readFromDocValues": false, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "date", + "type": "string", }, - Object { + "_source": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 30, + "count": 0, "esTypes": Array [ - "date", + "_source", ], "format": Object { "id": "number", @@ -83,20 +83,20 @@ Object { }, }, "lang": undefined, - "name": "time", - "readFromDocValues": true, + "name": "_source", + "readFromDocValues": false, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "date", + "type": "_source", }, - Object { + "_type": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "keyword", + "_type", ], "format": Object { "id": "number", @@ -105,20 +105,20 @@ Object { }, }, "lang": undefined, - "name": "@tags", - "readFromDocValues": true, + "name": "_type", + "readFromDocValues": false, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, "type": "string", }, - Object { + "area": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "date", + "geo_shape", ], "format": Object { "id": "number", @@ -127,20 +127,20 @@ Object { }, }, "lang": undefined, - "name": "utc_time", - "readFromDocValues": true, + "name": "area", + "readFromDocValues": false, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "date", + "type": "geo_shape", }, - Object { + "bytes": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 0, + "count": 10, "esTypes": Array [ - "integer", + "long", ], "format": Object { "id": "number", @@ -149,7 +149,7 @@ Object { }, }, "lang": undefined, - "name": "phpmemory", + "name": "bytes", "readFromDocValues": true, "script": undefined, "scripted": false, @@ -157,12 +157,12 @@ Object { "subType": undefined, "type": "number", }, - Object { + "custom_user_field": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "ip", + "conflict", ], "format": Object { "id": "number", @@ -171,20 +171,20 @@ Object { }, }, "lang": undefined, - "name": "ip", + "name": "custom_user_field", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "ip", + "type": "conflict", }, - Object { + "extension": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "attachment", + "text", ], "format": Object { "id": "number", @@ -193,15 +193,41 @@ Object { }, }, "lang": undefined, - "name": "request_body", - "readFromDocValues": true, + "name": "extension", + "readFromDocValues": false, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "attachment", + "type": "string", }, - Object { + "extension.keyword": Object { + "aggregatable": true, + "conflictDescriptions": undefined, + "count": 0, + "esTypes": Array [ + "keyword", + ], + "format": Object { + "id": "number", + "params": Object { + "pattern": "$0,0.[00]", + }, + }, + "lang": undefined, + "name": "extension.keyword", + "readFromDocValues": true, + "script": undefined, + "scripted": false, + "searchable": true, + "subType": Object { + "multi": Object { + "parent": "extension", + }, + }, + "type": "string", + }, + "geo.coordinates": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, @@ -215,7 +241,7 @@ Object { }, }, "lang": undefined, - "name": "point", + "name": "geo.coordinates", "readFromDocValues": true, "script": undefined, "scripted": false, @@ -223,12 +249,12 @@ Object { "subType": undefined, "type": "geo_point", }, - Object { + "geo.src": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "geo_shape", + "keyword", ], "format": Object { "id": "number", @@ -237,15 +263,15 @@ Object { }, }, "lang": undefined, - "name": "area", - "readFromDocValues": false, + "name": "geo.src", + "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "geo_shape", + "type": "string", }, - Object { + "hashed": Object { "aggregatable": false, "conflictDescriptions": undefined, "count": 0, @@ -267,12 +293,12 @@ Object { "subType": undefined, "type": "murmur3", }, - Object { + "ip": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "geo_point", + "ip", ], "format": Object { "id": "number", @@ -281,15 +307,15 @@ Object { }, }, "lang": undefined, - "name": "geo.coordinates", + "name": "ip", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "geo_point", + "type": "ip", }, - Object { + "machine.os": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, @@ -303,7 +329,7 @@ Object { }, }, "lang": undefined, - "name": "extension", + "name": "machine.os", "readFromDocValues": false, "script": undefined, "scripted": false, @@ -311,7 +337,7 @@ Object { "subType": undefined, "type": "string", }, - Object { + "machine.os.raw": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, @@ -325,19 +351,19 @@ Object { }, }, "lang": undefined, - "name": "extension.keyword", + "name": "machine.os.raw", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": Object { "multi": Object { - "parent": "extension", + "parent": "machine.os", }, }, "type": "string", }, - Object { + "non-filterable": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, @@ -351,20 +377,20 @@ Object { }, }, "lang": undefined, - "name": "machine.os", + "name": "non-filterable", "readFromDocValues": false, "script": undefined, "scripted": false, - "searchable": true, + "searchable": false, "subType": undefined, "type": "string", }, - Object { - "aggregatable": true, + "non-sortable": Object { + "aggregatable": false, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "keyword", + "text", ], "format": Object { "id": "number", @@ -373,24 +399,20 @@ Object { }, }, "lang": undefined, - "name": "machine.os.raw", - "readFromDocValues": true, + "name": "non-sortable", + "readFromDocValues": false, "script": undefined, "scripted": false, - "searchable": true, - "subType": Object { - "multi": Object { - "parent": "machine.os", - }, - }, + "searchable": false, + "subType": undefined, "type": "string", }, - Object { + "phpmemory": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "keyword", + "integer", ], "format": Object { "id": "number", @@ -399,20 +421,20 @@ Object { }, }, "lang": undefined, - "name": "geo.src", + "name": "phpmemory", "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "string", + "type": "number", }, - Object { + "point": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "_id", + "geo_point", ], "format": Object { "id": "number", @@ -421,20 +443,20 @@ Object { }, }, "lang": undefined, - "name": "_id", - "readFromDocValues": false, + "name": "point", + "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "string", + "type": "geo_point", }, - Object { + "request_body": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "_type", + "attachment", ], "format": Object { "id": "number", @@ -443,20 +465,20 @@ Object { }, }, "lang": undefined, - "name": "_type", - "readFromDocValues": false, + "name": "request_body", + "readFromDocValues": true, "script": undefined, "scripted": false, "searchable": true, "subType": undefined, - "type": "string", + "type": "attachment", }, - Object { + "script date": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "_source", + "date", ], "format": Object { "id": "number", @@ -464,43 +486,21 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": undefined, - "name": "_source", + "lang": "painless", + "name": "script date", "readFromDocValues": false, - "script": undefined, - "scripted": false, + "script": "1234", + "scripted": true, "searchable": true, "subType": undefined, - "type": "_source", + "type": "date", }, - Object { + "script murmur3": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "text", - ], - "format": Object { - "id": "number", - "params": Object { - "pattern": "$0,0.[00]", - }, - }, - "lang": undefined, - "name": "non-filterable", - "readFromDocValues": false, - "script": undefined, - "scripted": false, - "searchable": false, - "subType": undefined, - "type": "string", - }, - Object { - "aggregatable": false, - "conflictDescriptions": undefined, - "count": 0, - "esTypes": Array [ - "text", + "murmur3", ], "format": Object { "id": "number", @@ -508,21 +508,21 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": undefined, - "name": "non-sortable", + "lang": "expression", + "name": "script murmur3", "readFromDocValues": false, - "script": undefined, - "scripted": false, - "searchable": false, + "script": "1234", + "scripted": true, + "searchable": true, "subType": undefined, - "type": "string", + "type": "murmur3", }, - Object { + "script number": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "conflict", + "long", ], "format": Object { "id": "number", @@ -530,16 +530,16 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": undefined, - "name": "custom_user_field", - "readFromDocValues": true, - "script": undefined, - "scripted": false, + "lang": "expression", + "name": "script number", + "readFromDocValues": false, + "script": "1234", + "scripted": true, "searchable": true, "subType": undefined, - "type": "conflict", + "type": "number", }, - Object { + "script string": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, @@ -561,12 +561,12 @@ Object { "subType": undefined, "type": "string", }, - Object { + "ssl": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 0, + "count": 20, "esTypes": Array [ - "long", + "boolean", ], "format": Object { "id": "number", @@ -574,19 +574,19 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": "expression", - "name": "script number", - "readFromDocValues": false, - "script": "1234", - "scripted": true, + "lang": undefined, + "name": "ssl", + "readFromDocValues": true, + "script": undefined, + "scripted": false, "searchable": true, "subType": undefined, - "type": "number", + "type": "boolean", }, - Object { + "time": Object { "aggregatable": true, "conflictDescriptions": undefined, - "count": 0, + "count": 30, "esTypes": Array [ "date", ], @@ -596,21 +596,21 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": "painless", - "name": "script date", - "readFromDocValues": false, - "script": "1234", - "scripted": true, + "lang": undefined, + "name": "time", + "readFromDocValues": true, + "script": undefined, + "scripted": false, "searchable": true, "subType": undefined, "type": "date", }, - Object { + "utc_time": Object { "aggregatable": true, "conflictDescriptions": undefined, "count": 0, "esTypes": Array [ - "murmur3", + "date", ], "format": Object { "id": "number", @@ -618,21 +618,22 @@ Object { "pattern": "$0,0.[00]", }, }, - "lang": "expression", - "name": "script murmur3", - "readFromDocValues": false, - "script": "1234", - "scripted": true, + "lang": undefined, + "name": "utc_time", + "readFromDocValues": true, + "script": undefined, + "scripted": false, "searchable": true, "subType": undefined, - "type": "murmur3", + "type": "date", }, - ], + }, "id": "test-pattern", "sourceFilters": undefined, "timeFieldName": "timestamp", "title": "title", + "type": "index-pattern", "typeMeta": undefined, - "version": 2, + "version": "2", } `; diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap new file mode 100644 index 0000000000000..752fdcf11991c --- /dev/null +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`IndexPatterns savedObjectToSpec 1`] = ` +Object { + "fields": Object {}, + "id": "id", + "intervalName": undefined, + "sourceFilters": Array [ + Object { + "value": "item1", + }, + Object { + "value": "item2", + }, + ], + "timeFieldName": "@timestamp", + "title": "kibana-*", + "type": "", + "typeMeta": Object {}, + "version": "version", +} +`; diff --git a/src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts b/src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts deleted file mode 100644 index 4eba0576ff235..0000000000000 --- a/src/plugins/data/common/index_patterns/index_patterns/_fields_fetcher.ts +++ /dev/null @@ -1,48 +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 { IndexPattern } from '.'; -import { GetFieldsOptions, IIndexPatternsApiClient } from '../types'; - -/** @internal */ -export const createFieldsFetcher = ( - indexPattern: IndexPattern, - apiClient: IIndexPatternsApiClient, - metaFields: string[] = [] -) => { - const fieldFetcher = { - fetch: (options: GetFieldsOptions) => { - return fieldFetcher.fetchForWildcard(indexPattern.title, { - ...options, - type: indexPattern.type, - params: indexPattern.typeMeta && indexPattern.typeMeta.params, - }); - }, - fetchForWildcard: (pattern: string, options: GetFieldsOptions = {}) => { - return apiClient.getFieldsForWildcard({ - pattern, - metaFields, - type: options.type, - params: options.params || {}, - }); - }, - }; - - return fieldFetcher; -}; diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index f49897c47d562..a8d53223c06d1 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -17,19 +17,18 @@ * under the License. */ -import { defaults, map, last } from 'lodash'; +import { map, last } from 'lodash'; import { IndexPattern } from './index_pattern'; import { DuplicateField } from '../../../../kibana_utils/common'; -// @ts-ignore +// @ts-expect-error import mockLogStashFields from '../../../../../fixtures/logstash_fields'; -// @ts-ignore import { stubbedSavedObjectIndexPattern } from '../../../../../fixtures/stubbed_saved_object_index_pattern'; import { IndexPatternField } from '../fields'; import { fieldFormatsMock } from '../../field_formats/mocks'; -import { FieldFormat, IndexPatternsService } from '../..'; +import { FieldFormat } from '../..'; class MockFieldFormatter {} @@ -63,91 +62,33 @@ jest.mock('../../field_mapping', () => { }; }); -let mockFieldsFetcherResponse: any[] = []; - -jest.mock('./_fields_fetcher', () => ({ - createFieldsFetcher: jest.fn().mockImplementation(() => ({ - fetch: jest.fn().mockImplementation(() => { - return new Promise((resolve) => resolve(mockFieldsFetcherResponse)); - }), - every: jest.fn(), - })), -})); - -let object: any = {}; - -const savedObjectsClient = { - create: jest.fn(), - get: jest.fn().mockImplementation(() => object), - update: jest.fn().mockImplementation(async (type, id, body, { version }) => { - if (object.version !== version) { - throw new Object({ - res: { - status: 409, - }, - }); - } - object.attributes.title = body.title; - object.version += 'a'; - return { - id: object.id, - version: object.version, - }; - }), -}; - -const patternCache = { - clear: jest.fn(), - get: jest.fn(), - set: jest.fn(), - clearAll: jest.fn(), -}; - -const apiClient = { - _getUrl: jest.fn(), - getFieldsForTimePattern: jest.fn(), - getFieldsForWildcard: jest.fn(), -}; - // helper function to create index patterns -function create(id: string, payload?: any): Promise { - const indexPattern = new IndexPattern(id, { - savedObjectsClient: savedObjectsClient as any, - apiClient, - patternCache, +function create(id: string) { + const { + type, + version, + attributes: { timeFieldName, fields, title }, + } = stubbedSavedObjectIndexPattern(id); + + return new IndexPattern({ + spec: { id, type, version, timeFieldName, fields, title }, + savedObjectsClient: {} as any, fieldFormats: fieldFormatsMock, - indexPatternsService: {} as IndexPatternsService, - onNotification: () => {}, - onError: () => {}, shortDotsEnable: false, metaFields: [], }); - - setDocsourcePayload(id, payload); - - return indexPattern.init(); -} - -function setDocsourcePayload(id: string | null, providedPayload: any) { - object = defaults(providedPayload || {}, stubbedSavedObjectIndexPattern(id)); } describe('IndexPattern', () => { - const indexPatternId = 'test-pattern'; - let indexPattern: IndexPattern; // create an indexPattern instance for each test beforeEach(() => { - return create(indexPatternId).then((pattern: IndexPattern) => { - indexPattern = pattern; - }); + indexPattern = create('test-pattern'); }); describe('api', () => { test('should have expected properties', () => { - expect(indexPattern).toHaveProperty('refreshFields'); - expect(indexPattern).toHaveProperty('popularizeField'); expect(indexPattern).toHaveProperty('getScriptedFields'); expect(indexPattern).toHaveProperty('getNonScriptedFields'); expect(indexPattern).toHaveProperty('addScriptedField'); @@ -158,13 +99,6 @@ describe('IndexPattern', () => { }); }); - describe('init', () => { - test('should append the found fields', () => { - expect(savedObjectsClient.get).toHaveBeenCalled(); - expect(indexPattern.fields).toHaveLength(mockLogStashFields().length); - }); - }); - describe('fields', () => { test('should have expected properties on fields', function () { expect(indexPattern.fields[0]).toHaveProperty('displayName'); @@ -229,43 +163,9 @@ describe('IndexPattern', () => { }); }); - describe('refresh fields', () => { - test('should fetch fields from the fieldsFetcher', async () => { - expect(indexPattern.fields.length).toBeGreaterThan(2); - - mockFieldsFetcherResponse = [{ name: 'foo' }, { name: 'bar' }]; - - await indexPattern.refreshFields(); - - mockFieldsFetcherResponse = []; - - const newFields = indexPattern.getNonScriptedFields(); - - expect(newFields).toHaveLength(2); - expect([...newFields.map((f) => f.name)]).toEqual(['foo', 'bar']); - }); - - test('should preserve the scripted fields', async () => { - // add spy to indexPattern.getScriptedFields - // sinon.spy(indexPattern, 'getScriptedFields'); - - // refresh fields, which will fetch - await indexPattern.refreshFields(); - - // called to append scripted fields to the response from mapper.getFieldsForIndexPattern - // sinon.assert.calledOnce(indexPattern.getScriptedFields); - expect(indexPattern.getScriptedFields().map((f) => f.name)).toEqual( - mockLogStashFields() - .filter((f: IndexPatternField) => f.scripted) - .map((f: IndexPatternField) => f.name) - ); - }); - }); - describe('add and remove scripted fields', () => { test('should append the scripted field', async () => { // keep a copy of the current scripted field count - // const saveSpy = sinon.spy(indexPattern, 'save'); const oldCount = indexPattern.getScriptedFields().length; // add a new scripted field @@ -278,12 +178,10 @@ describe('IndexPattern', () => { await indexPattern.addScriptedField( scriptedField.name, scriptedField.script, - scriptedField.type, - 'lang' + scriptedField.type ); const scriptedFields = indexPattern.getScriptedFields(); - // expect(saveSpy.callCount).to.equal(1); expect(scriptedFields).toHaveLength(oldCount + 1); expect((indexPattern.fields.getByName(scriptedField.name) as IndexPatternField).name).toEqual( scriptedField.name @@ -291,14 +189,12 @@ describe('IndexPattern', () => { }); test('should remove scripted field, by name', async () => { - // const saveSpy = sinon.spy(indexPattern, 'save'); const scriptedFields = indexPattern.getScriptedFields(); const oldCount = scriptedFields.length; const scriptedField = last(scriptedFields)!; await indexPattern.removeScriptedField(scriptedField.name); - // expect(saveSpy.callCount).to.equal(1); expect(indexPattern.getScriptedFields().length).toEqual(oldCount - 1); expect(indexPattern.fields.getByName(scriptedField.name)).toEqual(undefined); }); @@ -308,7 +204,7 @@ describe('IndexPattern', () => { const scriptedField = last(scriptedFields) as any; expect.assertions(1); try { - await indexPattern.addScriptedField(scriptedField.name, "'new script'", 'string', 'lang'); + await indexPattern.addScriptedField(scriptedField.name, "'new script'", 'string'); } catch (e) { expect(e).toBeInstanceOf(DuplicateField); } @@ -330,8 +226,13 @@ describe('IndexPattern', () => { } as FieldFormat; indexPattern.getFormatterForField = () => formatter; const spec = indexPattern.toSpec(); - const restoredPattern = await create(spec.id as string); - restoredPattern.initFromSpec(spec); + const restoredPattern = new IndexPattern({ + spec, + savedObjectsClient: {} as any, + fieldFormats: fieldFormatsMock, + shortDotsEnable: false, + metaFields: [], + }); expect(restoredPattern.id).toEqual(indexPattern.id); expect(restoredPattern.title).toEqual(indexPattern.title); expect(restoredPattern.timeFieldName).toEqual(indexPattern.timeFieldName); @@ -339,54 +240,4 @@ describe('IndexPattern', () => { expect(restoredPattern.fieldFormatMap.bytes instanceof MockFieldFormatter).toEqual(true); }); }); - - describe('popularizeField', () => { - test('should increment the popularity count by default', () => { - // const saveSpy = sinon.stub(indexPattern, 'save'); - indexPattern.fields.forEach(async (field) => { - const oldCount = field.count || 0; - - await indexPattern.popularizeField(field.name); - - // expect(saveSpy.callCount).to.equal(i + 1); - expect(field.count).toEqual(oldCount + 1); - }); - }); - - test('should increment the popularity count', () => { - // const saveSpy = sinon.stub(indexPattern, 'save'); - indexPattern.fields.forEach(async (field) => { - const oldCount = field.count || 0; - const incrementAmount = 4; - - await indexPattern.popularizeField(field.name, incrementAmount); - - // expect(saveSpy.callCount).to.equal(i + 1); - expect(field.count).toEqual(oldCount + incrementAmount); - }); - }); - - test('should decrement the popularity count', () => { - indexPattern.fields.forEach(async (field) => { - const oldCount = field.count || 0; - const incrementAmount = 4; - const decrementAmount = -2; - - await indexPattern.popularizeField(field.name, incrementAmount); - await indexPattern.popularizeField(field.name, decrementAmount); - - expect(field.count).toEqual(oldCount + incrementAmount + decrementAmount); - }); - }); - - test('should not go below 0', () => { - indexPattern.fields.forEach(async (field) => { - const decrementAmount = -Number.MAX_VALUE; - - await indexPattern.popularizeField(field.name, decrementAmount); - - expect(field.count).toEqual(0); - }); - }); - }); }); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index 76f1a5e59d0ee..5fc6344c935d5 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -18,211 +18,91 @@ */ import _, { each, reject } from 'lodash'; -import { i18n } from '@kbn/i18n'; import { SavedObjectsClientCommon } from '../..'; -import { DuplicateField, SavedObjectNotFound } from '../../../../kibana_utils/common'; +import { DuplicateField } from '../../../../kibana_utils/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, - FieldTypeUnknownError, FieldFormatNotFoundError, + IFieldType, } from '../../../common'; -import { findByTitle } from '../utils'; -import { IndexPatternMissingIndices } from '../lib'; import { IndexPatternField, IIndexPatternFieldList, fieldList } from '../fields'; -import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; -import { OnNotification, OnError, IIndexPatternsApiClient, IndexPatternAttributes } from '../types'; import { FieldFormatsStartCommon, FieldFormat } from '../../field_formats'; -import { PatternCache } from './_pattern_cache'; -import { expandShorthand, FieldMappingSpec, MappingObject } from '../../field_mapping'; -import { IndexPatternSpec, TypeMeta, FieldSpec, SourceFilter } from '../types'; +import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; -import { IndexPatternsService } from '..'; - -const savedObjectType = 'index-pattern'; interface IndexPatternDeps { + spec?: IndexPatternSpec; savedObjectsClient: SavedObjectsClientCommon; - apiClient: IIndexPatternsApiClient; - patternCache: PatternCache; fieldFormats: FieldFormatsStartCommon; - indexPatternsService: IndexPatternsService; - onNotification: OnNotification; - onError: OnError; shortDotsEnable: boolean; metaFields: string[]; } +interface SavedObjectBody { + title?: string; + timeFieldName?: string; + intervalName?: string; + fields?: string; + sourceFilters?: string; + fieldFormatMap?: string; + typeMeta?: string; + type?: string; +} + +type FormatFieldFn = (hit: Record, fieldName: string) => any; + export class IndexPattern implements IIndexPattern { public id?: string; public title: string = ''; - public fieldFormatMap: any; + public fieldFormatMap: Record; public typeMeta?: TypeMeta; - public fields: IIndexPatternFieldList & { toSpec: () => FieldSpec[] }; + public fields: IIndexPatternFieldList & { toSpec: () => IndexPatternFieldMap }; public timeFieldName: string | undefined; public intervalName: string | undefined; public type: string | undefined; - public formatHit: any; - public formatField: any; - public flattenHit: any; + public formatHit: { + (hit: Record, type?: string): any; + formatField: FormatFieldFn; + }; + public formatField: FormatFieldFn; + public flattenHit: (hit: Record, deep?: boolean) => Record; public metaFields: string[]; - + // savedObject version public version: string | undefined; - private savedObjectsClient: SavedObjectsClientCommon; - private patternCache: PatternCache; public sourceFilters?: SourceFilter[]; - // todo make read only, update via method or factor out - public originalBody: { [key: string]: any } = {}; - public fieldsFetcher: any; // probably want to factor out any direct usage and change to private - private indexPatternsService: IndexPatternsService; + private originalSavedObjectBody: SavedObjectBody = {}; private shortDotsEnable: boolean = false; private fieldFormats: FieldFormatsStartCommon; - private onNotification: OnNotification; - private onError: OnError; - - private mapping: MappingObject = expandShorthand({ - title: ES_FIELD_TYPES.TEXT, - timeFieldName: ES_FIELD_TYPES.KEYWORD, - intervalName: ES_FIELD_TYPES.KEYWORD, - fields: 'json', - sourceFilters: 'json', - fieldFormatMap: { - type: ES_FIELD_TYPES.TEXT, - _serialize: (map = {}) => { - const serialized = _.transform(map, this.serializeFieldFormatMap); - return _.isEmpty(serialized) ? undefined : JSON.stringify(serialized); - }, - _deserialize: (map = '{}') => { - return _.mapValues(JSON.parse(map), (mapping) => { - return this.deserializeFieldFormatMap(mapping); - }); - }, - }, - type: ES_FIELD_TYPES.KEYWORD, - typeMeta: 'json', - }); - - constructor( - id: string | undefined, - { - savedObjectsClient, - apiClient, - patternCache, - fieldFormats, - indexPatternsService, - onNotification, - onError, - shortDotsEnable = false, - metaFields = [], - }: IndexPatternDeps - ) { - this.id = id; - this.savedObjectsClient = savedObjectsClient; - this.patternCache = patternCache; - this.fieldFormats = fieldFormats; - this.indexPatternsService = indexPatternsService; - this.onNotification = onNotification; - this.onError = onError; + constructor({ + spec = {}, + fieldFormats, + shortDotsEnable = false, + metaFields = [], + }: IndexPatternDeps) { + // set dependencies + this.fieldFormats = fieldFormats; + // set config this.shortDotsEnable = shortDotsEnable; this.metaFields = metaFields; + // initialize functionality this.fields = fieldList([], this.shortDotsEnable); - this.fieldsFetcher = createFieldsFetcher(this, apiClient, metaFields); this.flattenHit = flattenHitWrapper(this, metaFields); this.formatHit = formatHitProvider( this, fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING) ); this.formatField = this.formatHit.formatField; - } - private unknownFieldErrorNotification( - fieldType: string, - fieldName: string, - indexPatternTitle: string - ) { - const title = i18n.translate('data.indexPatterns.unknownFieldHeader', { - values: { type: fieldType }, - defaultMessage: 'Unknown field type {type}', - }); - const text = i18n.translate('data.indexPatterns.unknownFieldErrorMessage', { - values: { name: fieldName, title: indexPatternTitle }, - defaultMessage: 'Field {name} in indexPattern {title} is using an unknown field type.', - }); - this.onNotification({ title, text, color: 'danger', iconType: 'alert' }); - } - - private serializeFieldFormatMap(flat: any, format: string, field: string | undefined) { - if (format && field) { - flat[field] = format; - } - } - - private deserializeFieldFormatMap(mapping: any) { - try { - return this.fieldFormats.getInstance(mapping.id, mapping.params); - } catch (err) { - if (err instanceof FieldFormatNotFoundError) { - return undefined; - } else { - throw err; - } - } - } - - private isFieldRefreshRequired(specs?: FieldSpec[]): boolean { - if (!specs) { - return true; - } - - return specs.every((spec) => { - // See https://github.com/elastic/kibana/pull/8421 - const hasFieldCaps = 'aggregatable' in spec && 'searchable' in spec; - - // See https://github.com/elastic/kibana/pull/11969 - const hasDocValuesFlag = 'readFromDocValues' in spec; - - return !hasFieldCaps || !hasDocValuesFlag; - }); - } - - private async indexFields(specs?: FieldSpec[]) { - if (!this.id) { - return; - } - - if (this.isFieldRefreshRequired(specs)) { - await this.refreshFields(); - } else { - if (specs) { - try { - this.fields.replaceAll(specs); - } catch (err) { - if (err instanceof FieldTypeUnknownError) { - this.unknownFieldErrorNotification(err.fieldSpec.name, err.fieldSpec.type, this.title); - } else { - throw err; - } - } - } - } - } - - public initFromSpec(spec: IndexPatternSpec) { - // create fieldFormatMap from field list - const fieldFormatMap: Record = {}; - if (_.isArray(spec.fields)) { - spec.fields.forEach((field: FieldSpec) => { - if (field.format) { - fieldFormatMap[field.name as string] = { ...field.format }; - } - }); - } + // set values + this.id = spec.id; + const fieldFormatMap = this.fieldSpecsToFieldFormatMap(spec.fields); this.version = spec.version; @@ -230,53 +110,55 @@ export class IndexPattern implements IIndexPattern { this.timeFieldName = spec.timeFieldName; this.sourceFilters = spec.sourceFilters; - try { - this.fields.replaceAll(spec.fields || []); - } catch (err) { - if (err instanceof FieldTypeUnknownError) { - this.unknownFieldErrorNotification(err.fieldSpec.name, err.fieldSpec.type, this.title); - } else { - throw err; - } - } + this.fields.replaceAll(Object.values(spec.fields || {})); + this.type = spec.type; this.typeMeta = spec.typeMeta; this.fieldFormatMap = _.mapValues(fieldFormatMap, (mapping) => { return this.deserializeFieldFormatMap(mapping); }); - - return this; } - private updateFromElasticSearch(response: any) { - if (!response.found) { - throw new SavedObjectNotFound(savedObjectType, this.id, 'management/kibana/indexPatterns'); - } - - _.forOwn(this.mapping, (fieldMapping: FieldMappingSpec, name: string | undefined) => { - if (!fieldMapping._deserialize || !name) { - return; + /** + * Get last saved saved object fields + */ + getOriginalSavedObjectBody = () => ({ ...this.originalSavedObjectBody }); + + /** + * Reset last saved saved object fields. used after saving + */ + resetOriginalSavedObjectBody = () => { + this.originalSavedObjectBody = this.getAsSavedObjectBody(); + }; + + /** + * Converts field format spec to field format instance + * @param mapping + */ + private deserializeFieldFormatMap(mapping: SerializedFieldFormat>) { + try { + return this.fieldFormats.getInstance(mapping.id as string, mapping.params); + } catch (err) { + if (err instanceof FieldFormatNotFoundError) { + return undefined; + } else { + throw err; } - - response[name] = fieldMapping._deserialize(response[name]); - }); - - this.title = response.title; - this.timeFieldName = response.timeFieldName; - this.intervalName = response.intervalName; - this.sourceFilters = response.sourceFilters; - this.fieldFormatMap = response.fieldFormatMap; - this.type = response.type; - this.typeMeta = response.typeMeta; - - if (!this.title && this.id) { - this.title = this.id; } - this.version = response.version; - - return this.indexFields(response.fields); } + /** + * Extracts FieldFormatMap from FieldSpec map + * @param fldList FieldSpec map + */ + private fieldSpecsToFieldFormatMap = (fldList: IndexPatternSpec['fields'] = {}) => + Object.values(fldList).reduce>((col, fieldSpec) => { + if (fieldSpec.format) { + col[fieldSpec.name] = { ...fieldSpec.format }; + } + return col; + }, {}); + getComputedFields() { const scriptFields: any = {}; if (!this.fields) { @@ -318,37 +200,6 @@ export class IndexPattern implements IIndexPattern { }; } - async init() { - if (!this.id) { - return this; // no id === no elasticsearch document - } - - const savedObject = await this.savedObjectsClient.get( - savedObjectType, - this.id - ); - - const response = { - version: savedObject.version, - found: savedObject.version ? true : false, - title: savedObject.attributes.title, - timeFieldName: savedObject.attributes.timeFieldName, - intervalName: savedObject.attributes.intervalName, - fields: savedObject.attributes.fields, - sourceFilters: savedObject.attributes.sourceFilters, - fieldFormatMap: savedObject.attributes.fieldFormatMap, - typeMeta: savedObject.attributes.typeMeta, - type: savedObject.attributes.type, - }; - // Do this before we attempt to update from ES since that call can potentially perform a save - this.originalBody = this.prepBody(); - await this.updateFromElasticSearch(response); - // Do it after to ensure we have the most up to date information - this.originalBody = this.prepBody(); - - return this; - } - public toSpec(): IndexPatternSpec { return { id: this.id, @@ -359,17 +210,28 @@ export class IndexPattern implements IIndexPattern { sourceFilters: this.sourceFilters, fields: this.fields.toSpec({ getFormatterForField: this.getFormatterForField.bind(this) }), typeMeta: this.typeMeta, + type: this.type, }; } - // Get the source filtering configuration for that index. + /** + * Get the source filtering configuration for that index. + */ getSourceFiltering() { return { excludes: (this.sourceFilters && this.sourceFilters.map((filter: any) => filter.value)) || [], }; } - async addScriptedField(name: string, script: string, fieldType: string = 'string', lang: string) { + /** + * Add scripted field to field list + * + * @param name field name + * @param script script code + * @param fieldType + * @param lang + */ + async addScriptedField(name: string, script: string, fieldType: string = 'string') { const scriptedFields = this.getScriptedFields(); const names = _.map(scriptedFields, 'name'); @@ -377,27 +239,24 @@ export class IndexPattern implements IIndexPattern { throw new DuplicateField(name); } - try { - this.fields.add({ - name, - script, - type: fieldType, - scripted: true, - lang, - aggregatable: true, - searchable: true, - count: 0, - readFromDocValues: false, - }); - } catch (err) { - if (err instanceof FieldTypeUnknownError) { - this.unknownFieldErrorNotification(err.fieldSpec.name, err.fieldSpec.type, this.title); - } else { - throw err; - } - } + this.fields.add({ + name, + script, + type: fieldType, + scripted: true, + lang: 'painless', + aggregatable: true, + searchable: true, + count: 0, + readFromDocValues: false, + }); } + /** + * Remove scripted field from field list + * @param fieldName + */ + removeScriptedField(fieldName: string) { const field = this.fields.getByName(fieldName); if (field) { @@ -405,34 +264,6 @@ export class IndexPattern implements IIndexPattern { } } - async popularizeField(fieldName: string, unit = 1) { - /** - * This function is just used by Discover and it's high likely to be removed in the near future - * It doesn't use the save function to skip the error message that's displayed when - * a user adds several columns in a higher frequency that the changes can be persisted to ES - * resulting in 409 errors - */ - if (!this.id) return; - const field = this.fields.getByName(fieldName); - if (!field) { - return; - } - const count = Math.max((field.count || 0) + unit, 0); - if (field.count === count) { - return; - } - field.count = count; - - try { - const res = await this.savedObjectsClient.update(savedObjectType, this.id, this.prepBody(), { - version: this.version, - }); - this.version = res.version; - } catch (e) { - // no need for an error message here - } - } - getNonScriptedFields() { return [...this.fields.getAll().filter((field) => !field.scripted)]; } @@ -450,13 +281,9 @@ export class IndexPattern implements IIndexPattern { return timeField && timeField.esTypes && timeField.esTypes.indexOf('date_nanos') !== -1; } - isTimeBasedWildcard(): boolean { - return this.isTimeBased() && this.isWildcard(); - } - getTimeField() { if (!this.timeFieldName || !this.fields || !this.fields.getByName) return undefined; - return this.fields.getByName(this.timeFieldName) || undefined; + return this.fields.getByName(this.timeFieldName); } getFieldByName(name: string): IndexPatternField | undefined { @@ -468,24 +295,41 @@ export class IndexPattern implements IIndexPattern { return this.typeMeta?.aggs; } - isWildcard() { - return _.includes(this.title, '*'); - } + /** + * Returns index pattern as saved object body for saving + */ + getAsSavedObjectBody() { + const serializeFieldFormatMap = ( + flat: any, + format: FieldFormat | undefined, + field: string | undefined + ) => { + if (format && field) { + flat[field] = format; + } + }; + const serialized = _.transform(this.fieldFormatMap, serializeFieldFormatMap); + const fieldFormatMap = _.isEmpty(serialized) ? undefined : JSON.stringify(serialized); - prepBody() { return { title: this.title, timeFieldName: this.timeFieldName, intervalName: this.intervalName, - sourceFilters: this.mapping.sourceFilters._serialize!(this.sourceFilters), - fields: this.mapping.fields._serialize!(this.fields), - fieldFormatMap: this.mapping.fieldFormatMap._serialize!(this.fieldFormatMap), + sourceFilters: this.sourceFilters ? JSON.stringify(this.sourceFilters) : undefined, + fields: this.fields ? JSON.stringify(this.fields) : undefined, + fieldFormatMap, type: this.type, - typeMeta: this.mapping.typeMeta._serialize!(this.typeMeta), + typeMeta: this.typeMeta ? JSON.stringify(this.typeMeta) : undefined, }; } - getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat { + /** + * Provide a field, get its formatter + * @param field + */ + getFormatterForField( + field: IndexPatternField | IndexPatternField['spec'] | IFieldType + ): FieldFormat { return ( this.fieldFormatMap[field.name] || this.fieldFormats.getDefaultInstance( @@ -494,81 +338,4 @@ export class IndexPattern implements IIndexPattern { ) ); } - - async create(allowOverride: boolean = false) { - const _create = async (duplicateId?: string) => { - if (duplicateId) { - this.patternCache.clear(duplicateId); - await this.savedObjectsClient.delete(savedObjectType, duplicateId); - } - - const body = this.prepBody(); - const response = await this.savedObjectsClient.create(savedObjectType, body, { id: this.id }); - - this.id = response.id; - return response.id; - }; - - const potentialDuplicateByTitle = await findByTitle(this.savedObjectsClient, this.title); - // If there is potentially duplicate title, just create it - if (!potentialDuplicateByTitle) { - return await _create(); - } - - // We found a duplicate but we aren't allowing override, show the warn modal - if (!allowOverride) { - return false; - } - - return await _create(potentialDuplicateByTitle.id); - } - - async _fetchFields() { - const fields = await this.fieldsFetcher.fetch(this); - const scripted = this.getScriptedFields().map((field) => field.spec); - try { - this.fields.replaceAll([...fields, ...scripted]); - } catch (err) { - if (err instanceof FieldTypeUnknownError) { - this.unknownFieldErrorNotification(err.fieldSpec.name, err.fieldSpec.type, this.title); - } else { - throw err; - } - } - } - - refreshFields() { - return ( - this._fetchFields() - // todo - .then(() => this.indexPatternsService.save(this)) - .catch((err) => { - // https://github.com/elastic/kibana/issues/9224 - // This call will attempt to remap fields from the matching - // ES index which may not actually exist. In that scenario, - // we still want to notify the user that there is a problem - // but we do not want to potentially make any pages unusable - // so do not rethrow the error here - - if (err instanceof IndexPatternMissingIndices) { - this.onNotification({ - title: (err as any).message, - color: 'danger', - iconType: 'alert', - }); - return []; - } - - this.onError(err, { - title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', { - defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', - values: { - id: this.id, - title: this.title, - }, - }), - }); - }) - ); - } } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts index d3b3a73a4b50f..b22437ebbdb4e 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.test.ts @@ -18,7 +18,7 @@ */ import { defaults } from 'lodash'; -import { IndexPatternsService } from '.'; +import { IndexPatternsService, IndexPattern } from '.'; import { fieldFormatsMock } from '../../field_formats/mocks'; import { stubbedSavedObjectIndexPattern } from '../../../../../fixtures/stubbed_saved_object_index_pattern'; import { UiSettingsCommon, SavedObjectsClientCommon, SavedObject } from '../types'; @@ -31,7 +31,6 @@ const createFieldsFetcher = jest.fn().mockImplementation(() => ({ })); const fieldFormats = fieldFormatsMock; - let object: any = {}; function setDocsourcePayload(id: string | null, providedPayload: any) { @@ -43,16 +42,18 @@ describe('IndexPatterns', () => { let savedObjectsClient: SavedObjectsClientCommon; beforeEach(() => { + const indexPatternObj = { id: 'id', version: 'a', attributes: { title: 'title' } }; savedObjectsClient = {} as SavedObjectsClientCommon; savedObjectsClient.find = jest.fn( - () => - Promise.resolve([{ id: 'id', attributes: { title: 'title' } }]) as Promise< - Array> - > + () => Promise.resolve([indexPatternObj]) as Promise>> ); savedObjectsClient.delete = jest.fn(() => Promise.resolve({}) as Promise); - savedObjectsClient.get = jest.fn().mockImplementation(() => object); savedObjectsClient.create = jest.fn(); + savedObjectsClient.get = jest.fn().mockImplementation(async (type, id) => ({ + id: object.id, + version: object.version, + attributes: object.attributes, + })); savedObjectsClient.update = jest .fn() .mockImplementation(async (type, id, body, { version }) => { @@ -141,30 +142,73 @@ describe('IndexPatterns', () => { }); // Create a normal index patterns - const pattern = await indexPatterns.make('foo'); + const pattern = await indexPatterns.get('foo'); expect(pattern.version).toBe('fooa'); + indexPatterns.clearCache(); // Create the same one - we're going to handle concurrency - const samePattern = await indexPatterns.make('foo'); + const samePattern = await indexPatterns.get('foo'); expect(samePattern.version).toBe('fooaa'); // This will conflict because samePattern did a save (from refreshFields) // but the resave should work fine pattern.title = 'foo2'; - await indexPatterns.save(pattern); + await indexPatterns.updateSavedObject(pattern); // This should not be able to recover samePattern.title = 'foo3'; let result; try { - await indexPatterns.save(samePattern); + await indexPatterns.updateSavedObject(samePattern); } catch (err) { result = err; } expect(result.res.status).toBe(409); }); + + test('create', async () => { + const title = 'kibana-*'; + indexPatterns.refreshFields = jest.fn(); + + const indexPattern = await indexPatterns.create({ title }, true); + expect(indexPattern).toBeInstanceOf(IndexPattern); + expect(indexPattern.title).toBe(title); + expect(indexPatterns.refreshFields).not.toBeCalled(); + + await indexPatterns.create({ title }); + expect(indexPatterns.refreshFields).toBeCalled(); + }); + + test('createAndSave', async () => { + const title = 'kibana-*'; + indexPatterns.createSavedObject = jest.fn(); + indexPatterns.setDefault = jest.fn(); + await indexPatterns.createAndSave({ title }); + expect(indexPatterns.createSavedObject).toBeCalled(); + expect(indexPatterns.setDefault).toBeCalled(); + }); + + test('savedObjectToSpec', () => { + const savedObject = { + id: 'id', + version: 'version', + attributes: { + title: 'kibana-*', + timeFieldName: '@timestamp', + fields: '[]', + sourceFilters: '[{"value":"item1"},{"value":"item2"}]', + fieldFormatMap: '{"field":{}}', + typeMeta: '{}', + type: '', + }, + type: 'index-pattern', + references: [], + }; + + expect(indexPatterns.savedObjectToSpec(savedObject)).toMatchSnapshot(); + }); }); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 47484f8ec75bb..9a86541376cd8 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -33,9 +33,17 @@ import { IIndexPatternsApiClient, GetFieldsOptions, IndexPatternSpec, + IndexPatternAttributes, + FieldSpec, + FieldFormatMap, + IndexPatternFieldMap, } from '../types'; import { FieldFormatsStartCommon } from '../../field_formats'; import { UI_SETTINGS, SavedObject } from '../../../common'; +import { SavedObjectNotFound } from '../../../../kibana_utils/common'; +import { IndexPatternMissingIndices } from '../lib'; +import { findByTitle } from '../utils'; +import { DuplicateIndexPatternError } from '../errors'; const indexPatternCache = createIndexPatternCache(); const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; @@ -86,6 +94,9 @@ export class IndexPatternsService { ); } + /** + * Refresh cache of index pattern ids and titles + */ private async refreshSavedObjectsCache() { this.savedObjectsCache = await this.savedObjectsClient.find({ type: 'index-pattern', @@ -94,6 +105,10 @@ export class IndexPatternsService { }); } + /** + * Get list of index pattern ids + * @param refresh Force refresh of index pattern list + */ getIds = async (refresh: boolean = false) => { if (!this.savedObjectsCache || refresh) { await this.refreshSavedObjectsCache(); @@ -104,6 +119,10 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.id); }; + /** + * Get list of index pattern titles + * @param refresh Force refresh of index pattern list + */ getTitles = async (refresh: boolean = false): Promise => { if (!this.savedObjectsCache || refresh) { await this.refreshSavedObjectsCache(); @@ -114,14 +133,29 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.attributes?.title); }; - getFieldsForTimePattern = (options: GetFieldsOptions = {}) => { - return this.apiClient.getFieldsForTimePattern(options); - }; - - getFieldsForWildcard = (options: GetFieldsOptions = {}) => { - return this.apiClient.getFieldsForWildcard(options); + /** + * Get list of index pattern ids with titles + * @param refresh Force refresh of index pattern list + */ + getIdsWithTitle = async ( + refresh: boolean = false + ): Promise> => { + if (!this.savedObjectsCache || refresh) { + await this.refreshSavedObjectsCache(); + } + if (!this.savedObjectsCache) { + return []; + } + return this.savedObjectsCache.map((obj) => ({ + id: obj?.id, + title: obj?.attributes?.title, + })); }; + /** + * Clear index pattern list cache + * @param id optionally clear a single id + */ clearCache = (id?: string) => { this.savedObjectsCache = null; if (id) { @@ -130,6 +164,7 @@ export class IndexPatternsService { indexPatternCache.clearAll(); } }; + getCache = async () => { if (!this.savedObjectsCache) { await this.refreshSavedObjectsCache(); @@ -137,6 +172,9 @@ export class IndexPatternsService { return this.savedObjectsCache; }; + /** + * Get default index pattern + */ getDefault = async () => { const defaultIndexPatternId = await this.config.get('defaultIndex'); if (defaultIndexPatternId) { @@ -146,47 +184,351 @@ export class IndexPatternsService { return null; }; + /** + * Optionally set default index pattern, unless force = true + * @param id + * @param force + */ + setDefault = async (id: string, force = false) => { + if (force || !this.config.get('defaultIndex')) { + await this.config.set('defaultIndex', id); + } + }; + + private isFieldRefreshRequired(specs?: IndexPatternFieldMap): boolean { + if (!specs) { + return true; + } + + return Object.values(specs).every((spec) => { + // See https://github.com/elastic/kibana/pull/8421 + const hasFieldCaps = 'aggregatable' in spec && 'searchable' in spec; + + // See https://github.com/elastic/kibana/pull/11969 + const hasDocValuesFlag = 'readFromDocValues' in spec; + + return !hasFieldCaps || !hasDocValuesFlag; + }); + } + + /** + * Get field list by providing { pattern } + * @param options + */ + getFieldsForWildcard = async (options: GetFieldsOptions = {}) => { + const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); + return this.apiClient.getFieldsForWildcard({ + pattern: options.pattern, + metaFields, + type: options.type, + params: options.params || {}, + }); + }; + + /** + * Get field list by providing an index patttern (or spec) + * @param options + */ + getFieldsForIndexPattern = async ( + indexPattern: IndexPattern | IndexPatternSpec, + options: GetFieldsOptions = {} + ) => + this.getFieldsForWildcard({ + pattern: indexPattern.title as string, + ...options, + type: indexPattern.type, + params: indexPattern.typeMeta && indexPattern.typeMeta.params, + }); + + /** + * Refresh field list for a given index pattern + * @param indexPattern + */ + refreshFields = async (indexPattern: IndexPattern) => { + try { + const fields = await this.getFieldsForIndexPattern(indexPattern); + const scripted = indexPattern.getScriptedFields().map((field) => field.spec); + indexPattern.fields.replaceAll([...fields, ...scripted]); + } catch (err) { + if (err instanceof IndexPatternMissingIndices) { + this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' }); + } + + this.onError(err, { + title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', { + defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + values: { id: indexPattern.id, title: indexPattern.title }, + }), + }); + } + }; + + /** + * Refreshes a field list from a spec before an index pattern instance is created + * @param fields + * @param id + * @param title + * @param options + */ + private refreshFieldSpecMap = async ( + fields: IndexPatternFieldMap, + id: string, + title: string, + options: GetFieldsOptions + ) => { + const scriptdFields = Object.values(fields).filter((field) => field.scripted); + try { + const newFields = await this.getFieldsForWildcard(options); + return this.fieldArrayToMap([...newFields, ...scriptdFields]); + } catch (err) { + if (err instanceof IndexPatternMissingIndices) { + this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' }); + return {}; + } + + this.onError(err, { + title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', { + defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + values: { id, title }, + }), + }); + } + return fields; + }; + + /** + * Applies a set of formats to a set of fields + * @param fieldSpecs + * @param fieldFormatMap + */ + private addFormatsToFields = (fieldSpecs: FieldSpec[], fieldFormatMap: FieldFormatMap) => { + Object.entries(fieldFormatMap).forEach(([fieldName, value]) => { + const field = fieldSpecs.find((fld: FieldSpec) => fld.name === fieldName); + if (field) { + field.format = value; + } + }); + }; + + /** + * Converts field array to map + * @param fields + */ + fieldArrayToMap = (fields: FieldSpec[]) => + fields.reduce((collector, field) => { + collector[field.name] = field; + return collector; + }, {}); + + /** + * Converts index pattern saved object to index pattern spec + * @param savedObject + */ + + savedObjectToSpec = (savedObject: SavedObject): IndexPatternSpec => { + const { + id, + version, + attributes: { + title, + timeFieldName, + intervalName, + fields, + sourceFilters, + fieldFormatMap, + typeMeta, + type, + }, + } = savedObject; + + const parsedSourceFilters = sourceFilters ? JSON.parse(sourceFilters) : undefined; + const parsedTypeMeta = typeMeta ? JSON.parse(typeMeta) : undefined; + const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; + const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; + + this.addFormatsToFields(parsedFields, parsedFieldFormatMap); + return { + id, + version, + title, + intervalName, + timeFieldName, + sourceFilters: parsedSourceFilters, + fields: this.fieldArrayToMap(parsedFields), + typeMeta: parsedTypeMeta, + type, + }; + }; + + /** + * Get an index pattern by id. Cache optimized + * @param id + */ + get = async (id: string): Promise => { const cache = indexPatternCache.get(id); if (cache) { return cache; } - const indexPattern = await this.make(id); + const savedObject = await this.savedObjectsClient.get( + savedObjectType, + id + ); + + if (!savedObject.version) { + throw new SavedObjectNotFound(savedObjectType, id, 'management/kibana/indexPatterns'); + } + + const spec = this.savedObjectToSpec(savedObject); + const { title, type, typeMeta } = spec; + const parsedFieldFormats: FieldFormatMap = savedObject.attributes.fieldFormatMap + ? JSON.parse(savedObject.attributes.fieldFormatMap) + : {}; + + const isFieldRefreshRequired = this.isFieldRefreshRequired(spec.fields); + let isSaveRequired = isFieldRefreshRequired; + try { + spec.fields = isFieldRefreshRequired + ? await this.refreshFieldSpecMap(spec.fields || {}, id, spec.title as string, { + pattern: title, + metaFields: await this.config.get(UI_SETTINGS.META_FIELDS), + type, + params: typeMeta && typeMeta.params, + }) + : spec.fields; + } catch (err) { + isSaveRequired = false; + if (err instanceof IndexPatternMissingIndices) { + this.onNotification({ + title: (err as any).message, + color: 'danger', + iconType: 'alert', + }); + } else { + this.onError(err, { + title: i18n.translate('data.indexPatterns.fetchFieldErrorTitle', { + defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + values: { id, title }, + }), + }); + } + } + + Object.entries(parsedFieldFormats).forEach(([fieldName, value]) => { + const field = spec.fields?.[fieldName]; + if (field) { + field.format = value; + } + }); + + const indexPattern = await this.create(spec, true); + indexPatternCache.set(id, indexPattern); + if (isSaveRequired) { + try { + this.updateSavedObject(indexPattern); + } catch (err) { + this.onError(err, { + title: i18n.translate('data.indexPatterns.fetchFieldSaveErrorTitle', { + defaultMessage: + 'Error saving after fetching fields for index pattern {title} (ID: {id})', + values: { + id: indexPattern.id, + title: indexPattern.title, + }, + }), + }); + } + } - return indexPatternCache.set(id, indexPattern); + indexPattern.resetOriginalSavedObjectBody(); + return indexPattern; }; - async specToIndexPattern(spec: IndexPatternSpec) { + /** + * Create a new index pattern instance + * @param spec + * @param skipFetchFields + */ + async create(spec: IndexPatternSpec, skipFetchFields = false): Promise { const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE); const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); - const indexPattern = new IndexPattern(spec.id, { + const indexPattern = new IndexPattern({ + spec, savedObjectsClient: this.savedObjectsClient, - apiClient: this.apiClient, - patternCache: indexPatternCache, fieldFormats: this.fieldFormats, - indexPatternsService: this, - onNotification: this.onNotification, - onError: this.onError, shortDotsEnable, metaFields, }); - indexPattern.initFromSpec(spec); + if (!skipFetchFields) { + await this.refreshFields(indexPattern); + } + + return indexPattern; + } + + /** + * Create a new index pattern and save it right away + * @param spec + * @param override Overwrite if existing index pattern exists + * @param skipFetchFields + */ + + async createAndSave(spec: IndexPatternSpec, override = false, skipFetchFields = false) { + const indexPattern = await this.create(spec, skipFetchFields); + await this.createSavedObject(indexPattern, override); + await this.setDefault(indexPattern.id as string); + return indexPattern; + } + + /** + * Save a new index pattern + * @param indexPattern + * @param override Overwrite if existing index pattern exists + */ + + async createSavedObject(indexPattern: IndexPattern, override = false) { + const dupe = await findByTitle(this.savedObjectsClient, indexPattern.title); + if (dupe) { + if (override) { + await this.delete(dupe.id); + } else { + throw new DuplicateIndexPatternError(`Duplicate index pattern: ${indexPattern.title}`); + } + } + + const body = indexPattern.getAsSavedObjectBody(); + const response = await this.savedObjectsClient.create(savedObjectType, body, { + id: indexPattern.id, + }); + indexPattern.id = response.id; + indexPatternCache.set(indexPattern.id, indexPattern); return indexPattern; } - async save(indexPattern: IndexPattern, saveAttempts: number = 0): Promise { + /** + * Save existing index pattern. Will attempt to merge differences if there are conflicts + * @param indexPattern + * @param saveAttempts + */ + + async updateSavedObject( + indexPattern: IndexPattern, + saveAttempts: number = 0, + ignoreErrors: boolean = false + ): Promise { if (!indexPattern.id) return; - const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE); - const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); - const body = indexPattern.prepBody(); + // get the list of attributes + const body = indexPattern.getAsSavedObjectBody(); + const originalBody = indexPattern.getOriginalSavedObjectBody(); + // get changed keys const originalChangedKeys: string[] = []; Object.entries(body).forEach(([key, value]) => { - if (value !== indexPattern.originalBody[key]) { + if (value !== (originalBody as any)[key]) { originalChangedKeys.push(key); } }); @@ -197,92 +539,63 @@ export class IndexPatternsService { indexPattern.id = resp.id; indexPattern.version = resp.version; }) - .catch((err) => { + .catch(async (err) => { if (err?.res?.status === 409 && saveAttempts++ < MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS) { - const samePattern = new IndexPattern(indexPattern.id, { - savedObjectsClient: this.savedObjectsClient, - apiClient: this.apiClient, - patternCache: indexPatternCache, - fieldFormats: this.fieldFormats, - indexPatternsService: this, - onNotification: this.onNotification, - onError: this.onError, - shortDotsEnable, - metaFields, + const samePattern = await this.get(indexPattern.id as string); + // What keys changed from now and what the server returned + const updatedBody = samePattern.getAsSavedObjectBody(); + + // Build a list of changed keys from the server response + // and ensure we ignore the key if the server response + // is the same as the original response (since that is expected + // if we made a change in that key) + + const serverChangedKeys: string[] = []; + Object.entries(updatedBody).forEach(([key, value]) => { + if (value !== (body as any)[key] && value !== (originalBody as any)[key]) { + serverChangedKeys.push(key); + } }); - return samePattern.init().then(() => { - // What keys changed from now and what the server returned - const updatedBody = samePattern.prepBody(); - - // Build a list of changed keys from the server response - // and ensure we ignore the key if the server response - // is the same as the original response (since that is expected - // if we made a change in that key) - - const serverChangedKeys: string[] = []; - Object.entries(updatedBody).forEach(([key, value]) => { - if (value !== (body as any)[key] && value !== indexPattern.originalBody[key]) { - serverChangedKeys.push(key); - } - }); - - let unresolvedCollision = false; - for (const originalKey of originalChangedKeys) { - for (const serverKey of serverChangedKeys) { - if (originalKey === serverKey) { - unresolvedCollision = true; - break; - } + let unresolvedCollision = false; + for (const originalKey of originalChangedKeys) { + for (const serverKey of serverChangedKeys) { + if (originalKey === serverKey) { + unresolvedCollision = true; + break; } } + } - if (unresolvedCollision) { - const title = i18n.translate('data.indexPatterns.unableWriteLabel', { - defaultMessage: - 'Unable to write index pattern! Refresh the page to get the most up to date changes for this index pattern.', - }); - - this.onNotification({ title, color: 'danger' }); - throw err; + if (unresolvedCollision) { + if (ignoreErrors) { + return; } - - // Set the updated response on this object - serverChangedKeys.forEach((key) => { - (indexPattern as any)[key] = (samePattern as any)[key]; + const title = i18n.translate('data.indexPatterns.unableWriteLabel', { + defaultMessage: + 'Unable to write index pattern! Refresh the page to get the most up to date changes for this index pattern.', }); - indexPattern.version = samePattern.version; - // Clear cache - indexPatternCache.clear(indexPattern.id!); + this.onNotification({ title, color: 'danger' }); + throw err; + } - // Try the save again - return this.save(indexPattern, saveAttempts); + // Set the updated response on this object + serverChangedKeys.forEach((key) => { + (indexPattern as any)[key] = (samePattern as any)[key]; }); + indexPattern.version = samePattern.version; + + // Clear cache + indexPatternCache.clear(indexPattern.id!); + + // Try the save again + return this.updateSavedObject(indexPattern, saveAttempts, ignoreErrors); } throw err; }); } - async make(id?: string): Promise { - const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE); - const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); - - const indexPattern = new IndexPattern(id, { - savedObjectsClient: this.savedObjectsClient, - apiClient: this.apiClient, - patternCache: indexPatternCache, - fieldFormats: this.fieldFormats, - indexPatternsService: this, - onNotification: this.onNotification, - onError: this.onError, - shortDotsEnable, - metaFields, - }); - - return indexPattern.init(); - } - /** * Deletes an index pattern from .kibana index * @param indexPatternId: Id of kibana Index Pattern to delete diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 7a230c20f6cd0..cb0c3aa0de38e 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -22,29 +22,23 @@ import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notificatio import type { SavedObject } from 'src/core/server'; import { IFieldType } from './fields'; import { SerializedFieldFormat } from '../../../expressions/common'; -import { KBN_FIELD_TYPES } from '..'; +import { KBN_FIELD_TYPES, IndexPatternField, FieldFormat } from '..'; + +export type FieldFormatMap = Record; export interface IIndexPattern { - [key: string]: any; fields: IFieldType[]; title: string; id?: string; type?: string; timeFieldName?: string; getTimeField?(): IFieldType | undefined; - fieldFormatMap?: Record< - string, - { - id: string; - params: unknown; - } - >; + fieldFormatMap?: Record | undefined>; + getFormatterForField?: ( + field: IndexPatternField | IndexPatternField['spec'] | IFieldType + ) => FieldFormat; } -/** - * Use data plugin interface instead - * @deprecated - */ export interface IndexPatternAttributes { type: string; fields: string; @@ -166,15 +160,18 @@ export interface FieldSpec { indexed?: boolean; } +export type IndexPatternFieldMap = Record; + export interface IndexPatternSpec { id?: string; version?: string; - - title: string; + title?: string; + intervalName?: string; timeFieldName?: string; sourceFilters?: SourceFilter[]; - fields?: FieldSpec[]; + fields?: IndexPatternFieldMap; typeMeta?: TypeMeta; + type?: string; } export interface SourceFilter { diff --git a/src/plugins/data/common/search/aggs/buckets/histogram.ts b/src/plugins/data/common/search/aggs/buckets/histogram.ts index 4b631e1fd7cd7..c3d3f041dd0c7 100644 --- a/src/plugins/data/common/search/aggs/buckets/histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/histogram.ts @@ -158,6 +158,7 @@ export const getHistogramBucketAgg = ({ maxBucketsUiSettings: getConfig(UI_SETTINGS.HISTOGRAM_MAX_BARS), maxBucketsUserInput: aggConfig.params.maxBars, intervalBase: aggConfig.params.intervalBase, + esTypes: aggConfig.params.field?.spec?.esTypes || [], }); }, }, diff --git a/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.test.ts b/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.test.ts index d3a95b32cd425..7e5e20e5917aa 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.test.ts @@ -21,6 +21,7 @@ import { calculateHistogramInterval, CalculateHistogramIntervalParams, } from './histogram_calculate_interval'; +import { ES_FIELD_TYPES } from '../../../../types'; describe('calculateHistogramInterval', () => { describe('auto calculating mode', () => { @@ -36,10 +37,91 @@ describe('calculateHistogramInterval', () => { min: 0, max: 1, }, + esTypes: [], }; }); describe('maxBucketsUserInput is defined', () => { + test('should set 1 as an interval for integer numbers that are less than maxBuckets #1', () => { + const p = { + ...params, + maxBucketsUserInput: 100, + values: { + min: 1, + max: 50, + }, + esTypes: [ES_FIELD_TYPES.INTEGER], + }; + expect(calculateHistogramInterval(p)).toEqual(1); + }); + + test('should set 1 as an interval for integer numbers that are less than maxBuckets #2', () => { + const p = { + ...params, + maxBucketsUiSettings: 1000, + maxBucketsUserInput: 258, + values: { + min: 521, + max: 689, + }, + esTypes: [ES_FIELD_TYPES.INTEGER], + }; + expect(calculateHistogramInterval(p)).toEqual(1); + }); + + test('should set correct interval for integer numbers that are greater than maxBuckets #1', () => { + const p = { + ...params, + maxBucketsUserInput: 100, + values: { + min: 400, + max: 790, + }, + esTypes: [ES_FIELD_TYPES.INTEGER, ES_FIELD_TYPES.SHORT], + }; + expect(calculateHistogramInterval(p)).toEqual(5); + }); + + test('should set correct interval for integer numbers that are greater than maxBuckets #2', () => { + // diff === 3456211; interval === 50000; buckets === 69 + const p = { + ...params, + maxBucketsUserInput: 100, + values: { + min: 567, + max: 3456778, + }, + esTypes: [ES_FIELD_TYPES.LONG], + }; + expect(calculateHistogramInterval(p)).toEqual(50000); + }); + + test('should not set integer interval if the field type is float #1', () => { + const p = { + ...params, + maxBucketsUserInput: 100, + values: { + min: 0, + max: 1, + }, + esTypes: [ES_FIELD_TYPES.FLOAT], + }; + expect(calculateHistogramInterval(p)).toEqual(0.01); + }); + + test('should not set integer interval if the field type is float #2', () => { + const p = { + ...params, + maxBucketsUserInput: 100, + values: { + min: 0, + max: 1, + }, + esTypes: [ES_FIELD_TYPES.INTEGER, ES_FIELD_TYPES.FLOAT], + }; + expect(calculateHistogramInterval(p)).toEqual(0.01); + }); + test('should not set interval which more than largest possible', () => { const p = { ...params, @@ -48,6 +130,7 @@ describe('calculateHistogramInterval', () => { min: 150, max: 250, }, + esTypes: [ES_FIELD_TYPES.SHORT], }; expect(calculateHistogramInterval(p)).toEqual(1); }); @@ -61,6 +144,7 @@ describe('calculateHistogramInterval', () => { min: 0.1, max: 0.9, }, + esTypes: [ES_FIELD_TYPES.FLOAT], }) ).toBe(0.02); }); @@ -74,6 +158,7 @@ describe('calculateHistogramInterval', () => { min: 10.45, max: 1000.05, }, + esTypes: [ES_FIELD_TYPES.FLOAT], }) ).toBe(100); }); @@ -88,6 +173,7 @@ describe('calculateHistogramInterval', () => { min: 0, max: 100, }, + esTypes: [ES_FIELD_TYPES.BYTE], }) ).toEqual(1); }); @@ -100,8 +186,9 @@ describe('calculateHistogramInterval', () => { min: 1, max: 10, }, + esTypes: [ES_FIELD_TYPES.INTEGER], }) - ).toEqual(0.1); + ).toEqual(1); }); test('should set intervals for integer numbers (diff more than maxBucketsUiSettings)', () => { @@ -113,6 +200,7 @@ describe('calculateHistogramInterval', () => { min: 45678, max: 90123, }, + esTypes: [ES_FIELD_TYPES.INTEGER], }) ).toEqual(500); }); @@ -127,6 +215,7 @@ describe('calculateHistogramInterval', () => { min: 1.245, max: 2.9, }, + esTypes: [ES_FIELD_TYPES.FLOAT], }) ).toEqual(0.02); expect( @@ -136,6 +225,7 @@ describe('calculateHistogramInterval', () => { min: 0.5, max: 2.3, }, + esTypes: [ES_FIELD_TYPES.FLOAT], }) ).toEqual(0.02); }); @@ -149,6 +239,7 @@ describe('calculateHistogramInterval', () => { min: 0.1, max: 0.9, }, + esTypes: [ES_FIELD_TYPES.FLOAT], }) ).toBe(0.01); }); diff --git a/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.ts index 378340e876296..313ecf1000f41 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/histogram_calculate_interval.ts @@ -18,6 +18,7 @@ */ import { isAutoInterval } from '../_interval_options'; +import { ES_FIELD_TYPES } from '../../../../types'; interface IntervalValuesRange { min: number; @@ -28,6 +29,7 @@ export interface CalculateHistogramIntervalParams { interval: string; maxBucketsUiSettings: number; maxBucketsUserInput?: number | ''; + esTypes: ES_FIELD_TYPES[]; intervalBase?: number; values?: IntervalValuesRange; } @@ -77,11 +79,27 @@ const calculateForGivenInterval = ( - The lower power of 10, times 2 - The lower power of 10, times 5 **/ -const calculateAutoInterval = (diff: number, maxBars: number) => { +const calculateAutoInterval = (diff: number, maxBars: number, esTypes: ES_FIELD_TYPES[]) => { const exactInterval = diff / maxBars; - const lowerPower = Math.pow(10, Math.floor(Math.log10(exactInterval))); + // For integer fields that are less than maxBars, we should use 1 as the value of interval + // Elastic has 4 integer data types: long, integer, short, byte + // see: https://www.elastic.co/guide/en/elasticsearch/reference/current/number.html + if ( + diff < maxBars && + esTypes.every((esType) => + [ + ES_FIELD_TYPES.INTEGER, + ES_FIELD_TYPES.LONG, + ES_FIELD_TYPES.SHORT, + ES_FIELD_TYPES.BYTE, + ].includes(esType) + ) + ) { + return 1; + } + const lowerPower = Math.pow(10, Math.floor(Math.log10(exactInterval))); const autoBuckets = diff / lowerPower; if (autoBuckets > maxBars) { @@ -103,6 +121,7 @@ export const calculateHistogramInterval = ({ maxBucketsUserInput, intervalBase, values, + esTypes, }: CalculateHistogramIntervalParams) => { const isAuto = isAutoInterval(interval); let calculatedInterval = isAuto ? 0 : parseFloat(interval); @@ -119,8 +138,10 @@ export const calculateHistogramInterval = ({ calculatedInterval = isAuto ? calculateAutoInterval( diff, + // Mind maxBucketsUserInput can be an empty string, hence we need to ensure it here - Math.min(maxBucketsUiSettings, maxBucketsUserInput || maxBucketsUiSettings) + Math.min(maxBucketsUiSettings, maxBucketsUserInput || maxBucketsUiSettings), + esTypes ) : calculateForGivenInterval(diff, calculatedInterval, maxBucketsUiSettings); } diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts new file mode 100644 index 0000000000000..15399ffc43791 --- /dev/null +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay.test.ts @@ -0,0 +1,76 @@ +/* + * 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 { AggConfigs } from '../agg_configs'; +import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common/field_formats'; +import { getShardDelayBucketAgg, SHARD_DELAY_AGG_NAME } from './shard_delay'; + +describe('Shard Delay Agg', () => { + const getConfig = (() => {}) as FieldFormatsGetConfigFn; + const getAggConfigs = () => { + const field = { name: 'bytes' }; + + const indexPattern = { + id: '1234', + title: 'logstash-*', + fields: { + getByName: () => field, + filter: () => [field], + }, + getFormatterForField: () => + new NumberFormat( + { + pattern: '0,0.[000] b', + }, + getConfig + ), + } as any; + + return new AggConfigs( + indexPattern, + [ + { + type: SHARD_DELAY_AGG_NAME, + params: { + duration: 1000, + }, + }, + ], + { + typesRegistry: { + get: getShardDelayBucketAgg, + } as any, + } + ); + }; + + describe('write', () => { + test('writes the delay as the value parameter', () => { + const aggConfigs = getAggConfigs(); + const agg = aggConfigs.aggs[0]; + expect(agg.write(aggConfigs)).toMatchInlineSnapshot(` + Object { + "params": Object { + "value": "5s", + }, + } + `); + }); + }); +}); diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay.ts new file mode 100644 index 0000000000000..2decf5a74bc85 --- /dev/null +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay.ts @@ -0,0 +1,50 @@ +/* + * 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 { BucketAggType } from './bucket_agg_type'; +import { BaseAggParams } from '../types'; +import { aggShardDelayFnName } from './shard_delay_fn'; + +export const SHARD_DELAY_AGG_NAME = 'shard_delay'; + +export interface AggParamsShardDelay extends BaseAggParams { + delay?: number; +} + +export const getShardDelayBucketAgg = () => + new BucketAggType({ + name: SHARD_DELAY_AGG_NAME, + title: 'Shard Delay', + expressionName: aggShardDelayFnName, + createFilter: () => ({ match_all: {} }), + customLabels: false, + params: [ + { + name: 'delay', + type: 'string', + default: '5s', + write(aggConfig, output) { + output.params = { + ...output.params, + value: aggConfig.params.delay, + }; + }, + }, + ], + }); diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.test.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.test.ts new file mode 100644 index 0000000000000..b0ebfb005c218 --- /dev/null +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { functionWrapper } from '../test_helpers'; +import { aggShardDelay } from './shard_delay_fn'; + +describe('agg_expression_functions', () => { + describe('aggShardDelay', () => { + const fn = functionWrapper(aggShardDelay()); + + test('correctly serializes', () => { + const actual = fn({ + delay: 1000, + }); + expect(actual).toMatchInlineSnapshot(` + Object { + "type": "agg_type", + "value": Object { + "enabled": true, + "id": undefined, + "params": Object { + "customLabel": undefined, + "delay": 1000, + "json": undefined, + }, + "schema": undefined, + "type": "shard_delay", + }, + } + `); + }); + + test('correctly parses json string argument', () => { + const actual = fn({ + delay: 1000, + json: '{ "foo": true }', + }); + + expect(actual.value.params.json).toEqual({ foo: true }); + + expect(() => { + fn({ + delay: 1000, + json: '/// intentionally malformed json ///', + }); + }).toThrowErrorMatchingInlineSnapshot(`"Unable to parse json argument string"`); + }); + }); +}); diff --git a/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.ts b/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.ts new file mode 100644 index 0000000000000..86de428fa03d7 --- /dev/null +++ b/src/plugins/data/common/search/aggs/buckets/shard_delay_fn.ts @@ -0,0 +1,105 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { Assign } from '@kbn/utility-types'; +import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { AggExpressionType, AggConfigSerialized } from '../'; +import { getParsedValue } from '../utils/get_parsed_value'; +import { AggParamsShardDelay, SHARD_DELAY_AGG_NAME } from './shard_delay'; + +export const aggShardDelayFnName = 'aggShardDelay'; + +type Input = any; +type AggArgs = AggParamsShardDelay & Pick; + +type Arguments = Assign; + +type Output = AggExpressionType; +type FunctionDefinition = ExpressionFunctionDefinition< + typeof aggShardDelayFnName, + Input, + Arguments, + Output +>; + +export const aggShardDelay = (): FunctionDefinition => ({ + name: aggShardDelayFnName, + help: i18n.translate('data.search.aggs.function.buckets.shardDelay.help', { + defaultMessage: 'Generates a serialized agg config for a Shard Delay agg', + }), + type: 'agg_type', + args: { + id: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.shardDelay.id.help', { + defaultMessage: 'ID for this aggregation', + }), + }, + enabled: { + types: ['boolean'], + default: true, + help: i18n.translate('data.search.aggs.buckets.shardDelay.enabled.help', { + defaultMessage: 'Specifies whether this aggregation should be enabled', + }), + }, + schema: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.shardDelay.schema.help', { + defaultMessage: 'Schema to use for this aggregation', + }), + }, + delay: { + types: ['number'], + help: i18n.translate('data.search.aggs.buckets.shardDelay.delay.help', { + defaultMessage: 'Delay in ms between shards to process.', + }), + }, + json: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.shardDelay.json.help', { + defaultMessage: 'Advanced json to include when the agg is sent to Elasticsearch', + }), + }, + customLabel: { + types: ['string'], + help: i18n.translate('data.search.aggs.buckets.shardDelay.customLabel.help', { + defaultMessage: 'Represents a custom label for this aggregation', + }), + }, + }, + fn: (input, args) => { + const { id, enabled, schema, ...rest } = args; + + return { + type: 'agg_type', + value: { + id, + enabled, + schema, + type: SHARD_DELAY_AGG_NAME, + params: { + ...rest, + json: getParsedValue(args, 'json'), + delay: getParsedValue(args, 'delay'), + }, + }, + }; + }, +}); diff --git a/src/plugins/data/common/search/aggs/param_types/field.ts b/src/plugins/data/common/search/aggs/param_types/field.ts index 492294bdf4e5f..a0bc71ac8e156 100644 --- a/src/plugins/data/common/search/aggs/param_types/field.ts +++ b/src/plugins/data/common/search/aggs/param_types/field.ts @@ -90,9 +90,10 @@ export class FieldParamType extends BaseParamType { 'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage', { defaultMessage: - 'Saved {fieldParameter} parameter is now invalid. Please select a new field.', + 'Saved field "{fieldParameter}" is invalid for use with the "{aggType}" aggregation. Please select a new field.', values: { - fieldParameter: '"field"', + fieldParameter: fieldName, + aggType: aggConfig?.type?.title, }, } ) diff --git a/src/plugins/data/common/search/es_search/index.ts b/src/plugins/data/common/search/es_search/index.ts index d8f7b5091eb8f..8e8897c7d7517 100644 --- a/src/plugins/data/common/search/es_search/index.ts +++ b/src/plugins/data/common/search/es_search/index.ts @@ -18,3 +18,4 @@ */ export * from './types'; +export * from './utils'; diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts index 81124c1e095f7..b1c3e5cdd3960 100644 --- a/src/plugins/data/common/search/es_search/types.ts +++ b/src/plugins/data/common/search/es_search/types.ts @@ -37,22 +37,8 @@ export type ISearchRequestParams> = { trackTotalHits?: boolean; } & Search; -export interface IEsSearchRequest extends IKibanaSearchRequest { - params?: ISearchRequestParams; +export interface IEsSearchRequest extends IKibanaSearchRequest { indexType?: string; } -export interface IEsSearchResponse extends IKibanaSearchResponse { - /** - * Indicates whether async search is still in flight - */ - isRunning?: boolean; - /** - * Indicates whether the results returned are complete or partial - */ - isPartial?: boolean; - rawResponse: SearchResponse; -} - -export const isEsResponse = (response: any): response is IEsSearchResponse => - response && response.rawResponse; +export type IEsSearchResponse = IKibanaSearchResponse>; diff --git a/src/plugins/data/common/search/es_search/utils.ts b/src/plugins/data/common/search/es_search/utils.ts new file mode 100644 index 0000000000000..ec66a3d3f923e --- /dev/null +++ b/src/plugins/data/common/search/es_search/utils.ts @@ -0,0 +1,41 @@ +/* + * 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 { IKibanaSearchResponse } from '..'; + +/** + * @returns true if response had an error while executing in ES + */ +export const isErrorResponse = (response?: IKibanaSearchResponse) => { + return !response || (!response.isRunning && response.isPartial); +}; + +/** + * @returns true if response is completed successfully + */ +export const isCompleteResponse = (response?: IKibanaSearchResponse) => { + return response && !response.isRunning && !response.isPartial; +}; + +/** + * @returns true if request is still running an/d response contains partial results + */ +export const isPartialResponse = (response?: IKibanaSearchResponse) => { + return response && response.isRunning && response.isPartial; +}; diff --git a/src/plugins/data/common/search/search_source/index.ts b/src/plugins/data/common/search/search_source/index.ts index 70c9cfcee2348..2ef1b4c3c5199 100644 --- a/src/plugins/data/common/search/search_source/index.ts +++ b/src/plugins/data/common/search/search_source/index.ts @@ -17,11 +17,12 @@ * under the License. */ -export { SearchSource, ISearchSource, SearchSourceDependencies } from './search_source'; export { createSearchSource } from './create_search_source'; -export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types'; export { injectReferences } from './inject_references'; export { extractReferences } from './extract_references'; export { parseSearchSourceJSON } from './parse_json'; export * from './fetch'; export * from './legacy'; +export * from './search_source'; +export * from './search_source_service'; +export * from './types'; diff --git a/src/plugins/data/common/search/search_source/inject_references.test.ts b/src/plugins/data/common/search/search_source/inject_references.test.ts new file mode 100644 index 0000000000000..c607f16d24734 --- /dev/null +++ b/src/plugins/data/common/search/search_source/inject_references.test.ts @@ -0,0 +1,120 @@ +/* + * 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 { SavedObjectReference } from 'src/core/types'; +import { SearchSourceFields } from './types'; + +import { injectReferences } from './inject_references'; + +describe('injectSearchSourceReferences', () => { + let searchSourceJSON: SearchSourceFields & { indexRefName: string }; + let references: SavedObjectReference[]; + + beforeEach(() => { + searchSourceJSON = { + highlightAll: true, + version: true, + query: { + query: 'play_name:"Henry IV"', + language: 'kuery', + }, + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + }; + references = [ + { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + type: 'index-pattern', + id: '033af690-fde7-11ea-91f3-fb9e73f9bbe9', + }, + ]; + }); + + test('injects references', () => { + const actual = injectReferences(searchSourceJSON, references); + expect(actual).toMatchInlineSnapshot(` + Object { + "highlightAll": true, + "index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9", + "query": Object { + "language": "kuery", + "query": "play_name:\\"Henry IV\\"", + }, + "version": true, + } + `); + }); + + test('skips injecting references if none exists', () => { + // @ts-expect-error + delete searchSourceJSON.indexRefName; + references = []; + const actual = injectReferences(searchSourceJSON, references); + expect(actual).toMatchInlineSnapshot(` + Object { + "highlightAll": true, + "query": Object { + "language": "kuery", + "query": "play_name:\\"Henry IV\\"", + }, + "version": true, + } + `); + }); + + test('throws an error if there is a broken reference', () => { + searchSourceJSON.indexRefName = 'oops'; + expect(() => injectReferences(searchSourceJSON, references)).toThrowErrorMatchingInlineSnapshot( + `"Could not find reference for oops"` + ); + }); + + test('handles filters', () => { + searchSourceJSON.filter = [ + // @ts-expect-error + { meta: { indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index' } }, + ]; + const actual = injectReferences(searchSourceJSON, references); + expect(actual).toMatchInlineSnapshot(` + Object { + "filter": Array [ + Object { + "meta": Object { + "index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9", + }, + }, + ], + "highlightAll": true, + "index": "033af690-fde7-11ea-91f3-fb9e73f9bbe9", + "query": Object { + "language": "kuery", + "query": "play_name:\\"Henry IV\\"", + }, + "version": true, + } + `); + }); + + test('throws an error if there is a broken filter reference', () => { + // @ts-expect-error + searchSourceJSON.filter = [{ meta: { indexRefName: 'oops' } }]; + expect(() => injectReferences(searchSourceJSON, references)).toThrowErrorMatchingInlineSnapshot( + `"Could not find reference for oops"` + ); + }); +}); diff --git a/src/plugins/data/common/search/search_source/legacy/types.ts b/src/plugins/data/common/search/search_source/legacy/types.ts index 1a0a96a76a703..8ac713a658932 100644 --- a/src/plugins/data/common/search/search_source/legacy/types.ts +++ b/src/plugins/data/common/search/search_source/legacy/types.ts @@ -18,15 +18,36 @@ */ import { BehaviorSubject } from 'rxjs'; +import { ApiResponse } from '@elastic/elasticsearch'; import { SearchResponse } from 'elasticsearch'; import { FetchHandlers, SearchRequest } from '../fetch'; +interface MsearchHeaders { + index: string; + preference?: number | string; +} + +interface MsearchRequest { + header: MsearchHeaders; + body: any; +} + +// @internal +export interface MsearchRequestBody { + searches: MsearchRequest[]; +} + +// @internal +export interface MsearchResponse { + body: ApiResponse<{ responses: Array> }>; +} + // @internal export interface LegacyFetchHandlers { callMsearch: (params: { - body: SearchRequest; + body: MsearchRequestBody; signal: AbortSignal; - }) => Promise>>; + }) => Promise; loadingCount$: BehaviorSubject; } diff --git a/src/plugins/data/common/search/search_source/mocks.ts b/src/plugins/data/common/search/search_source/mocks.ts index f582861e37c15..d4c0707f950bb 100644 --- a/src/plugins/data/common/search/search_source/mocks.ts +++ b/src/plugins/data/common/search/search_source/mocks.ts @@ -20,8 +20,8 @@ import { BehaviorSubject } from 'rxjs'; import { uiSettingsServiceMock } from '../../../../../core/public/mocks'; -import { ISearchSource, SearchSource } from './search_source'; -import { SearchSourceFields } from './types'; +import { SearchSource } from './search_source'; +import { ISearchStartSearchSource, ISearchSource, SearchSourceFields } from './types'; export const searchSourceInstanceMock: MockedKeys = { setPreferredSearchStrategyId: jest.fn(), @@ -45,7 +45,7 @@ export const searchSourceInstanceMock: MockedKeys = { serialize: jest.fn(), }; -export const searchSourceMock = { +export const searchSourceCommonMock: jest.Mocked = { create: jest.fn().mockReturnValue(searchSourceInstanceMock), createEmpty: jest.fn().mockReturnValue(searchSourceInstanceMock), }; diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 74abd9238bc2b..00e06663e998e 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Observable, BehaviorSubject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { IndexPattern } from '../../index_patterns'; import { GetConfigFn } from '../../types'; import { fetchSoon } from './legacy'; @@ -53,16 +53,7 @@ describe('SearchSource', () => { let searchSourceDependencies: SearchSourceDependencies; beforeEach(() => { - mockSearchMethod = jest.fn(() => { - return new Observable((subscriber) => { - setTimeout(() => { - subscriber.next({ - rawResponse: '', - }); - subscriber.complete(); - }, 100); - }); - }); + mockSearchMethod = jest.fn().mockResolvedValue({ rawResponse: '' }); searchSourceDependencies = { getConfig: jest.fn(), diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index d8a036ce970dd..3eafea06f9f5b 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -71,22 +71,15 @@ import { setWith } from '@elastic/safer-lodash-set'; import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash'; -import { map } from 'rxjs/operators'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/common'; import { IIndexPattern } from '../../index_patterns'; -import { ISearchGeneric } from '../..'; -import { SearchSourceOptions, SearchSourceFields } from './types'; +import { IEsSearchRequest, IEsSearchResponse, ISearchOptions } from '../..'; +import { ISearchSource, SearchSourceOptions, SearchSourceFields } from './types'; import { FetchHandlers, RequestFailure, getSearchParamsFromRequest, SearchRequest } from './fetch'; -import { - getEsQueryConfig, - buildEsQuery, - Filter, - UI_SETTINGS, - ISearchOptions, -} from '../../../common'; +import { getEsQueryConfig, buildEsQuery, Filter, UI_SETTINGS } from '../../../common'; import { getHighlightRequest } from '../../../common/field_formats'; import { fetchSoon } from './legacy'; import { extractReferences } from './extract_references'; @@ -108,7 +101,7 @@ export const searchSourceRequiredUiSettings = [ ]; export interface SearchSourceDependencies extends FetchHandlers { - search: ISearchGeneric; + search: (request: IEsSearchRequest, options: ISearchOptions) => Promise; } /** @public **/ @@ -264,7 +257,7 @@ export class SearchSource { if (getConfig(UI_SETTINGS.COURIER_BATCH_SEARCHES)) { response = await this.legacyFetch(searchRequest, options); } else { - response = await this.fetch$(searchRequest, options).toPromise(); + response = await this.fetchSearch(searchRequest, options); } // TODO: Remove casting when https://github.com/elastic/elasticsearch-js/issues/1287 is resolved @@ -308,17 +301,17 @@ export class SearchSource { /** * Run a search using the search service - * @return {Observable>} + * @return {Promise>} */ - private fetch$(searchRequest: SearchRequest, options: ISearchOptions) { + private fetchSearch(searchRequest: SearchRequest, options: ISearchOptions) { const { search, getConfig, onResponse } = this.dependencies; const params = getSearchParamsFromRequest(searchRequest, { getConfig, }); - return search({ params, indexType: searchRequest.indexType }, options).pipe( - map(({ rawResponse }) => onResponse(searchRequest, rawResponse)) + return search({ params, indexType: searchRequest.indexType }, options).then(({ rawResponse }) => + onResponse(searchRequest, rawResponse) ); } @@ -558,9 +551,3 @@ export class SearchSource { return [filterField]; } } - -/** - * search source interface - * @public - */ -export type ISearchSource = Pick; diff --git a/src/plugins/data/common/search/search_source/search_source_service.test.ts b/src/plugins/data/common/search/search_source/search_source_service.test.ts new file mode 100644 index 0000000000000..bbbaac11bbe91 --- /dev/null +++ b/src/plugins/data/common/search/search_source/search_source_service.test.ts @@ -0,0 +1,50 @@ +/* + * 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 { BehaviorSubject } from 'rxjs'; +import { IndexPatternsContract } from '../../index_patterns/index_patterns'; +import { SearchSourceService, SearchSourceDependencies } from './'; + +describe('SearchSource service', () => { + let dependencies: jest.Mocked; + + beforeEach(() => { + jest.resetModules(); + dependencies = { + getConfig: jest.fn(), + search: jest.fn(), + onResponse: jest.fn(), + legacy: { + callMsearch: jest.fn(), + loadingCount$: new BehaviorSubject(0), + }, + }; + }); + + describe('start()', () => { + test('exposes proper contract', () => { + const start = new SearchSourceService().start( + (jest.fn() as unknown) as jest.Mocked, + dependencies + ); + + expect(Object.keys(start)).toEqual(['create', 'createEmpty']); + }); + }); +}); diff --git a/src/plugins/data/common/search/search_source/search_source_service.ts b/src/plugins/data/common/search/search_source/search_source_service.ts new file mode 100644 index 0000000000000..40981402419c7 --- /dev/null +++ b/src/plugins/data/common/search/search_source/search_source_service.ts @@ -0,0 +1,42 @@ +/* + * 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 { createSearchSource, SearchSource, SearchSourceDependencies } from './'; +import { IndexPatternsContract } from '../../index_patterns/index_patterns'; + +export class SearchSourceService { + public setup() {} + + public start(indexPatterns: IndexPatternsContract, dependencies: SearchSourceDependencies) { + return { + /** + * creates searchsource based on serialized search source fields + */ + create: createSearchSource(indexPatterns, dependencies), + /** + * creates an enpty search source + */ + createEmpty: () => { + return new SearchSource({}, dependencies); + }, + }; + } + + public stop() {} +} diff --git a/src/plugins/data/common/search/search_source/types.ts b/src/plugins/data/common/search/search_source/types.ts index 0882aa9a2ceec..bbfc308e0459d 100644 --- a/src/plugins/data/common/search/search_source/types.ts +++ b/src/plugins/data/common/search/search_source/types.ts @@ -16,9 +16,32 @@ * specific language governing permissions and limitations * under the License. */ + import { NameList } from 'elasticsearch'; -import { IndexPattern, Query } from '../..'; -import { Filter } from '../../../common'; +import { Filter, IndexPattern, Query } from '../..'; +import { SearchSource } from './search_source'; + +/** + * search source interface + * @public + */ +export type ISearchSource = Pick; + +/** + * high level search service + * @public + */ +export interface ISearchStartSearchSource { + /** + * creates {@link SearchSource} based on provided serialized {@link SearchSourceFields} + * @param fields + */ + create: (fields?: SearchSourceFields) => Promise; + /** + * creates empty {@link SearchSource} + */ + createEmpty: () => ISearchSource; +} export type EsQuerySearchAfter = [string | number, string | number]; @@ -75,8 +98,6 @@ export interface SearchSourceOptions { callParentStartHandlers?: boolean; } -export { ISearchSource } from './search_source'; - export interface SortOptions { mode?: 'min' | 'max' | 'sum' | 'avg' | 'median'; type?: 'double' | 'long' | 'date' | 'date_nanos'; diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index 0a299b57275f8..c3943af5c6ff7 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -26,14 +26,14 @@ export type ISearch = ( ) => Observable; export type ISearchGeneric = < - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( request: SearchStrategyRequest, options?: ISearchOptions ) => Observable; -export interface IKibanaSearchResponse { +export interface IKibanaSearchResponse { /** * Some responses may contain a unique id to identify the request this response came from. */ @@ -50,16 +50,25 @@ export interface IKibanaSearchResponse { * that represents how progress is indicated. */ loaded?: number; + + /** + * Indicates whether search is still in flight + */ + isRunning?: boolean; + + /** + * Indicates whether the results returned are complete or partial + */ + isPartial?: boolean; + + rawResponse: RawResponse; } -export interface IKibanaSearchRequest { +export interface IKibanaSearchRequest { /** * An id can be used to uniquely identify this request. */ id?: string; - /** - * Optionally tell search strategies to output debug information. - */ - debug?: boolean; + params?: Params; } diff --git a/src/plugins/data/config.ts b/src/plugins/data/config.ts index 09cb2cb2afeca..83a384a049a75 100644 --- a/src/plugins/data/config.ts +++ b/src/plugins/data/config.ts @@ -28,6 +28,16 @@ export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), }), }), + search: schema.object({ + aggs: schema.object({ + shardDelay: schema.object({ + // Whether or not to register the shard_delay (which is only available in snapshot versions + // of Elasticsearch) agg type/expression function to make it available in the UI for either + // functional or manual testing + enabled: schema.boolean({ defaultValue: false }), + }), + }), + }), }); export type ConfigSchema = TypeOf; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 5038af9409316..0e21f6f695551 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -230,6 +230,8 @@ import { formatHitProvider, } from './index_patterns'; +export type { IndexPatternsService } from './index_patterns'; + // Index patterns namespace: export const indexPatterns = { ILLEGAL_CHARACTERS_KEY, @@ -262,9 +264,12 @@ export { UI_SETTINGS, TypeMeta as IndexPatternTypeMeta, AggregationRestrictions as IndexPatternAggRestrictions, + IndexPatternSpec, fieldList, } from '../common'; +export { DuplicateIndexPatternError } from '../common/index_patterns/errors'; + /* * Autocomplete query suggestions: */ @@ -360,8 +365,6 @@ export { ISearchGeneric, ISearchSource, parseSearchSourceJSON, - RequestTimeoutError, - SearchError, SearchInterceptor, SearchInterceptorDeps, SearchRequest, @@ -370,11 +373,16 @@ export { // expression functions and types EsdslExpressionFunctionDefinition, EsRawResponseExpressionTypeDefinition, + // errors + SearchError, + SearchTimeoutError, + TimeoutErrorMode, + PainlessError, } from './search'; export type { SearchSource } from './search'; -export { ISearchOptions } from '../common'; +export { ISearchOptions, isErrorResponse, isCompleteResponse, isPartialResponse } from '../common'; // Search namespace export const search = { @@ -415,6 +423,7 @@ export { StatefulSearchBarProps, FilterBar, QueryStringInput, + QueryStringInputProps, IndexPatternSelect, } from './ui'; diff --git a/src/plugins/data/public/index_patterns/index_pattern.stub.ts b/src/plugins/data/public/index_patterns/index_pattern.stub.ts new file mode 100644 index 0000000000000..e5c6c008e3e28 --- /dev/null +++ b/src/plugins/data/public/index_patterns/index_pattern.stub.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 sinon from 'sinon'; + +import { CoreSetup } from 'src/core/public'; +import { FieldFormat as FieldFormatImpl } from '../../common/field_formats'; +import { IFieldType, FieldSpec } from '../../common/index_patterns'; +import { FieldFormatsStart } from '../field_formats'; +import { IndexPattern, indexPatterns, KBN_FIELD_TYPES, fieldList } from '../'; +import { getFieldFormatsRegistry } from '../test_utils'; +import { setFieldFormats } from '../services'; + +setFieldFormats(({ + getDefaultInstance: () => + ({ + getConverterFor: () => (value: any) => value, + convert: (value: any) => JSON.stringify(value), + } as FieldFormatImpl), +} as unknown) as FieldFormatsStart); + +export function getStubIndexPattern( + pattern: string, + getConfig: (cfg: any) => any, + timeField: string | null, + fields: FieldSpec[] | IFieldType[], + core: CoreSetup +): IndexPattern { + return (new StubIndexPattern( + pattern, + getConfig, + timeField, + fields, + core + ) as unknown) as IndexPattern; +} + +export class StubIndexPattern { + id: string; + title: string; + popularizeField: Function; + timeFieldName: string | null; + isTimeBased: () => boolean; + getConfig: (cfg: any) => any; + getNonScriptedFields: Function; + getScriptedFields: Function; + getFieldByName: Function; + getSourceFiltering: Function; + metaFields: string[]; + fieldFormatMap: Record; + getComputedFields: Function; + flattenHit: Function; + formatHit: Record; + fieldsFetcher: Record; + formatField: Function; + getFormatterForField: () => { convert: Function }; + _reindexFields: Function; + stubSetFieldFormat: Function; + fields?: FieldSpec[]; + + constructor( + pattern: string, + getConfig: (cfg: any) => any, + timeField: string | null, + fields: FieldSpec[] | IFieldType[], + core: CoreSetup + ) { + const registeredFieldFormats = getFieldFormatsRegistry(core); + + this.id = pattern; + this.title = pattern; + this.popularizeField = sinon.stub(); + this.timeFieldName = timeField; + this.isTimeBased = () => Boolean(this.timeFieldName); + this.getConfig = getConfig; + this.getNonScriptedFields = sinon.spy(IndexPattern.prototype.getNonScriptedFields); + this.getScriptedFields = sinon.spy(IndexPattern.prototype.getScriptedFields); + this.getFieldByName = sinon.spy(IndexPattern.prototype.getFieldByName); + this.getSourceFiltering = sinon.stub(); + this.metaFields = ['_id', '_type', '_source']; + this.fieldFormatMap = {}; + + this.getComputedFields = IndexPattern.prototype.getComputedFields.bind(this); + this.flattenHit = indexPatterns.flattenHitWrapper( + (this as unknown) as IndexPattern, + this.metaFields + ); + this.formatHit = indexPatterns.formatHitProvider( + (this as unknown) as IndexPattern, + registeredFieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING) + ); + this.fieldsFetcher = { apiClient: { baseUrl: '' } }; + this.formatField = this.formatHit.formatField; + this.getFormatterForField = () => ({ + convert: () => '', + }); + + this._reindexFields = function () { + this.fields = fieldList((this.fields || fields) as FieldSpec[], false); + }; + + this.stubSetFieldFormat = function ( + fieldName: string, + id: string, + params: Record + ) { + const FieldFormat = registeredFieldFormats.getType(id); + this.fieldFormatMap[fieldName] = new FieldFormat!(params); + this._reindexFields(); + }; + + this._reindexFields(); + + return this; + } +} diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 0eb0e3b658045..1b83eb569b1a1 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -78,7 +78,7 @@ const createStartContract = (): Start => { }; }; -export { createSearchSourceMock } from './search/mocks'; +export { createSearchSourceMock } from '../common/search/search_source/mocks'; export { getCalculateAutoTimeExpression } from '../common/search/aggs'; export const dataPluginMock = { diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 3b18e0fbed537..5abf4d3648af7 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -96,7 +96,7 @@ export class DataPublicPlugin private readonly storage: IStorageWrapper; constructor(initializerContext: PluginInitializerContext) { - this.searchService = new SearchService(); + this.searchService = new SearchService(initializerContext); this.queryService = new QueryService(); this.fieldFormatsService = new FieldFormatsService(); this.autocomplete = new AutocompleteService(initializerContext); diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index db8d9dba4e0c7..b8eb7a556ac44 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -7,7 +7,9 @@ import { $Values } from '@kbn/utility-types'; import _ from 'lodash'; import { Action } from 'history'; -import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; +import { ApiResponse } from '@elastic/elasticsearch'; +import { ApiResponse as ApiResponse_2 } from '@elastic/elasticsearch/lib/Transport'; +import { ApplicationStart } from 'kibana/public'; import { Assign } from '@kbn/utility-types'; import { BehaviorSubject } from 'rxjs'; import Boom from 'boom'; @@ -69,7 +71,6 @@ import { SavedObjectsClientContract } from 'src/core/public'; import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; -import { Subscription } from 'rxjs'; import { ToastInputFields } from 'src/core/public/notifications'; import { ToastsSetup } from 'kibana/public'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; @@ -454,6 +455,13 @@ export interface DataPublicPluginStartUi { SearchBar: React.ComponentType; } +// Warning: (ae-missing-release-tag) "DuplicateIndexPatternError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class DuplicateIndexPatternError extends Error { + constructor(message: string); +} + // @public (undocumented) export enum ES_FIELD_TYPES { // (undocumented) @@ -911,22 +919,15 @@ export interface IDataPluginServices extends Partial { // Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IEsSearchRequest extends IKibanaSearchRequest { +export interface IEsSearchRequest extends IKibanaSearchRequest { // (undocumented) indexType?: string; - // (undocumented) - params?: ISearchRequestParams; } // Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IEsSearchResponse extends IKibanaSearchResponse { - isPartial?: boolean; - isRunning?: boolean; - // (undocumented) - rawResponse: SearchResponse; -} +export type IEsSearchResponse = IKibanaSearchResponse>; // Warning: (ae-missing-release-tag) "IFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1004,16 +1005,15 @@ export interface IFieldType { // // @public (undocumented) export interface IIndexPattern { + // Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts + // // (undocumented) - [key: string]: any; - // (undocumented) - fieldFormatMap?: Record; + fieldFormatMap?: Record | undefined>; // (undocumented) fields: IFieldType[]; // (undocumented) + getFormatterForField?: (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat; + // (undocumented) getTimeField?(): IFieldType | undefined; // (undocumented) id?: string; @@ -1043,10 +1043,12 @@ export interface IIndexPatternFieldList extends Array { removeAll(): void; // (undocumented) replaceAll(specs: FieldSpec[]): void; + // Warning: (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts + // // (undocumented) toSpec(options?: { getFormatterForField?: IndexPattern['getFormatterForField']; - }): FieldSpec[]; + }): IndexPatternFieldMap; // (undocumented) update(field: FieldSpec): void; } @@ -1054,17 +1056,22 @@ export interface IIndexPatternFieldList extends Array { // Warning: (ae-missing-release-tag) "IKibanaSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IKibanaSearchRequest { - debug?: boolean; +export interface IKibanaSearchRequest { id?: string; + // (undocumented) + params?: Params; } // Warning: (ae-missing-release-tag) "IKibanaSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IKibanaSearchResponse { +export interface IKibanaSearchResponse { id?: string; + isPartial?: boolean; + isRunning?: boolean; loaded?: number; + // (undocumented) + rawResponse: RawResponse; total?: number; } @@ -1079,27 +1086,23 @@ export type IMetricAggType = MetricAggType; // @public (undocumented) export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts - constructor(id: string | undefined, { savedObjectsClient, apiClient, patternCache, fieldFormats, indexPatternsService, onNotification, onError, shortDotsEnable, metaFields, }: IndexPatternDeps); - // (undocumented) - addScriptedField(name: string, script: string, fieldType: string | undefined, lang: string): Promise; - // (undocumented) - create(allowOverride?: boolean): Promise; + constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); + addScriptedField(name: string, script: string, fieldType?: string): Promise; // (undocumented) - _fetchFields(): Promise; - // (undocumented) - fieldFormatMap: any; + fieldFormatMap: Record; // (undocumented) fields: IIndexPatternFieldList & { - toSpec: () => FieldSpec[]; + toSpec: () => IndexPatternFieldMap; }; // (undocumented) - fieldsFetcher: any; - // (undocumented) - flattenHit: any; + flattenHit: (hit: Record, deep?: boolean) => Record; // (undocumented) - formatField: any; + formatField: FormatFieldFn; // (undocumented) - formatHit: any; + formatHit: { + (hit: Record, type?: string): any; + formatField: FormatFieldFn; + }; // (undocumented) getAggregationRestrictions(): Record> | undefined; + getAsSavedObjectBody(): { + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }; // (undocumented) getComputedFields(): { storedFields: string[]; @@ -1120,13 +1133,21 @@ export class IndexPattern implements IIndexPattern { }; // (undocumented) getFieldByName(name: string): IndexPatternField | undefined; - // (undocumented) - getFormatterForField(field: IndexPatternField | IndexPatternField['spec']): FieldFormat; + getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; // (undocumented) getNonScriptedFields(): IndexPatternField[]; + getOriginalSavedObjectBody: () => { + title?: string | undefined; + timeFieldName?: string | undefined; + intervalName?: string | undefined; + fields?: string | undefined; + sourceFilters?: string | undefined; + fieldFormatMap?: string | undefined; + typeMeta?: string | undefined; + type?: string | undefined; + }; // (undocumented) getScriptedFields(): IndexPatternField[]; - // (undocumented) getSourceFiltering(): { excludes: any[]; }; @@ -1135,44 +1156,15 @@ export class IndexPattern implements IIndexPattern { // (undocumented) id?: string; // (undocumented) - init(): Promise; - // Warning: (ae-forgotten-export) The symbol "IndexPatternSpec" needs to be exported by the entry point index.d.ts - // - // (undocumented) - initFromSpec(spec: IndexPatternSpec): this; - // (undocumented) intervalName: string | undefined; // (undocumented) isTimeBased(): boolean; // (undocumented) - isTimeBasedWildcard(): boolean; - // (undocumented) isTimeNanosBased(): boolean; // (undocumented) - isWildcard(): boolean; - // (undocumented) metaFields: string[]; - // (undocumented) - originalBody: { - [key: string]: any; - }; - // (undocumented) - popularizeField(fieldName: string, unit?: number): Promise; - // (undocumented) - prepBody(): { - title: string; - timeFieldName: string | undefined; - intervalName: string | undefined; - sourceFilters: string | undefined; - fields: string | undefined; - fieldFormatMap: string | undefined; - type: string | undefined; - typeMeta: string | undefined; - }; - // (undocumented) - refreshFields(): Promise; - // (undocumented) removeScriptedField(fieldName: string): void; + resetOriginalSavedObjectBody: () => void; // Warning: (ae-forgotten-export) The symbol "SourceFilter" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -1205,7 +1197,7 @@ export type IndexPatternAggRestrictions = Record | undefined; set conflictDescriptions(conflictDescriptions: Record | undefined); - // (undocumented) get count(): number; set count(count: number); // (undocumented) @@ -1244,14 +1234,12 @@ export class IndexPatternField implements IFieldType { get esTypes(): string[] | undefined; // (undocumented) get filterable(): boolean; - // (undocumented) get lang(): string | undefined; set lang(lang: string | undefined); // (undocumented) get name(): string; // (undocumented) get readFromDocValues(): boolean; - // (undocumented) get script(): string | undefined; set script(script: string | undefined); // (undocumented) @@ -1282,24 +1270,7 @@ export class IndexPatternField implements IFieldType { // (undocumented) toSpec({ getFormatterForField, }?: { getFormatterForField?: IndexPattern['getFormatterForField']; - }): { - count: number; - script: string | undefined; - lang: string | undefined; - conflictDescriptions: Record | undefined; - name: string; - type: string; - esTypes: string[] | undefined; - scripted: boolean; - searchable: boolean; - aggregatable: boolean; - readFromDocValues: boolean; - subType: import("../types").IFieldSubType | undefined; - format: { - id: any; - params: any; - } | undefined; - }; + }): FieldSpec; // (undocumented) get type(): string; // (undocumented) @@ -1323,7 +1294,6 @@ export const indexPatterns: { formatHitProvider: typeof formatHitProvider; }; -// Warning: (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IndexPatternsContract" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1356,6 +1326,67 @@ export class IndexPatternSelect extends Component { UNSAFE_componentWillReceiveProps(nextProps: IndexPatternSelectProps): void; } +// Warning: (ae-missing-release-tag) "IndexPatternSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IndexPatternSpec { + // (undocumented) + fields?: IndexPatternFieldMap; + // (undocumented) + id?: string; + // (undocumented) + intervalName?: string; + // (undocumented) + sourceFilters?: SourceFilter[]; + // (undocumented) + timeFieldName?: string; + // (undocumented) + title?: string; + // (undocumented) + type?: string; + // (undocumented) + typeMeta?: IndexPatternTypeMeta; + // (undocumented) + version?: string; +} + +// Warning: (ae-missing-release-tag) "IndexPatternsService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class IndexPatternsService { + // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceDeps" needs to be exported by the entry point index.d.ts + constructor({ uiSettings, savedObjectsClient, apiClient, fieldFormats, onNotification, onError, onRedirectNoIndexPattern, }: IndexPatternsServiceDeps); + clearCache: (id?: string | undefined) => void; + create(spec: IndexPatternSpec, skipFetchFields?: boolean): Promise; + createAndSave(spec: IndexPatternSpec, override?: boolean, skipFetchFields?: boolean): Promise; + createSavedObject(indexPattern: IndexPattern, override?: boolean): Promise; + delete(indexPatternId: string): Promise<{}>; + // Warning: (ae-forgotten-export) The symbol "EnsureDefaultIndexPattern" needs to be exported by the entry point index.d.ts + // + // (undocumented) + ensureDefaultIndexPattern: EnsureDefaultIndexPattern; + fieldArrayToMap: (fields: FieldSpec[]) => Record; + get: (id: string) => Promise; + // Warning: (ae-forgotten-export) The symbol "IndexPatternSavedObjectAttrs" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getCache: () => Promise[] | null | undefined>; + getDefault: () => Promise; + getFieldsForIndexPattern: (indexPattern: IndexPattern | IndexPatternSpec, options?: GetFieldsOptions) => Promise; + // Warning: (ae-forgotten-export) The symbol "GetFieldsOptions" needs to be exported by the entry point index.d.ts + getFieldsForWildcard: (options?: GetFieldsOptions) => Promise; + getIds: (refresh?: boolean) => Promise; + getIdsWithTitle: (refresh?: boolean) => Promise>; + getTitles: (refresh?: boolean) => Promise; + refreshFields: (indexPattern: IndexPattern) => Promise; + savedObjectToSpec: (savedObject: SavedObject) => IndexPatternSpec; + setDefault: (id: string, force?: boolean) => Promise; + updateSavedObject(indexPattern: IndexPattern, saveAttempts?: number, ignoreErrors?: boolean): Promise; +} + // Warning: (ae-missing-release-tag) "TypeMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1381,6 +1412,11 @@ export type InputTimeRange = TimeRange | { to: Moment; }; +// Warning: (ae-missing-release-tag) "isCompleteResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const isCompleteResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined; + // Warning: (ae-missing-release-tag) "ISearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1389,7 +1425,7 @@ export type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) // Warning: (ae-missing-release-tag) "ISearchGeneric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type ISearchGeneric = (request: SearchStrategyRequest, options?: ISearchOptions) => Observable; +export type ISearchGeneric = (request: SearchStrategyRequest, options?: ISearchOptions) => Observable; // Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1425,6 +1461,8 @@ export interface ISearchStart { aggs: AggsStart; search: ISearchGeneric; searchSource: ISearchStartSearchSource; + // (undocumented) + showError: (e: Error) => void; } // @public @@ -1433,6 +1471,11 @@ export interface ISearchStartSearchSource { createEmpty: () => ISearchSource; } +// Warning: (ae-missing-release-tag) "isErrorResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const isErrorResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined; + // Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1443,6 +1486,11 @@ export const isFilter: (x: unknown) => x is Filter; // @public (undocumented) export const isFilters: (x: unknown) => x is Filter[]; +// Warning: (ae-missing-release-tag) "isPartialResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const isPartialResponse: (response?: IKibanaSearchResponse | undefined) => boolean | undefined; + // Warning: (ae-missing-release-tag) "isQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1581,6 +1629,19 @@ export interface OptionedValueProp { value: string; } +// Warning: (ae-forgotten-export) The symbol "KbnError" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PainlessError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class PainlessError extends KbnError { + // Warning: (ae-forgotten-export) The symbol "EsError" needs to be exported by the entry point index.d.ts + constructor(err: EsError, request: IKibanaSearchRequest); + // (undocumented) + getErrorMessage(application: ApplicationStart): JSX.Element; + // (undocumented) + painlessStack?: string; +} + // Warning: (ae-forgotten-export) The symbol "parseEsInterval" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ParsedInterval" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1676,11 +1737,54 @@ export interface QueryStateChange extends QueryStateChangePartial { globalFilters?: boolean; } -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const QueryStringInput: React.FC>; +export const QueryStringInput: React.FC; + +// Warning: (ae-missing-release-tag) "QueryStringInputProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface QueryStringInputProps { + // (undocumented) + bubbleSubmitEvent?: boolean; + // (undocumented) + className?: string; + // (undocumented) + dataTestSubj?: string; + // (undocumented) + disableAutoFocus?: boolean; + // (undocumented) + indexPatterns: Array; + // (undocumented) + isInvalid?: boolean; + // (undocumented) + languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; + // (undocumented) + onBlur?: () => void; + // (undocumented) + onChange?: (query: Query) => void; + // (undocumented) + onChangeQueryInputFocus?: (isFocused: boolean) => void; + // (undocumented) + onSubmit?: (query: Query) => void; + // Warning: (ae-forgotten-export) The symbol "PersistedLog" needs to be exported by the entry point index.d.ts + // + // (undocumented) + persistedLog?: PersistedLog; + // (undocumented) + placeholder?: string; + // (undocumented) + prepend?: any; + // (undocumented) + query: Query; + // (undocumented) + screenTitle?: string; + // Warning: (ae-forgotten-export) The symbol "SuggestionsListSize" needs to be exported by the entry point index.d.ts + // + // (undocumented) + size?: SuggestionsListSize; +} // @public (undocumented) export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField; @@ -1803,13 +1907,6 @@ export interface RefreshInterval { value: number; } -// Warning: (ae-missing-release-tag) "RequestTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public -export class RequestTimeoutError extends Error { - constructor(message?: string); -} - // Warning: (ae-missing-release-tag) "SavedQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1933,24 +2030,27 @@ export class SearchInterceptor { protected application: CoreStart['application']; // (undocumented) protected readonly deps: SearchInterceptorDeps; + // (undocumented) + protected getTimeoutMode(): TimeoutErrorMode; + // (undocumented) + protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) - protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable; - search(request: IEsSearchRequest, options?: ISearchOptions): Observable; + protected runSearch(request: IKibanaSearchRequest, signal: AbortSignal, strategy?: string): Observable; + search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; // @internal (undocumented) protected setupAbortSignal({ abortSignal, timeout, }: { abortSignal?: AbortSignal; timeout?: number; }): { combinedSignal: AbortSignal; + timeoutSignal: AbortSignal; cleanup: () => void; }; // (undocumented) - protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; - // @internal - protected timeoutSubscriptions: Subscription; -} + showError(e: Error): void; + } // Warning: (ae-missing-release-tag) "SearchInterceptorDeps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2063,6 +2163,17 @@ export interface SearchSourceFields { version?: boolean; } +// Warning: (ae-missing-release-tag) "SearchTimeoutError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class SearchTimeoutError extends KbnError { + constructor(err: Error, mode: TimeoutErrorMode); + // (undocumented) + getErrorMessage(application: ApplicationStart): JSX.Element; + // (undocumented) + mode: TimeoutErrorMode; + } + // Warning: (ae-missing-release-tag) "SortDirection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2135,6 +2246,18 @@ export class TimeHistory { // @public (undocumented) export type TimeHistoryContract = PublicMethodsOf; +// Warning: (ae-missing-release-tag) "TimeoutErrorMode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum TimeoutErrorMode { + // (undocumented) + CHANGE = 2, + // (undocumented) + CONTACT = 1, + // (undocumented) + UPGRADE = 0 +} + // Warning: (ae-missing-release-tag) "TimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2189,6 +2312,7 @@ export const UI_SETTINGS: { // src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:70:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:98:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/public/field_formats/field_formats_service.ts:67:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts @@ -2217,27 +2341,27 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "UrlFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "StringFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:178:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/search/errors/index.ts b/src/plugins/data/public/search/errors/index.ts new file mode 100644 index 0000000000000..6082e758a8bad --- /dev/null +++ b/src/plugins/data/public/search/errors/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 './painless_error'; +export * from './timeout_error'; diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/src/plugins/data/public/search/errors/painless_error.tsx new file mode 100644 index 0000000000000..244f205469a2f --- /dev/null +++ b/src/plugins/data/public/search/errors/painless_error.tsx @@ -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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiSpacer, EuiText, EuiCodeBlock } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ApplicationStart } from 'kibana/public'; +import { KbnError } from '../../../../kibana_utils/common'; +import { EsError, isEsError } from './types'; +import { IKibanaSearchRequest } from '..'; + +export class PainlessError extends KbnError { + painlessStack?: string; + constructor(err: EsError, request: IKibanaSearchRequest) { + const rootCause = getRootCause(err as EsError); + + super( + i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', { + defaultMessage: "Error executing Painless script: '{script}'.", + values: { script: rootCause?.script }, + }) + ); + this.painlessStack = rootCause?.script_stack ? rootCause?.script_stack.join('\n') : undefined; + } + + public getErrorMessage(application: ApplicationStart) { + function onClick() { + application.navigateToApp('management', { + path: `/kibana/indexPatterns`, + }); + } + + return ( + <> + {this.message} + + + {this.painlessStack ? ( + + {this.painlessStack} + + ) : null} + + + + + + + ); + } +} + +function getFailedShards(err: EsError) { + const failedShards = + err.body?.attributes?.error?.failed_shards || + err.body?.attributes?.error?.caused_by?.failed_shards; + return failedShards ? failedShards[0] : undefined; +} + +function getRootCause(err: EsError) { + return getFailedShards(err)?.reason; +} + +export function isPainlessError(err: Error | EsError) { + if (!isEsError(err)) return false; + + const rootCause = getRootCause(err as EsError); + if (!rootCause) return false; + + const { lang } = rootCause; + return lang === 'painless'; +} diff --git a/src/plugins/data/public/search/errors/timeout_error.test.tsx b/src/plugins/data/public/search/errors/timeout_error.test.tsx new file mode 100644 index 0000000000000..87b491b976ebc --- /dev/null +++ b/src/plugins/data/public/search/errors/timeout_error.test.tsx @@ -0,0 +1,62 @@ +/* + * 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 { SearchTimeoutError, TimeoutErrorMode } from './timeout_error'; + +import { coreMock } from '../../../../../core/public/mocks'; +const startMock = coreMock.createStart(); + +import { mount } from 'enzyme'; +import { AbortError } from 'src/plugins/data/common'; + +describe('SearchTimeoutError', () => { + beforeEach(() => { + jest.clearAllMocks(); + startMock.application.navigateToApp.mockImplementation(jest.fn()); + }); + + it('Should navigate to upgrade', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.UPGRADE); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(1); + component.find('EuiButton').simulate('click'); + expect(startMock.application.navigateToApp).toHaveBeenCalledWith('management', { + path: '/kibana/indexPatterns', + }); + }); + + it('Should create contact admin message', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.CONTACT); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(0); + }); + + it('Should navigate to settings', () => { + const e = new SearchTimeoutError(new AbortError(), TimeoutErrorMode.CHANGE); + const component = mount(e.getErrorMessage(startMock.application)); + + expect(component.find('EuiButton').length).toBe(1); + component.find('EuiButton').simulate('click'); + expect(startMock.application.navigateToApp).toHaveBeenCalledWith('management', { + path: '/kibana/settings', + }); + }); +}); diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx new file mode 100644 index 0000000000000..56aecb42f5326 --- /dev/null +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -0,0 +1,111 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; +import { ApplicationStart } from 'kibana/public'; +import { KbnError } from '../../../../kibana_utils/common'; + +export enum TimeoutErrorMode { + UPGRADE, + CONTACT, + CHANGE, +} + +/** + * Request Failure - When an entire multi request fails + * @param {Error} err - the Error that came back + */ +export class SearchTimeoutError extends KbnError { + public mode: TimeoutErrorMode; + constructor(err: Error, mode: TimeoutErrorMode) { + super(`Request timeout: ${JSON.stringify(err?.message)}`); + this.mode = mode; + } + + private getMessage() { + switch (this.mode) { + case TimeoutErrorMode.UPGRADE: + return i18n.translate('data.search.upgradeLicense', { + defaultMessage: + 'Your query has timed out. With our free Basic tier, your queries never time out.', + }); + case TimeoutErrorMode.CONTACT: + return i18n.translate('data.search.timeoutContactAdmin', { + defaultMessage: + 'Your query has timed out. Contact your system administrator to increase the run time.', + }); + case TimeoutErrorMode.CHANGE: + return i18n.translate('data.search.timeoutIncreaseSetting', { + defaultMessage: + 'Your query has timed out. Increase run time with the search timeout advanced setting.', + }); + } + } + + private getActionText() { + switch (this.mode) { + case TimeoutErrorMode.UPGRADE: + return i18n.translate('data.search.upgradeLicenseActionText', { + defaultMessage: 'Upgrade now', + }); + break; + case TimeoutErrorMode.CHANGE: + return i18n.translate('data.search.timeoutIncreaseSettingActionText', { + defaultMessage: 'Edit setting', + }); + break; + } + } + + private onClick(application: ApplicationStart) { + switch (this.mode) { + case TimeoutErrorMode.UPGRADE: + application.navigateToApp('management', { + path: `/kibana/indexPatterns`, + }); + break; + case TimeoutErrorMode.CHANGE: + application.navigateToApp('management', { + path: `/kibana/settings`, + }); + break; + } + } + + public getErrorMessage(application: ApplicationStart) { + const actionText = this.getActionText(); + return ( + <> + {this.getMessage()} + {actionText && ( + <> + + + this.onClick(application)} size="s"> + {actionText} + + + + )} + + ); + } +} diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts new file mode 100644 index 0000000000000..4182209eb68a5 --- /dev/null +++ b/src/plugins/data/public/search/errors/types.ts @@ -0,0 +1,73 @@ +/* + * 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. + */ + +interface FailedShard { + shard: number; + index: string; + node: string; + reason: { + type: string; + reason: string; + script_stack: string[]; + script: string; + lang: string; + position: { + offset: number; + start: number; + end: number; + }; + caused_by: { + type: string; + reason: string; + }; + }; +} + +export interface EsError { + body: { + statusCode: number; + error: string; + message: string; + attributes?: { + error?: { + root_cause?: [ + { + lang: string; + script: string; + } + ]; + type: string; + reason: string; + failed_shards: FailedShard[]; + caused_by: { + type: string; + reason: string; + phase: string; + grouped: boolean; + failed_shards: FailedShard[]; + script_stack: string[]; + }; + }; + }; + }; +} + +export function isEsError(e: any): e is EsError { + return !!e.body?.attributes; +} diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index fc3d71936a859..86804a819cb0e 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -46,4 +46,4 @@ export { export { getEsPreference } from './es_search'; export { SearchInterceptor, SearchInterceptorDeps } from './search_interceptor'; -export { RequestTimeoutError } from './request_timeout_error'; +export * from './errors'; diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index fdd6a90013413..8bad4cd269b3f 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -18,10 +18,8 @@ */ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; +import { searchSourceMock } from './search_source/mocks'; import { ISearchSetup, ISearchStart } from './types'; -import { searchSourceMock, createSearchSourceMock } from '../../common/search/search_source/mocks'; - -export * from '../../common/search/search_source/mocks'; function createSetupContract(): jest.Mocked { return { @@ -34,7 +32,8 @@ function createStartContract(): jest.Mocked { return { aggs: searchAggsStartMock(), search: jest.fn(), - searchSource: searchSourceMock, + showError: jest.fn(), + searchSource: searchSourceMock.createStartContract(), }; } @@ -42,5 +41,3 @@ export const searchServiceMock = { createSetupContract, createStartContract, }; - -export { createSearchSourceMock }; diff --git a/src/plugins/data/public/search/request_timeout_error.ts b/src/plugins/data/public/search/request_timeout_error.ts deleted file mode 100644 index 92894deb4f0ff..0000000000000 --- a/src/plugins/data/public/search/request_timeout_error.ts +++ /dev/null @@ -1,30 +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. - */ - -/** - * Class used to signify that a request timed out. Useful for applications to conditionally handle - * this type of error differently than other errors. - */ -export class RequestTimeoutError extends Error { - constructor(message = 'Request timed out') { - super(message); - this.message = message; - this.name = 'RequestTimeoutError'; - } -} diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 7bfa6f0ab1bc5..ade15adc1c3a3 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -22,6 +22,7 @@ import { coreMock } from '../../../../core/public/mocks'; import { IEsSearchRequest } from '../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../common'; +import { SearchTimeoutError, PainlessError } from './errors'; let searchInterceptor: SearchInterceptor; let mockCoreSetup: MockedKeys; @@ -53,8 +54,8 @@ describe('SearchInterceptor', () => { expect(result).toBe(mockResponse); }); - test('Observable should fail if fetch has an error', async () => { - const mockResponse: any = { result: 500 }; + test('Observable should fail if fetch has an internal error', async () => { + const mockResponse: any = { result: 500, message: 'Internal Error' }; mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, @@ -68,64 +69,83 @@ describe('SearchInterceptor', () => { } }); - test('Observable should fail if fetch times out (test merged signal)', async () => { - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); - }); - - setTimeout(resolve, 5000); - }); - }); + test('Should throw SearchTimeoutError on server timeout AND show toast', async (done) => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, }; const response = searchInterceptor.search(mockRequest); - const next = jest.fn(); - const error = (e: any) => { - expect(next).not.toBeCalled(); - expect(e).toBeInstanceOf(AbortError); - }; - response.subscribe({ next, error }); - - jest.advanceTimersByTime(5000); - - await flushPromises(); + try { + await response.toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + done(); + } }); - test('Should not timeout if requestTimeout is undefined', async () => { - searchInterceptor = new SearchInterceptor({ - startServices: mockCoreSetup.getStartServices(), - uiSettings: mockCoreSetup.uiSettings, - http: mockCoreSetup.http, - toasts: mockCoreSetup.notifications.toasts, - }); - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); - }); - - setTimeout(resolve, 5000); - }); - }); + test('Search error should be debounced', async (done) => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); const mockRequest: IEsSearchRequest = { params: {}, }; - const response = searchInterceptor.search(mockRequest); + try { + await searchInterceptor.search(mockRequest).toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(SearchTimeoutError); + try { + await searchInterceptor.search(mockRequest).toPromise(); + } catch (e2) { + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + done(); + } + } + }); - expect.assertions(1); - const next = jest.fn(); - const complete = () => { - expect(next).toBeCalled(); + test('Should throw Painless error on server error with OSS format', async (done) => { + const mockResponse: any = { + result: 500, + body: { + attributes: { + error: { + failed_shards: [ + { + reason: { + lang: 'painless', + script_stack: ['a', 'b'], + reason: 'banana', + }, + }, + ], + }, + }, + }, }; - response.subscribe({ next, complete }); - - jest.advanceTimersByTime(5000); + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); - await flushPromises(); + try { + await response.toPromise(); + } catch (e) { + expect(e).toBeInstanceOf(PainlessError); + done(); + } }); test('Observable should fail if user aborts (test merged signal)', async () => { diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 888e12a4285b1..2e42635a7f811 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,29 +17,21 @@ * under the License. */ -import { trimEnd, debounce } from 'lodash'; -import { - BehaviorSubject, - throwError, - timer, - Subscription, - defer, - from, - Observable, - NEVER, -} from 'rxjs'; +import { get, trimEnd, debounce } from 'lodash'; +import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; -import { i18n } from '@kbn/i18n'; import { getCombinedSignal, AbortError, - IEsSearchRequest, - IEsSearchResponse, + IKibanaSearchRequest, + IKibanaSearchResponse, ISearchOptions, ES_SEARCH_STRATEGY, } from '../../common'; import { SearchUsageCollector } from './collectors'; +import { SearchTimeoutError, PainlessError, isPainlessError, TimeoutErrorMode } from './errors'; +import { toMountPoint } from '../../../kibana_react/public'; export interface SearchInterceptorDeps { http: CoreSetup['http']; @@ -62,12 +54,6 @@ export class SearchInterceptor { */ protected pendingCount$ = new BehaviorSubject(0); - /** - * The subscriptions from scheduling the automatic timeout for each request. - * @internal - */ - protected timeoutSubscriptions: Subscription = new Subscription(); - /** * @internal */ @@ -84,14 +70,49 @@ export class SearchInterceptor { }); } + /* + * @returns `TimeoutErrorMode` indicating what action should be taken in case of a request timeout based on license and permissions. + * @internal + */ + protected getTimeoutMode() { + return TimeoutErrorMode.UPGRADE; + } + + /* + * @returns `Error` a search service specific error or the original error, if a specific error can't be recognized. + * @internal + */ + protected handleSearchError( + e: any, + request: IKibanaSearchRequest, + timeoutSignal: AbortSignal, + appAbortSignal?: AbortSignal + ): Error { + if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { + // Handle a client or a server side timeout + const err = new SearchTimeoutError(e, this.getTimeoutMode()); + + // Show the timeout error here, so that it's shown regardless of how an application chooses to handle errors. + this.showTimeoutError(err); + return err; + } else if (appAbortSignal?.aborted) { + // In the case an application initiated abort, throw the existing AbortError. + return e; + } else if (isPainlessError(e)) { + return new PainlessError(e, request); + } else { + return e; + } + } + /** * @internal */ protected runSearch( - request: IEsSearchRequest, + request: IKibanaSearchRequest, signal: AbortSignal, strategy?: string - ): Observable { + ): Observable { const { id, ...searchRequest } = request; const path = trimEnd(`/internal/search/${strategy || ES_SEARCH_STRATEGY}/${id || ''}`, '/'); const body = JSON.stringify(searchRequest); @@ -105,41 +126,6 @@ export class SearchInterceptor { ); } - /** - * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort - * either when `cancelPending` is called, when the request times out, or when the original - * `AbortSignal` is aborted. Updates `pendingCount$` when the request is started/finalized. - */ - public search( - request: IEsSearchRequest, - options?: ISearchOptions - ): Observable { - // Defer the following logic until `subscribe` is actually called - return defer(() => { - if (options?.abortSignal?.aborted) { - return throwError(new AbortError()); - } - - const { combinedSignal, cleanup } = this.setupAbortSignal({ - abortSignal: options?.abortSignal, - }); - this.pendingCount$.next(this.pendingCount$.getValue() + 1); - - return this.runSearch(request, combinedSignal, options?.strategy).pipe( - catchError((e: any) => { - if (e.body?.attributes?.error === 'Request timed out') { - this.showTimeoutError(e); - } - return throwError(e); - }), - finalize(() => { - this.pendingCount$.next(this.pendingCount$.getValue() - 1); - cleanup(); - }) - ); - }); - } - /** * @internal */ @@ -156,9 +142,7 @@ export class SearchInterceptor { const timeout$ = timeout ? timer(timeout) : NEVER; const subscription = timeout$.subscribe(() => { timeoutController.abort(); - this.showTimeoutError(new AbortError()); }); - this.timeoutSubscriptions.add(subscription); // Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs: // 1. The user manually aborts (via `cancelPending`) @@ -172,34 +156,95 @@ export class SearchInterceptor { const combinedSignal = getCombinedSignal(signals); const cleanup = () => { - this.timeoutSubscriptions.remove(subscription); + subscription.unsubscribe(); }; combinedSignal.addEventListener('abort', cleanup); return { combinedSignal, + timeoutSignal, cleanup, }; } - // Right now we are debouncing but we will hook this up with background sessions to show only one - // error notification per session. - protected showTimeoutError = debounce( - (e: Error) => { - this.deps.toasts.addError(e, { + /** + * Right now we are throttling but we will hook this up with background sessions to show only one + * error notification per session. + * @internal + */ + private showTimeoutError = debounce( + (e: SearchTimeoutError) => { + this.deps.toasts.addDanger({ title: 'Timed out', - toastMessage: i18n.translate('data.search.upgradeLicense', { - defaultMessage: - 'One or more queries timed out. With our free Basic tier, your queries never time out.', - }), + text: toMountPoint(e.getErrorMessage(this.application)), }); }, - 60000, - { - leading: true, - } + 30000, + { leading: true, trailing: false } ); + + /** + * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort + * either when `cancelPending` is called, when the request times out, or when the original + * `AbortSignal` is aborted. Updates `pendingCount$` when the request is started/finalized. + * + * @param request + * @options + * @returns `Observalbe` emitting the search response or an error. + */ + public search( + request: IKibanaSearchRequest, + options?: ISearchOptions + ): Observable { + // Defer the following logic until `subscribe` is actually called + return defer(() => { + if (options?.abortSignal?.aborted) { + return throwError(new AbortError()); + } + + const { timeoutSignal, combinedSignal, cleanup } = this.setupAbortSignal({ + abortSignal: options?.abortSignal, + }); + this.pendingCount$.next(this.pendingCount$.getValue() + 1); + + return this.runSearch(request, combinedSignal, options?.strategy).pipe( + catchError((e: any) => { + return throwError( + this.handleSearchError(e, request, timeoutSignal, options?.abortSignal) + ); + }), + finalize(() => { + this.pendingCount$.next(this.pendingCount$.getValue() - 1); + cleanup(); + }) + ); + }); + } + + /* + * + */ + public showError(e: Error) { + if (e instanceof AbortError) return; + + if (e instanceof SearchTimeoutError) { + // The SearchTimeoutError is shown by the interceptor in getSearchError (regardless of how the app chooses to handle errors) + return; + } + + if (e instanceof PainlessError) { + this.deps.toasts.addDanger({ + title: 'Search Error', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + return; + } + + this.deps.toasts.addError(e, { + title: 'Search Error', + }); + } } export type ISearchInterceptor = PublicMethodsOf; diff --git a/src/plugins/data/public/search/search_service.test.ts b/src/plugins/data/public/search/search_service.test.ts index 738f1e8ffbca0..b59fa6fa16bf6 100644 --- a/src/plugins/data/public/search/search_service.test.ts +++ b/src/plugins/data/public/search/search_service.test.ts @@ -26,11 +26,15 @@ describe('Search service', () => { let searchService: SearchService; let mockCoreSetup: MockedKeys; let mockCoreStart: MockedKeys; + const initializerContext = coreMock.createPluginInitializerContext(); + initializerContext.config.get = jest.fn().mockReturnValue({ + search: { aggs: { shardDelay: { enabled: false } } }, + }); beforeEach(() => { - searchService = new SearchService(); mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); + searchService = new SearchService(initializerContext); }); describe('setup()', () => { @@ -48,6 +52,7 @@ describe('Search service', () => { describe('start()', () => { it('exposes proper contract', async () => { const start = searchService.start(mockCoreStart, { + fieldFormats: {}, indexPatterns: {}, } as any); expect(start).toHaveProperty('aggs'); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index d8937ed30e401..5014418d6b70b 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -17,15 +17,16 @@ * under the License. */ -import { Plugin, CoreSetup, CoreStart } from 'src/core/public'; +import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from 'src/core/public'; import { BehaviorSubject } from 'rxjs'; import { ISearchSetup, ISearchStart, SearchEnhancements } from './types'; import { handleResponse } from './fetch'; import { - createSearchSource, + IEsSearchRequest, ISearchGeneric, - SearchSource, + ISearchOptions, + SearchSourceService, SearchSourceDependencies, } from '../../common/search'; import { getCallMsearch } from './legacy'; @@ -36,6 +37,12 @@ import { SearchUsageCollector, createUsageCollector } from './collectors'; import { UsageCollectionSetup } from '../../../usage_collection/public'; import { esdsl, esRawResponse } from './expressions'; import { ExpressionsSetup } from '../../../expressions/public'; +import { ConfigSchema } from '../../config'; +import { + SHARD_DELAY_AGG_NAME, + getShardDelayBucketAgg, +} from '../../common/search/aggs/buckets/shard_delay'; +import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; /** @internal */ export interface SearchServiceSetupDependencies { @@ -51,9 +58,12 @@ export interface SearchServiceStartDependencies { export class SearchService implements Plugin { private readonly aggsService = new AggsService(); + private readonly searchSourceService = new SearchSourceService(); private searchInterceptor!: ISearchInterceptor; private usageCollector?: SearchUsageCollector; + constructor(private initializerContext: PluginInitializerContext) {} + public setup( { http, getStartServices, notifications, uiSettings }: CoreSetup, { expressions, usageCollection }: SearchServiceSetupDependencies @@ -75,11 +85,18 @@ export class SearchService implements Plugin { expressions.registerFunction(esdsl); expressions.registerType(esRawResponse); + const aggs = this.aggsService.setup({ + registerFunction: expressions.registerFunction, + uiSettings, + }); + + if (this.initializerContext.config.get().search.aggs.shardDelay.enabled) { + aggs.types.registerBucket(SHARD_DELAY_AGG_NAME, getShardDelayBucketAgg); + expressions.registerFunction(aggShardDelay); + } + return { - aggs: this.aggsService.setup({ - registerFunction: expressions.registerFunction, - uiSettings, - }), + aggs, usageCollector: this.usageCollector!, __enhance: (enhancements: SearchEnhancements) => { this.searchInterceptor = enhancements.searchInterceptor; @@ -100,7 +117,9 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: uiSettings.get.bind(uiSettings), - search, + search: (request: IEsSearchRequest, options: ISearchOptions) => { + return search(request, options).toPromise(); + }, onResponse: handleResponse, legacy: { callMsearch: getCallMsearch({ http }), @@ -111,22 +130,15 @@ export class SearchService implements Plugin { return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), search, - searchSource: { - /** - * creates searchsource based on serialized search source fields - */ - create: createSearchSource(indexPatterns, searchSourceDependencies), - /** - * creates an enpty search source - */ - createEmpty: () => { - return new SearchSource({}, searchSourceDependencies); - }, + showError: (e: Error) => { + this.searchInterceptor.showError(e); }, + searchSource: this.searchSourceService.start(indexPatterns, searchSourceDependencies), }; } public stop() { this.aggsService.stop(); + this.searchSourceService.stop(); } } diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts new file mode 100644 index 0000000000000..bc382f09947c5 --- /dev/null +++ b/src/plugins/data/public/search/search_source/mocks.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { searchSourceCommonMock } from '../../../common/search/search_source/mocks'; +import { ISearchStart } from '../types'; + +function createStartContract(): jest.Mocked { + return searchSourceCommonMock; +} + +export const searchSourceMock = { + createStartContract, +}; diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 6ae5d83499aa6..85ef7aa4d97cb 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -21,10 +21,12 @@ import { PackageInfo } from 'kibana/server'; import { ISearchInterceptor } from './search_interceptor'; import { SearchUsageCollector } from './collectors'; import { AggsSetup, AggsSetupDependencies, AggsStartDependencies, AggsStart } from './aggs'; -import { ISearchGeneric, ISearchSource, SearchSourceFields } from '../../common/search'; +import { ISearchGeneric, ISearchStartSearchSource } from '../../common/search'; import { IndexPatternsContract } from '../../common/index_patterns/index_patterns'; import { UsageCollectionSetup } from '../../../usage_collection/public'; +export { ISearchStartSearchSource }; + export interface SearchEnhancements { searchInterceptor: ISearchInterceptor; } @@ -42,21 +44,6 @@ export interface ISearchSetup { __enhance: (enhancements: SearchEnhancements) => void; } -/** - * high level search service - * @public - */ -export interface ISearchStartSearchSource { - /** - * creates {@link SearchSource} based on provided serialized {@link SearchSourceFields} - * @param fields - */ - create: (fields?: SearchSourceFields) => Promise; - /** - * creates empty {@link SearchSource} - */ - createEmpty: () => ISearchSource; -} /** * search service * @public @@ -73,6 +60,8 @@ export interface ISearchStart { * {@link ISearchGeneric} */ search: ISearchGeneric; + + showError: (e: Error) => void; /** * high level search * {@link ISearchStartSearchSource} diff --git a/src/plugins/data/public/test_utils.ts b/src/plugins/data/public/test_utils.ts index f04b20e96fa1d..03dc572d0bc80 100644 --- a/src/plugins/data/public/test_utils.ts +++ b/src/plugins/data/public/test_utils.ts @@ -18,3 +18,4 @@ */ export { getFieldFormatsRegistry } from './field_formats/field_formats_registry.stub'; +export { getStubIndexPattern, StubIndexPattern } from './index_patterns/index_pattern.stub'; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx index 0e2bcc7581950..7bd4facc9caa9 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx @@ -106,7 +106,11 @@ class FilterEditorUI extends Component { - + {this.state.isCustomEditorOpen ? ( { - geo.coordinates in US - -`; - -exports[`negated alias 1`] = ` - - - NOT - - geo.coordinates in US - -`; diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.js b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.js deleted file mode 100644 index 55df544ad010b..0000000000000 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.js +++ /dev/null @@ -1,48 +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 React from 'react'; -import { FilterLabel } from './filter_label'; -import { shallow } from 'enzyme'; -import { phraseFilter } from '../../../../stubs'; - -test('alias', () => { - const filter = { - ...phraseFilter, - meta: { - ...phraseFilter.meta, - alias: 'geo.coordinates in US', - }, - }; - const component = shallow(); - expect(component).toMatchSnapshot(); -}); - -test('negated alias', () => { - const filter = { - ...phraseFilter, - meta: { - ...phraseFilter.meta, - alias: 'geo.coordinates in US', - negate: true, - }, - }; - const component = shallow(); - expect(component).toMatchSnapshot(); -}); diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.tsx new file mode 100644 index 0000000000000..59afc1606adf9 --- /dev/null +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.test.tsx @@ -0,0 +1,156 @@ +/* + * 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 { FilterLabel } from './filter_label'; +import { render, cleanup } from '@testing-library/react/pure'; +import { phraseFilter } from '../../../../stubs'; + +afterEach(cleanup); + +test('alias', () => { + const filter = { + ...phraseFilter, + meta: { + ...phraseFilter.meta, + alias: 'geo.coordinates in US', + }, + }; + const { container } = render(); + expect(container).toMatchInlineSnapshot(` +

+ + geo.coordinates in US +
+ `); +}); + +test('negated alias', () => { + const filter = { + ...phraseFilter, + meta: { + ...phraseFilter.meta, + alias: 'geo.coordinates in US', + negate: true, + }, + }; + const { container } = render(); + expect(container).toMatchInlineSnapshot(` +
+ + NOT + + geo.coordinates in US +
+ `); +}); + +test('alias with warning status', () => { + const filter = { + ...phraseFilter, + meta: { + ...phraseFilter.meta, + alias: 'geo.coordinates in US', + negate: true, + }, + }; + const { container } = render( + + ); + expect(container).toMatchInlineSnapshot(` +
+ + NOT + + geo.coordinates in US + : + + Warning + +
+ `); +}); + +test('alias with error status', () => { + const filter = { + ...phraseFilter, + meta: { + ...phraseFilter.meta, + alias: 'geo.coordinates in US', + negate: true, + }, + }; + const { container } = render( + + ); + expect(container).toMatchInlineSnapshot(` +
+ + NOT + + geo.coordinates in US + : + + Error + +
+ `); +}); + +test('warning', () => { + const { container } = render(); + expect(container).toMatchInlineSnapshot(` +
+ + machine.os + : + + Warning + +
+ `); +}); + +test('error', () => { + const { container } = render(); + expect(container).toMatchInlineSnapshot(` +
+ + machine.os + : + + Error + +
+ `); +}); diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx index 070631354d8b8..3b85d0753b8c5 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx @@ -22,13 +22,15 @@ import { EuiTextColor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { existsOperator, isOneOfOperator } from './filter_operators'; import { Filter, FILTERS } from '../../../../../common'; +import type { FilterLabelStatus } from '../../filter_item'; interface Props { filter: Filter; valueLabel?: string; + filterLabelStatus?: FilterLabelStatus; } -export function FilterLabel({ filter, valueLabel }: Props) { +export function FilterLabel({ filter, valueLabel, filterLabelStatus }: Props) { const prefixText = filter.meta.negate ? ` ${i18n.translate('data.filter.filterBar.negatedFilterPrefix', { defaultMessage: 'NOT ', @@ -50,6 +52,7 @@ export function FilterLabel({ filter, valueLabel }: Props) { {prefix} {filter.meta.alias} + {filterLabelStatus && <>: {getValue(valueLabel)}} ); } diff --git a/src/plugins/data/public/ui/filter_bar/filter_item.tsx b/src/plugins/data/public/ui/filter_bar/filter_item.tsx index 078fc8c9e1a8f..cbff20115f8ea 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_item.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_item.tsx @@ -49,7 +49,7 @@ interface Props { interface LabelOptions { title: string; - status: string; + status: FilterLabelStatus; message?: string; } @@ -57,6 +57,11 @@ const FILTER_ITEM_OK = ''; const FILTER_ITEM_WARNING = 'warn'; const FILTER_ITEM_ERROR = 'error'; +export type FilterLabelStatus = + | typeof FILTER_ITEM_OK + | typeof FILTER_ITEM_WARNING + | typeof FILTER_ITEM_ERROR; + export function FilterItem(props: Props) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [indexPatternExists, setIndexPatternExists] = useState(undefined); @@ -260,7 +265,7 @@ export function FilterItem(props: Props) { } function getValueLabel(): LabelOptions { - const label = { + const label: LabelOptions = { title: '', message: '', status: FILTER_ITEM_OK, @@ -326,6 +331,7 @@ export function FilterItem(props: Props) { props.onRemove()} diff --git a/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx index f9328875cc910..3f9cbce06b315 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_view/index.tsx @@ -22,10 +22,12 @@ import { i18n } from '@kbn/i18n'; import React, { FC } from 'react'; import { FilterLabel } from '../filter_editor/lib/filter_label'; import { Filter, isFilterPinned } from '../../../../common'; +import type { FilterLabelStatus } from '../filter_item'; interface Props { filter: Filter; valueLabel: string; + filterLabelStatus: FilterLabelStatus; errorMessage?: string; [propName: string]: any; } @@ -36,6 +38,7 @@ export const FilterView: FC = ({ onClick, valueLabel, errorMessage, + filterLabelStatus, ...rest }: Props) => { const [ref, innerText] = useInnerText(); @@ -65,7 +68,7 @@ export const FilterView: FC = ({ iconType="cross" iconSide="right" closeButtonProps={{ - // Removing tab focus on close button because the same option can be optained through the context menu + // Removing tab focus on close button because the same option can be obtained through the context menu // Also, we may want to add a `DEL` keyboard press functionality tabIndex: -1, }} @@ -80,7 +83,11 @@ export const FilterView: FC = ({ {...rest} > - + ); diff --git a/src/plugins/data/public/ui/index.ts b/src/plugins/data/public/ui/index.ts index 35b1bc50ddb1e..299b9d2681578 100644 --- a/src/plugins/data/public/ui/index.ts +++ b/src/plugins/data/public/ui/index.ts @@ -20,7 +20,7 @@ export { SuggestionsComponent } from './typeahead'; export { IndexPatternSelect } from './index_pattern_select'; export { FilterBar } from './filter_bar'; -export { QueryStringInput } from './query_string_input/query_string_input'; +export { QueryStringInput, QueryStringInputProps } from './query_string_input/query_string_input'; export { SearchBar, SearchBarProps, StatefulSearchBarProps } from './search_bar'; // @internal diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index 8e1151b387fee..ec45b80927f6d 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -46,8 +46,7 @@ import { PersistedLog, getQueryLog, matchPairs, toUser, fromUser } from '../../q import { SuggestionsListSize } from '../typeahead/suggestions_component'; import { SuggestionsComponent } from '..'; -interface Props { - kibana: KibanaReactContextValue; +export interface QueryStringInputProps { indexPatterns: Array; query: Query; disableAutoFocus?: boolean; @@ -67,6 +66,10 @@ interface Props { isInvalid?: boolean; } +interface Props extends QueryStringInputProps { + kibana: KibanaReactContextValue; +} + interface State { isSuggestionsVisible: boolean; index: number | null; @@ -442,7 +445,7 @@ export class QueryStringInputUI extends Component { // Send telemetry info every time the user opts in or out of kuery // As a result it is important this function only ever gets called in the // UI component's change handler. - this.services.http.post('/api/kibana/kql_opt_in_telemetry', { + this.services.http.post('/api/kibana/kql_opt_in_stats', { body: JSON.stringify({ opt_in: language === 'kuery' }), }); @@ -610,6 +613,7 @@ export class QueryStringInputUI extends Component { })} aria-haspopup="true" aria-expanded={this.state.isSuggestionsVisible} + data-skip-axe="aria-required-children" >
{ } } -export const QueryStringInput = withKibana(QueryStringInputUI); +export const QueryStringInput: React.FC = withKibana(QueryStringInputUI); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 03baff4910309..aac1fe1fde212 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -133,16 +133,17 @@ export { IndexPatternsFetcher, FieldDescriptor as IndexPatternFieldDescriptor, shouldReadFieldFromDocValues, // used only in logstash_fields fixture + FieldDescriptor, } from './index_patterns'; export { - IIndexPattern, IFieldType, IFieldSubType, ES_FIELD_TYPES, KBN_FIELD_TYPES, IndexPatternAttributes, UI_SETTINGS, + IndexPattern, } from '../common'; /** @@ -286,6 +287,9 @@ export { export const config: PluginConfigDescriptor = { exposeToBrowser: { autocomplete: true, + search: true, }, schema: configSchema, }; + +export type { IndexPatternsService } from './index_patterns'; diff --git a/src/plugins/data/server/index_patterns/utils.ts b/src/plugins/data/server/index_patterns/utils.ts index e841097fe49c2..1e7a85599612c 100644 --- a/src/plugins/data/server/index_patterns/utils.ts +++ b/src/plugins/data/server/index_patterns/utils.ts @@ -18,11 +18,11 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { IIndexPattern, IFieldType } from '../../common'; +import { IFieldType, IndexPatternAttributes, SavedObject } from '../../common'; export const getFieldByName = ( fieldName: string, - indexPattern: IIndexPattern + indexPattern: SavedObject ): IFieldType | undefined => { const fields: IFieldType[] = indexPattern && JSON.parse(indexPattern.attributes.fields); const field = fields && fields.find((f) => f.name === fieldName); @@ -33,8 +33,8 @@ export const getFieldByName = ( export const findIndexPatternById = async ( savedObjectsClient: SavedObjectsClientContract, index: string -): Promise => { - const savedObjectsResponse = await savedObjectsClient.find({ +): Promise | undefined> => { + const savedObjectsResponse = await savedObjectsClient.find({ type: 'index-pattern', fields: ['fields'], search: `"${index}"`, @@ -42,6 +42,6 @@ export const findIndexPatternById = async ( }); if (savedObjectsResponse.total > 0) { - return (savedObjectsResponse.saved_objects[0] as unknown) as IIndexPattern; + return savedObjectsResponse.saved_objects[0]; } }; diff --git a/src/plugins/data/server/kql_telemetry/route.ts b/src/plugins/data/server/kql_telemetry/route.ts index dd7ff333e6257..efcb3d038bcc6 100644 --- a/src/plugins/data/server/kql_telemetry/route.ts +++ b/src/plugins/data/server/kql_telemetry/route.ts @@ -27,7 +27,7 @@ export function registerKqlTelemetryRoute( ) { router.post( { - path: '/api/kibana/kql_opt_in_telemetry', + path: '/api/kibana/kql_opt_in_stats', validate: { body: schema.object({ opt_in: schema.boolean(), diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 588885391262e..88f24b7ca5a70 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -111,13 +111,15 @@ export class DataServerPlugin public start(core: CoreStart) { const fieldFormats = this.fieldFormats.start(); + const indexPatterns = this.indexPatterns.start(core, { + fieldFormats, + logger: this.logger.get('indexPatterns'), + }); + return { - search: this.searchService.start(core, { fieldFormats }), fieldFormats, - indexPatterns: this.indexPatterns.start(core, { - fieldFormats, - logger: this.logger.get('indexPatterns'), - }), + indexPatterns, + search: this.searchService.start(core, { fieldFormats, indexPatterns }), }; } diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 0c74ecb4b2c9d..0d4ba0cba24a3 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -19,6 +19,7 @@ import { ISearchSetup, ISearchStart } from './types'; import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; +import { searchSourceMock } from './search_source/mocks'; export function createSearchSetupMock(): jest.Mocked { return { @@ -33,5 +34,6 @@ export function createSearchStartMock(): jest.Mocked { aggs: searchAggsStartMock(), getSearchStrategy: jest.fn(), search: jest.fn(), + searchSource: searchSourceMock.createStartContract(), }; } diff --git a/src/plugins/data/server/search/routes/call_msearch.test.ts b/src/plugins/data/server/search/routes/call_msearch.test.ts new file mode 100644 index 0000000000000..3d409e22aaa88 --- /dev/null +++ b/src/plugins/data/server/search/routes/call_msearch.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { Observable } from 'rxjs'; +import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; + +import { + coreMock, + pluginInitializerContextConfigMock, +} from '../../../../../../src/core/server/mocks'; +import { convertRequestBody, getCallMsearch } from './call_msearch'; + +describe('callMsearch', () => { + let esClient: DeeplyMockedKeys; + let globalConfig$: Observable; + let uiSettings: IUiSettingsClient; + let callMsearch: ReturnType; + + beforeEach(() => { + const coreContext = coreMock.createRequestHandlerContext(); + esClient = coreContext.elasticsearch.client; + globalConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; + uiSettings = coreContext.uiSettings.client; + + callMsearch = getCallMsearch({ esClient, globalConfig$, uiSettings }); + }); + + it('handler calls msearch with the given request', async () => { + const mockBody = { + searches: [{ header: { index: 'foo' }, body: { query: { match_all: {} } } }], + }; + + await callMsearch({ + body: mockBody, + signal: new AbortController().signal, + }); + + expect(esClient.asCurrentUser.msearch).toHaveBeenCalledTimes(1); + expect(esClient.asCurrentUser.msearch.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "body": "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\"} + {\\"query\\":{\\"match_all\\":{}}} + ", + }, + Object { + "querystring": Object { + "ignore_throttled": true, + "ignore_unavailable": true, + "max_concurrent_shard_requests": undefined, + }, + }, + ] + `); + }); + + describe('convertRequestBody', () => { + it('combines header & body into proper msearch request', () => { + const request = { + searches: [{ header: { index: 'foo', preference: 0 }, body: { test: true } }], + }; + expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` + "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} + {\\"timeout\\":\\"30000ms\\",\\"test\\":true} + " + `); + }); + + it('handles multiple searches', () => { + const request = { + searches: [ + { header: { index: 'foo', preference: 0 }, body: { test: true } }, + { header: { index: 'bar', preference: 1 }, body: { hello: 'world' } }, + ], + }; + expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` + "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} + {\\"timeout\\":\\"30000ms\\",\\"test\\":true} + {\\"ignore_unavailable\\":true,\\"index\\":\\"bar\\",\\"preference\\":1} + {\\"timeout\\":\\"30000ms\\",\\"hello\\":\\"world\\"} + " + `); + }); + }); +}); diff --git a/src/plugins/data/server/search/routes/call_msearch.ts b/src/plugins/data/server/search/routes/call_msearch.ts new file mode 100644 index 0000000000000..764dcd189f8db --- /dev/null +++ b/src/plugins/data/server/search/routes/call_msearch.ts @@ -0,0 +1,100 @@ +/* + * 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 { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { ApiResponse } from '@elastic/elasticsearch'; +import { SearchResponse } from 'elasticsearch'; +import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; + +import { MsearchRequestBody, MsearchResponse } from '../../../common/search/search_source'; +import { shimHitsTotal } from './shim_hits_total'; +import { getShardTimeout, getDefaultSearchParams, toSnakeCase } from '..'; + +/** @internal */ +export function convertRequestBody( + requestBody: MsearchRequestBody, + { timeout }: { timeout?: string } +): string { + return requestBody.searches.reduce((req, curr) => { + const header = JSON.stringify({ + ignore_unavailable: true, + ...curr.header, + }); + const body = JSON.stringify({ + timeout, + ...curr.body, + }); + return `${req}${header}\n${body}\n`; + }, ''); +} + +interface CallMsearchDependencies { + esClient: IScopedClusterClient; + globalConfig$: Observable; + uiSettings: IUiSettingsClient; +} + +/** + * Helper for the `/internal/_msearch` route, exported separately here + * so that it can be reused elsewhere in the data plugin on the server, + * e.g. SearchSource + * + * @internal + */ +export function getCallMsearch(dependencies: CallMsearchDependencies) { + return async (params: { + body: MsearchRequestBody; + signal?: AbortSignal; + }): Promise => { + const { esClient, globalConfig$, uiSettings } = dependencies; + + // get shardTimeout + const config = await globalConfig$.pipe(first()).toPromise(); + const timeout = getShardTimeout(config); + + // trackTotalHits is not supported by msearch + const { trackTotalHits, ...defaultParams } = await getDefaultSearchParams(uiSettings); + + const body = convertRequestBody(params.body, timeout); + + // Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 + const promise = esClient.asCurrentUser.msearch( + { + body, + }, + { + querystring: toSnakeCase(defaultParams), + } + ); + if (params.signal) { + params.signal.addEventListener('abort', () => promise.abort()); + } + const response = (await promise) as ApiResponse<{ responses: Array> }>; + + return { + body: { + ...response, + body: { + responses: response.body.responses?.map((r: SearchResponse) => shimHitsTotal(r)), + }, + }, + }; + }; +} diff --git a/src/plugins/data/server/search/routes/index.ts b/src/plugins/data/server/search/routes/index.ts index a290f08f9b843..f858653f26439 100644 --- a/src/plugins/data/server/search/routes/index.ts +++ b/src/plugins/data/server/search/routes/index.ts @@ -17,6 +17,7 @@ * under the License. */ +export * from './call_msearch'; export * from './msearch'; export * from './search'; export * from './shim_hits_total'; diff --git a/src/plugins/data/server/search/routes/msearch.test.ts b/src/plugins/data/server/search/routes/msearch.test.ts index 3a7d67c31b8be..2f414fe0b4920 100644 --- a/src/plugins/data/server/search/routes/msearch.test.ts +++ b/src/plugins/data/server/search/routes/msearch.test.ts @@ -30,7 +30,8 @@ import { httpServerMock, pluginInitializerContextConfigMock, } from '../../../../../../src/core/server/mocks'; -import { registerMsearchRoute, convertRequestBody } from './msearch'; +import { convertRequestBody } from './call_msearch'; +import { registerMsearchRoute } from './msearch'; import { DataPluginStart } from '../../plugin'; import { dataPluginMock } from '../../mocks'; @@ -49,7 +50,7 @@ describe('msearch route', () => { it('handler calls /_msearch with the given request', async () => { const response = { id: 'yay', body: { responses: [{ hits: { total: 5 } }] } }; - const mockClient = { transport: { request: jest.fn().mockResolvedValue(response) } }; + const mockClient = { msearch: jest.fn().mockResolvedValue(response) }; const mockContext = { core: { elasticsearch: { client: { asCurrentUser: mockClient } }, @@ -70,11 +71,16 @@ describe('msearch route', () => { const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - expect(mockClient.transport.request.mock.calls[0][0].method).toBe('GET'); - expect(mockClient.transport.request.mock.calls[0][0].path).toBe('/_msearch'); - expect(mockClient.transport.request.mock.calls[0][0].body).toEqual( + expect(mockClient.msearch.mock.calls[0][0].body).toEqual( convertRequestBody(mockBody as any, {}) ); + expect(mockClient.msearch.mock.calls[0][1].querystring).toMatchInlineSnapshot(` + Object { + "ignore_throttled": true, + "ignore_unavailable": true, + "max_concurrent_shard_requests": undefined, + } + `); expect(mockResponse.ok).toBeCalled(); expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: response, @@ -89,7 +95,7 @@ describe('msearch route', () => { }, }; const mockClient = { - transport: { request: jest.fn().mockReturnValue(Promise.reject(response)) }, + msearch: jest.fn().mockReturnValue(Promise.reject(response)), }; const mockContext = { core: { @@ -111,40 +117,11 @@ describe('msearch route', () => { const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); - expect(mockClient.transport.request).toBeCalled(); + expect(mockClient.msearch).toBeCalled(); expect(mockResponse.customError).toBeCalled(); const error: any = mockResponse.customError.mock.calls[0][0]; expect(error.body.message).toBe('oh no'); expect(error.body.attributes.error).toBe('oops'); }); - - describe('convertRequestBody', () => { - it('combines header & body into proper msearch request', () => { - const request = { - searches: [{ header: { index: 'foo', preference: 0 }, body: { test: true } }], - }; - expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` - "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} - {\\"timeout\\":\\"30000ms\\",\\"test\\":true} - " - `); - }); - - it('handles multiple searches', () => { - const request = { - searches: [ - { header: { index: 'foo', preference: 0 }, body: { test: true } }, - { header: { index: 'bar', preference: 1 }, body: { hello: 'world' } }, - ], - }; - expect(convertRequestBody(request, { timeout: '30000ms' })).toMatchInlineSnapshot(` - "{\\"ignore_unavailable\\":true,\\"index\\":\\"foo\\",\\"preference\\":0} - {\\"timeout\\":\\"30000ms\\",\\"test\\":true} - {\\"ignore_unavailable\\":true,\\"index\\":\\"bar\\",\\"preference\\":1} - {\\"timeout\\":\\"30000ms\\",\\"hello\\":\\"world\\"} - " - `); - }); - }); }); diff --git a/src/plugins/data/server/search/routes/msearch.ts b/src/plugins/data/server/search/routes/msearch.ts index e1ddb06e4fb6f..7b44aa18bf8fc 100644 --- a/src/plugins/data/server/search/routes/msearch.ts +++ b/src/plugins/data/server/search/routes/msearch.ts @@ -17,46 +17,12 @@ * under the License. */ -import { first } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; -import { SearchResponse } from 'elasticsearch'; import { IRouter } from 'src/core/server'; import { SearchRouteDependencies } from '../search_service'; -import { shimHitsTotal } from './shim_hits_total'; -import { getShardTimeout, getDefaultSearchParams, toSnakeCase } from '..'; -interface MsearchHeaders { - index: string; - preference?: number | string; -} - -interface MsearchRequest { - header: MsearchHeaders; - body: any; -} - -interface RequestBody { - searches: MsearchRequest[]; -} - -/** @internal */ -export function convertRequestBody( - requestBody: RequestBody, - { timeout }: { timeout?: string } -): string { - return requestBody.searches.reduce((req, curr) => { - const header = JSON.stringify({ - ignore_unavailable: true, - ...curr.header, - }); - const body = JSON.stringify({ - timeout, - ...curr.body, - }); - return `${req}${header}\n${body}\n`; - }, ''); -} +import { getCallMsearch } from './call_msearch'; /** * The msearch route takes in an array of searches, each consisting of header @@ -93,35 +59,15 @@ export function registerMsearchRoute(router: IRouter, deps: SearchRouteDependenc }, }, async (context, request, res) => { - const client = context.core.elasticsearch.client.asCurrentUser; - - // get shardTimeout - const config = await deps.globalConfig$.pipe(first()).toPromise(); - const timeout = getShardTimeout(config); - - const body = convertRequestBody(request.body, timeout); - - // trackTotalHits is not supported by msearch - const { trackTotalHits, ...defaultParams } = await getDefaultSearchParams( - context.core.uiSettings.client - ); + const callMsearch = getCallMsearch({ + esClient: context.core.elasticsearch.client, + globalConfig$: deps.globalConfig$, + uiSettings: context.core.uiSettings.client, + }); try { - const response = await client.transport.request({ - method: 'GET', - path: '/_msearch', - body, - querystring: toSnakeCase(defaultParams), - }); - - return res.ok({ - body: { - ...response, - body: { - responses: response.body.responses?.map((r: SearchResponse) => shimHitsTotal(r)), - }, - }, - }); + const response = await callMsearch({ body: request.body }); + return res.ok(response); } catch (err) { return res.customError({ statusCode: err.statusCode || 500, diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index b5d5ec283767d..492ad4395b32a 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -22,7 +22,6 @@ import { IRouter } from 'src/core/server'; import { getRequestAbortedSignal } from '../../lib'; import { SearchRouteDependencies } from '../search_service'; import { shimHitsTotal } from './shim_hits_total'; -import { isEsResponse } from '../../../common'; export function registerSearchRoute( router: IRouter, @@ -62,11 +61,9 @@ export function registerSearchRoute( return res.ok({ body: { ...response, - ...(isEsResponse(response) - ? { - rawResponse: shimHitsTotal(response.rawResponse), - } - : {}), + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, }, }); } catch (err) { diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts index 7057c9c7ca15c..a001d56b36514 100644 --- a/src/plugins/data/server/search/search_service.test.ts +++ b/src/plugins/data/server/search/search_service.test.ts @@ -22,6 +22,7 @@ import { coreMock } from '../../../../core/server/mocks'; import { DataPluginStart } from '../plugin'; import { createFieldFormatsStartMock } from '../field_formats/mocks'; +import { createIndexPatternsStartMock } from '../index_patterns/mocks'; import { SearchService, SearchServiceSetupDependencies } from './search_service'; @@ -54,6 +55,7 @@ describe('Search service', () => { it('exposes proper contract', async () => { const start = plugin.start(mockCoreStart, { fieldFormats: createFieldFormatsStartMock(), + indexPatterns: createIndexPatternsStartMock(), }); expect(start).toHaveProperty('aggs'); expect(start).toHaveProperty('getSearchStrategy'); diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index e19d3dd8a5451..1a9e7d83bc956 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -17,10 +17,12 @@ * under the License. */ -import { Observable } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { pick } from 'lodash'; import { CoreSetup, CoreStart, + KibanaRequest, Logger, Plugin, PluginInitializerContext, @@ -28,24 +30,38 @@ import { SharedGlobalConfig, StartServicesAccessor, } from 'src/core/server'; +import { first } from 'rxjs/operators'; import { ISearchSetup, ISearchStart, ISearchStrategy, SearchEnhancements } from './types'; import { AggsService, AggsSetupDependencies } from './aggs'; import { FieldFormatsStart } from '../field_formats'; -import { registerMsearchRoute, registerSearchRoute } from './routes'; +import { IndexPatternsServiceStart } from '../index_patterns'; +import { getCallMsearch, registerMsearchRoute, registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; import { DataPluginStart } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; import { registerUsageCollector } from './collectors/register'; import { usageProvider } from './collectors/usage'; import { searchTelemetry } from '../saved_objects'; -import { IEsSearchRequest, IEsSearchResponse, ISearchOptions } from '../../common'; +import { + IKibanaSearchRequest, + IKibanaSearchResponse, + IEsSearchRequest, + IEsSearchResponse, + ISearchOptions, + SearchSourceDependencies, + SearchSourceService, + searchSourceRequiredUiSettings, +} from '../../common/search'; +import { + getShardDelayBucketAgg, + SHARD_DELAY_AGG_NAME, +} from '../../common/search/aggs/buckets/shard_delay'; +import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; +import { ConfigSchema } from '../../config'; -type StrategyMap< - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse -> = Record>; +type StrategyMap = Record>; /** @internal */ export interface SearchServiceSetupDependencies { @@ -56,6 +72,7 @@ export interface SearchServiceSetupDependencies { /** @internal */ export interface SearchServiceStartDependencies { fieldFormats: FieldFormatsStart; + indexPatterns: IndexPatternsServiceStart; } /** @internal */ @@ -66,11 +83,12 @@ export interface SearchRouteDependencies { export class SearchService implements Plugin { private readonly aggsService = new AggsService(); + private readonly searchSourceService = new SearchSourceService(); private defaultSearchStrategyName: string = ES_SEARCH_STRATEGY; - private searchStrategies: StrategyMap = {}; + private searchStrategies: StrategyMap = {}; constructor( - private initializerContext: PluginInitializerContext, + private initializerContext: PluginInitializerContext, private readonly logger: Logger ) {} @@ -102,44 +120,96 @@ export class SearchService implements Plugin { registerUsageCollector(usageCollection, this.initializerContext); } + const aggs = this.aggsService.setup({ registerFunction }); + + this.initializerContext.config + .create() + .pipe(first()) + .toPromise() + .then((value) => { + if (value.search.aggs.shardDelay.enabled) { + aggs.types.registerBucket(SHARD_DELAY_AGG_NAME, getShardDelayBucketAgg); + registerFunction(aggShardDelay); + } + }); + return { __enhance: (enhancements: SearchEnhancements) => { if (this.searchStrategies.hasOwnProperty(enhancements.defaultStrategy)) { this.defaultSearchStrategyName = enhancements.defaultStrategy; } }, - aggs: this.aggsService.setup({ registerFunction }), + aggs, registerSearchStrategy: this.registerSearchStrategy, usage, }; } - - private search( - context: RequestHandlerContext, - searchRequest: IEsSearchRequest, - options: ISearchOptions - ) { - return this.getSearchStrategy(options.strategy || this.defaultSearchStrategyName).search( - context, - searchRequest, - options - ); - } - public start( - { uiSettings }: CoreStart, - { fieldFormats }: SearchServiceStartDependencies + { elasticsearch, savedObjects, uiSettings }: CoreStart, + { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), getSearchStrategy: this.getSearchStrategy, search: ( context: RequestHandlerContext, - searchRequest: IEsSearchRequest, + searchRequest: IKibanaSearchRequest, options: Record ) => { return this.search(context, searchRequest, options); }, + searchSource: { + asScoped: async (request: KibanaRequest) => { + const esClient = elasticsearch.client.asScoped(request); + const savedObjectsClient = savedObjects.getScopedClient(request); + const scopedIndexPatterns = await indexPatterns.indexPatternsServiceFactory(request); + const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + + // cache ui settings, only including items which are explicitly needed by SearchSource + const uiSettingsCache = pick( + await uiSettingsClient.getAll(), + searchSourceRequiredUiSettings + ); + + const searchSourceDependencies: SearchSourceDependencies = { + getConfig: (key: string): T => uiSettingsCache[key], + search: (searchRequest, options) => { + /** + * Unless we want all SearchSource users to provide both a KibanaRequest + * (needed for index patterns) AND the RequestHandlerContext (needed for + * low-level search), we need to fake the context as it can be derived + * from the request object anyway. This will pose problems for folks who + * are registering custom search strategies as they are only getting a + * subset of the entire context. Ideally low-level search should be + * refactored to only require the needed dependencies: esClient & uiSettings. + */ + const fakeRequestHandlerContext = { + core: { + elasticsearch: { + client: esClient, + }, + uiSettings: { + client: uiSettingsClient, + }, + }, + } as RequestHandlerContext; + return this.search(fakeRequestHandlerContext, searchRequest, options); + }, + // onResponse isn't used on the server, so we just return the original value + onResponse: (req, res) => res, + legacy: { + callMsearch: getCallMsearch({ + esClient, + globalConfig$: this.initializerContext.config.legacy.globalConfig$, + uiSettings: uiSettingsClient, + }), + loadingCount$: new BehaviorSubject(0), + }, + }; + + return this.searchSourceService.start(scopedIndexPatterns, searchSourceDependencies); + }, + }, }; } @@ -148,8 +218,8 @@ export class SearchService implements Plugin { } private registerSearchStrategy = < - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( name: string, strategy: ISearchStrategy @@ -158,7 +228,25 @@ export class SearchService implements Plugin { this.searchStrategies[name] = strategy; }; - private getSearchStrategy = (name: string): ISearchStrategy => { + private search = < + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse + >( + context: RequestHandlerContext, + searchRequest: SearchStrategyRequest, + options: ISearchOptions + ): Promise => { + return this.getSearchStrategy( + options.strategy || this.defaultSearchStrategyName + ).search(context, searchRequest, options); + }; + + private getSearchStrategy = < + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse + >( + name: string + ): ISearchStrategy => { this.logger.debug(`Get strategy ${name}`); const strategy = this.searchStrategies[name]; if (!strategy) { diff --git a/src/plugins/data/server/search/search_source/mocks.ts b/src/plugins/data/server/search/search_source/mocks.ts new file mode 100644 index 0000000000000..7e9cc8f2ff42c --- /dev/null +++ b/src/plugins/data/server/search/search_source/mocks.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 { KibanaRequest } from 'src/core/server'; + +import { searchSourceCommonMock } from '../../../common/search/search_source/mocks'; +import { ISearchStart } from '../types'; + +function createStartContract(): MockedKeys { + return { + asScoped: async (request: jest.Mocked) => { + return searchSourceCommonMock; + }, + }; +} + +export const searchSourceMock = { + createStartContract, +}; diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index aefdac2ab639f..0de4ef529e896 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -17,8 +17,13 @@ * under the License. */ -import { RequestHandlerContext } from '../../../../core/server'; -import { ISearchOptions } from '../../common/search'; +import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { + ISearchOptions, + ISearchStartSearchSource, + IKibanaSearchRequest, + IKibanaSearchResponse, +} from '../../common/search'; import { AggsSetup, AggsStart } from './aggs'; import { SearchUsage } from './collectors'; import { IEsSearchRequest, IEsSearchResponse } from './es_search'; @@ -34,8 +39,8 @@ export interface ISearchSetup { * strategies. */ registerSearchStrategy: < - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( name: string, strategy: ISearchStrategy @@ -53,8 +58,8 @@ export interface ISearchSetup { } export interface ISearchStart< - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse > { aggs: AggsStart; /** @@ -66,9 +71,12 @@ export interface ISearchStart< ) => ISearchStrategy; search: ( context: RequestHandlerContext, - request: IEsSearchRequest, + request: SearchStrategyRequest, options: ISearchOptions - ) => Promise; + ) => Promise; + searchSource: { + asScoped: (request: KibanaRequest) => Promise; + }; } /** @@ -76,8 +84,8 @@ export interface ISearchStart< * that resolves to a response. */ export interface ISearchStrategy< - SearchStrategyRequest extends IEsSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IEsSearchResponse = IEsSearchResponse + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse > { search: ( context: RequestHandlerContext, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 2024e9e7f2974..fed0c1a02297e 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -5,170 +5,52 @@ ```ts import { $Values } from '@kbn/utility-types'; -import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; +import { ApiResponse } from '@elastic/elasticsearch'; import { Assign } from '@kbn/utility-types'; -import Boom from 'boom'; -import { BulkIndexDocumentsParams } from 'elasticsearch'; -import { CatAliasesParams } from 'elasticsearch'; -import { CatAllocationParams } from 'elasticsearch'; -import { CatCommonParams } from 'elasticsearch'; -import { CatFielddataParams } from 'elasticsearch'; -import { CatHealthParams } from 'elasticsearch'; -import { CatHelpParams } from 'elasticsearch'; -import { CatIndicesParams } from 'elasticsearch'; -import { CatRecoveryParams } from 'elasticsearch'; -import { CatSegmentsParams } from 'elasticsearch'; -import { CatShardsParams } from 'elasticsearch'; -import { CatSnapshotsParams } from 'elasticsearch'; -import { CatTasksParams } from 'elasticsearch'; -import { CatThreadPoolParams } from 'elasticsearch'; -import { ClearScrollParams } from 'elasticsearch'; -import { Client } from 'elasticsearch'; -import { ClusterAllocationExplainParams } from 'elasticsearch'; -import { ClusterGetSettingsParams } from 'elasticsearch'; -import { ClusterHealthParams } from 'elasticsearch'; -import { ClusterPendingTasksParams } from 'elasticsearch'; -import { ClusterPutSettingsParams } from 'elasticsearch'; -import { ClusterRerouteParams } from 'elasticsearch'; -import { ClusterStateParams } from 'elasticsearch'; -import { ClusterStatsParams } from 'elasticsearch'; +import { BehaviorSubject } from 'rxjs'; import { ConfigDeprecationProvider } from '@kbn/config'; import { CoreSetup } from 'src/core/server'; import { CoreSetup as CoreSetup_2 } from 'kibana/server'; import { CoreStart } from 'src/core/server'; -import { CountParams } from 'elasticsearch'; -import { CreateDocumentParams } from 'elasticsearch'; -import { DeleteDocumentByQueryParams } from 'elasticsearch'; -import { DeleteDocumentParams } from 'elasticsearch'; -import { DeleteScriptParams } from 'elasticsearch'; -import { DeleteTemplateParams } from 'elasticsearch'; +import { CoreStart as CoreStart_2 } from 'kibana/server'; import { Duration } from 'moment'; +import { ElasticsearchClient } from 'kibana/server'; import { Ensure } from '@kbn/utility-types'; import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; -import { ExistsParams } from 'elasticsearch'; -import { ExplainParams } from 'elasticsearch'; import { ExpressionAstFunction } from 'src/plugins/expressions/common'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; -import { FieldStatsParams } from 'elasticsearch'; -import { GenericParams } from 'elasticsearch'; -import { GetParams } from 'elasticsearch'; -import { GetResponse } from 'elasticsearch'; -import { GetScriptParams } from 'elasticsearch'; -import { GetSourceParams } from 'elasticsearch'; -import { GetTemplateParams } from 'elasticsearch'; -import { IncomingHttpHeaders } from 'http'; -import { IndexDocumentParams } from 'elasticsearch'; -import { IndicesAnalyzeParams } from 'elasticsearch'; -import { IndicesClearCacheParams } from 'elasticsearch'; -import { IndicesCloseParams } from 'elasticsearch'; -import { IndicesCreateParams } from 'elasticsearch'; -import { IndicesDeleteAliasParams } from 'elasticsearch'; -import { IndicesDeleteParams } from 'elasticsearch'; -import { IndicesDeleteTemplateParams } from 'elasticsearch'; -import { IndicesExistsAliasParams } from 'elasticsearch'; -import { IndicesExistsParams } from 'elasticsearch'; -import { IndicesExistsTemplateParams } from 'elasticsearch'; -import { IndicesExistsTypeParams } from 'elasticsearch'; -import { IndicesFlushParams } from 'elasticsearch'; -import { IndicesFlushSyncedParams } from 'elasticsearch'; -import { IndicesForcemergeParams } from 'elasticsearch'; -import { IndicesGetAliasParams } from 'elasticsearch'; -import { IndicesGetFieldMappingParams } from 'elasticsearch'; -import { IndicesGetMappingParams } from 'elasticsearch'; -import { IndicesGetParams } from 'elasticsearch'; -import { IndicesGetSettingsParams } from 'elasticsearch'; -import { IndicesGetTemplateParams } from 'elasticsearch'; -import { IndicesGetUpgradeParams } from 'elasticsearch'; -import { IndicesOpenParams } from 'elasticsearch'; -import { IndicesPutAliasParams } from 'elasticsearch'; -import { IndicesPutMappingParams } from 'elasticsearch'; -import { IndicesPutSettingsParams } from 'elasticsearch'; -import { IndicesPutTemplateParams } from 'elasticsearch'; -import { IndicesRecoveryParams } from 'elasticsearch'; -import { IndicesRefreshParams } from 'elasticsearch'; -import { IndicesRolloverParams } from 'elasticsearch'; -import { IndicesSegmentsParams } from 'elasticsearch'; -import { IndicesShardStoresParams } from 'elasticsearch'; -import { IndicesShrinkParams } from 'elasticsearch'; -import { IndicesStatsParams } from 'elasticsearch'; -import { IndicesUpdateAliasesParams } from 'elasticsearch'; -import { IndicesUpgradeParams } from 'elasticsearch'; -import { IndicesValidateQueryParams } from 'elasticsearch'; -import { InfoParams } from 'elasticsearch'; -import { IngestDeletePipelineParams } from 'elasticsearch'; -import { IngestGetPipelineParams } from 'elasticsearch'; -import { IngestPutPipelineParams } from 'elasticsearch'; -import { IngestSimulateParams } from 'elasticsearch'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; -import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; -import { KibanaConfigType as KibanaConfigType_2 } from 'src/core/server/kibana_config'; -import { KibanaRequest } from 'kibana/server'; -import { LegacyAPICaller as LegacyAPICaller_2 } from 'kibana/server'; -import { Logger } from '@kbn/logging'; -import { Logger as Logger_2 } from 'kibana/server'; +import { KibanaRequest } from 'src/core/server'; +import { KibanaRequest as KibanaRequest_2 } from 'kibana/server'; +import { LegacyAPICaller } from 'kibana/server'; +import { Logger } from 'kibana/server'; import { LoggerFactory } from '@kbn/logging'; -import { LogMeta } from '@kbn/logging'; -import { MGetParams } from 'elasticsearch'; -import { MGetResponse } from 'elasticsearch'; import { Moment } from 'moment'; import moment from 'moment'; -import { MSearchParams } from 'elasticsearch'; -import { MSearchResponse } from 'elasticsearch'; -import { MSearchTemplateParams } from 'elasticsearch'; -import { MTermVectorsParams } from 'elasticsearch'; -import { NodesHotThreadsParams } from 'elasticsearch'; -import { NodesInfoParams } from 'elasticsearch'; -import { NodesStatsParams } from 'elasticsearch'; +import { NameList } from 'elasticsearch'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { PathConfigType } from '@kbn/utils'; -import { PingParams } from 'elasticsearch'; import { Plugin as Plugin_2 } from 'src/core/server'; +import { Plugin as Plugin_3 } from 'kibana/server'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/server'; -import { PutScriptParams } from 'elasticsearch'; -import { PutTemplateParams } from 'elasticsearch'; import { RecursiveReadonly } from '@kbn/utility-types'; -import { ReindexParams } from 'elasticsearch'; -import { ReindexRethrottleParams } from 'elasticsearch'; -import { RenderSearchTemplateParams } from 'elasticsearch'; import { RequestAdapter } from 'src/plugins/inspector/common'; +import { RequestHandlerContext } from 'src/core/server'; import { RequestStatistics } from 'src/plugins/inspector/common'; import { SavedObject } from 'src/core/server'; -import { SavedObjectsClientContract as SavedObjectsClientContract_2 } from 'src/core/server'; -import { ScrollParams } from 'elasticsearch'; +import { SavedObjectsClientContract } from 'src/core/server'; import { Search } from '@elastic/elasticsearch/api/requestParams'; -import { SearchParams } from 'elasticsearch'; import { SearchResponse } from 'elasticsearch'; -import { SearchShardsParams } from 'elasticsearch'; -import { SearchTemplateParams } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; import { ShardsResponse } from 'elasticsearch'; -import { SnapshotCreateParams } from 'elasticsearch'; -import { SnapshotCreateRepositoryParams } from 'elasticsearch'; -import { SnapshotDeleteParams } from 'elasticsearch'; -import { SnapshotDeleteRepositoryParams } from 'elasticsearch'; -import { SnapshotGetParams } from 'elasticsearch'; -import { SnapshotGetRepositoryParams } from 'elasticsearch'; -import { SnapshotRestoreParams } from 'elasticsearch'; -import { SnapshotStatusParams } from 'elasticsearch'; -import { SnapshotVerifyRepositoryParams } from 'elasticsearch'; -import { SuggestParams } from 'elasticsearch'; -import { TasksCancelParams } from 'elasticsearch'; -import { TasksGetParams } from 'elasticsearch'; -import { TasksListParams } from 'elasticsearch'; -import { TermvectorsParams } from 'elasticsearch'; import { ToastInputFields } from 'src/core/public/notifications'; -import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; -import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; -import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { Unit } from '@elastic/datemath'; import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; -import { UpdateDocumentByQueryParams } from 'elasticsearch'; -import { UpdateDocumentParams } from 'elasticsearch'; // Warning: (ae-forgotten-export) The symbol "AggConfigSerialized" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AggConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -396,6 +278,32 @@ export interface EsQueryConfig { queryStringOptions: Record; } +// Warning: (ae-missing-release-tag) "FieldDescriptor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +interface FieldDescriptor { + // (undocumented) + aggregatable: boolean; + // (undocumented) + esTypes: string[]; + // (undocumented) + name: string; + // (undocumented) + readFromDocValues: boolean; + // (undocumented) + searchable: boolean; + // Warning: (ae-forgotten-export) The symbol "FieldSubType" needs to be exported by the entry point index.d.ts + // + // (undocumented) + subType?: FieldSubType; + // (undocumented) + type: string; +} + +export { FieldDescriptor } + +export { FieldDescriptor as IndexPatternFieldDescriptor } + // Warning: (ae-missing-release-tag) "FieldFormatConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -467,6 +375,7 @@ export function getShardTimeout(config: SharedGlobalConfig): { timeout?: undefined; }; +// Warning: (ae-forgotten-export) The symbol "IIndexPattern" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getTime" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -498,28 +407,20 @@ export type IAggConfigs = AggConfigs; export type IAggType = AggType; // Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IEsSearchRequest extends IKibanaSearchRequest { +export interface IEsSearchRequest extends IKibanaSearchRequest { // (undocumented) indexType?: string; - // Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts - // - // (undocumented) - params?: ISearchRequestParams; } // Warning: (ae-forgotten-export) The symbol "IKibanaSearchResponse" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IEsSearchResponse extends IKibanaSearchResponse { - isPartial?: boolean; - isRunning?: boolean; - // (undocumented) - rawResponse: SearchResponse; -} +export type IEsSearchResponse = IKibanaSearchResponse>; // Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -590,40 +491,125 @@ export interface IFieldType { visualizable?: boolean; } -// Warning: (ae-missing-release-tag) "IIndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-forgotten-export) The symbol "MetricAggType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "IMetricAggType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type IMetricAggType = MetricAggType; + +// Warning: (ae-missing-release-tag) "IndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface IIndexPattern { +export class IndexPattern implements IIndexPattern { + // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts + constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); + addScriptedField(name: string, script: string, fieldType?: string): Promise; // (undocumented) - [key: string]: any; + fieldFormatMap: Record; + // Warning: (ae-forgotten-export) The symbol "IIndexPatternFieldList" needs to be exported by the entry point index.d.ts + // + // (undocumented) + fields: IIndexPatternFieldList & { + toSpec: () => IndexPatternFieldMap; + }; + // (undocumented) + flattenHit: (hit: Record, deep?: boolean) => Record; + // (undocumented) + formatField: FormatFieldFn; // (undocumented) - fieldFormatMap?: Record; + formatHit: { + (hit: Record, type?: string): any; + formatField: FormatFieldFn; + }; // (undocumented) - fields: IFieldType[]; + getAggregationRestrictions(): Record> | undefined; + getAsSavedObjectBody(): { + title: string; + timeFieldName: string | undefined; + intervalName: string | undefined; + sourceFilters: string | undefined; + fields: string | undefined; + fieldFormatMap: string | undefined; + type: string | undefined; + typeMeta: string | undefined; + }; // (undocumented) - getTimeField?(): IFieldType | undefined; + getComputedFields(): { + storedFields: string[]; + scriptFields: any; + docvalueFields: { + field: any; + format: string; + }[]; + }; + // (undocumented) + getFieldByName(name: string): IndexPatternField | undefined; + getFormatterForField(field: IndexPatternField | IndexPatternField['spec'] | IFieldType): FieldFormat; + // Warning: (ae-forgotten-export) The symbol "IndexPatternField" needs to be exported by the entry point index.d.ts + // + // (undocumented) + getNonScriptedFields(): IndexPatternField[]; + getOriginalSavedObjectBody: () => { + title?: string | undefined; + timeFieldName?: string | undefined; + intervalName?: string | undefined; + fields?: string | undefined; + sourceFilters?: string | undefined; + fieldFormatMap?: string | undefined; + typeMeta?: string | undefined; + type?: string | undefined; + }; + // (undocumented) + getScriptedFields(): IndexPatternField[]; + getSourceFiltering(): { + excludes: any[]; + }; + // (undocumented) + getTimeField(): IndexPatternField | undefined; // (undocumented) id?: string; // (undocumented) - timeFieldName?: string; + intervalName: string | undefined; + // (undocumented) + isTimeBased(): boolean; + // (undocumented) + isTimeNanosBased(): boolean; + // (undocumented) + metaFields: string[]; + removeScriptedField(fieldName: string): void; + resetOriginalSavedObjectBody: () => void; + // Warning: (ae-forgotten-export) The symbol "SourceFilter" needs to be exported by the entry point index.d.ts + // + // (undocumented) + sourceFilters?: SourceFilter[]; + // (undocumented) + timeFieldName: string | undefined; // (undocumented) title: string; + // Warning: (ae-forgotten-export) The symbol "IndexPatternSpec" needs to be exported by the entry point index.d.ts + // + // (undocumented) + toSpec(): IndexPatternSpec; // (undocumented) - type?: string; + type: string | undefined; + // Warning: (ae-forgotten-export) The symbol "TypeMeta" needs to be exported by the entry point index.d.ts + // + // (undocumented) + typeMeta?: TypeMeta; + // (undocumented) + version: string | undefined; } -// Warning: (ae-forgotten-export) The symbol "MetricAggType" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "IMetricAggType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IMetricAggType = MetricAggType; - // Warning: (ae-missing-release-tag) "IndexPatternAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public @deprecated +// @public (undocumented) export interface IndexPatternAttributes { // (undocumented) fieldFormatMap?: string; @@ -643,28 +629,6 @@ export interface IndexPatternAttributes { typeMeta: string; } -// Warning: (ae-missing-release-tag) "FieldDescriptor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface IndexPatternFieldDescriptor { - // (undocumented) - aggregatable: boolean; - // (undocumented) - esTypes: string[]; - // (undocumented) - name: string; - // (undocumented) - readFromDocValues: boolean; - // (undocumented) - searchable: boolean; - // Warning: (ae-forgotten-export) The symbol "FieldSubType" needs to be exported by the entry point index.d.ts - // - // (undocumented) - subType?: FieldSubType; - // (undocumented) - type: string; -} - // Warning: (ae-missing-release-tag) "indexPatterns" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -677,20 +641,35 @@ export const indexPatterns: { // // @public (undocumented) export class IndexPatternsFetcher { - constructor(callDataCluster: LegacyAPICaller_2); + constructor(callDataCluster: LegacyAPICaller); getFieldsForTimePattern(options: { pattern: string; metaFields: string[]; lookBack: number; interval: string; - }): Promise; + }): Promise; getFieldsForWildcard(options: { pattern: string | string[]; metaFields?: string[]; fieldCapsOptions?: { allowNoIndices: boolean; }; - }): Promise; + }): Promise; +} + +// Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStart" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "IndexPatternsService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class IndexPatternsService implements Plugin_3 { + // (undocumented) + setup(core: CoreSetup_2): void; + // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStartDeps" needs to be exported by the entry point index.d.ts + // + // (undocumented) + start(core: CoreStart_2, { fieldFormats, logger }: IndexPatternsServiceStartDeps): { + indexPatternsServiceFactory: (kibanaRequest: KibanaRequest_2) => Promise; + }; } // Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -713,29 +692,31 @@ export interface ISearchSetup { // // (undocumented) aggs: AggsSetup; - registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void; + registerSearchStrategy: (name: string, strategy: ISearchStrategy) => void; usage?: SearchUsage; } // Warning: (ae-missing-release-tag) "ISearchStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ISearchStart { +export interface ISearchStart { // Warning: (ae-forgotten-export) The symbol "AggsStart" needs to be exported by the entry point index.d.ts // // (undocumented) aggs: AggsStart; getSearchStrategy: (name: string) => ISearchStrategy; - // Warning: (ae-forgotten-export) The symbol "RequestHandlerContext" needs to be exported by the entry point index.d.ts - // // (undocumented) - search: (context: RequestHandlerContext, request: IEsSearchRequest, options: ISearchOptions) => Promise; + search: (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise; + // (undocumented) + searchSource: { + asScoped: (request: KibanaRequest) => Promise; + }; } // Warning: (ae-missing-release-tag) "ISearchStrategy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export interface ISearchStrategy { +export interface ISearchStrategy { // (undocumented) cancel?: (context: RequestHandlerContext, id: string) => Promise; // (undocumented) @@ -887,13 +868,13 @@ export class Plugin implements Plugin_2>; fieldFormats: { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; + indexPatternsServiceFactory: (kibanaRequest: import("../../../core/server").KibanaRequest) => Promise; }; + search: ISearchStart>; }; // (undocumented) stop(): void; @@ -926,8 +907,6 @@ export interface PluginStart { // // (undocumented) fieldFormats: FieldFormatsStart; - // Warning: (ae-forgotten-export) The symbol "IndexPatternsServiceStart" needs to be exported by the entry point index.d.ts - // // (undocumented) indexPatterns: IndexPatternsServiceStart; // (undocumented) @@ -1113,7 +1092,8 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // // src/plugins/data/common/es_query/filters/meta_filter.ts:53:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/meta_filter.ts:54:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/fields/types.ts:41:25 - (ae-forgotten-export) The symbol "IndexPattern" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:64:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:70:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:40:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:71:21 - (ae-forgotten-export) The symbol "getEsQueryConfig" needs to be exported by the entry point index.d.ts @@ -1135,20 +1115,22 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:225:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:225:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:225:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:225:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:227:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:228:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:237:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:238:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:239:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:248:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:251:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:226:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:226:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:226:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:226:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:228:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:229:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:238:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:239:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:240:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:245:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:249:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:252:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index_patterns/index_patterns_service.ts:50:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:88:66 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/search/types.ts:78:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/discover/public/application/angular/context.js b/src/plugins/discover/public/application/angular/context.js index 6223090aa9f97..bb9d71c8671a2 100644 --- a/src/plugins/discover/public/application/angular/context.js +++ b/src/plugins/discover/public/application/angular/context.js @@ -45,26 +45,18 @@ const k7Breadcrumbs = ($route) => { }; getAngularModule().config(($routeProvider) => { - $routeProvider - // deprecated route, kept for compatibility - // should be removed in the future - .when('/context/:indexPatternId/:type/:id*', { - redirectTo: '/context/:indexPatternId/:id', - }) - .when('/context/:indexPatternId/:id*', { - controller: ContextAppRouteController, - k7Breadcrumbs, - controllerAs: 'contextAppRoute', - resolve: { - indexPattern: ($route, Promise) => { - const indexPattern = getServices().indexPatterns.get( - $route.current.params.indexPatternId - ); - return Promise.props({ ip: indexPattern }); - }, + $routeProvider.when('/context/:indexPatternId/:id*', { + controller: ContextAppRouteController, + k7Breadcrumbs, + controllerAs: 'contextAppRoute', + resolve: { + indexPattern: ($route, Promise) => { + const indexPattern = getServices().indexPatterns.get($route.current.params.indexPatternId); + return Promise.props({ ip: indexPattern }); }, - template: contextAppRouteTemplate, - }); + }, + template: contextAppRouteTemplate, + }); }); function ContextAppRouteController($routeParams, $scope, $route) { diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js b/src/plugins/discover/public/application/angular/context/query_parameters/actions.js index fcd4b8ac02cfb..cdf9283737226 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.js @@ -19,6 +19,7 @@ import _ from 'lodash'; import { esFilters } from '../../../../../../data/public'; +import { popularizeField } from '../../../helpers/popularize_field'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; @@ -56,7 +57,7 @@ export function getQueryParameterActions(filterManager, indexPatterns) { filterManager.addFilters(newFilters); if (indexPatterns) { const indexPattern = await indexPatterns.get(indexPatternId); - indexPattern.popularizeField(field.name, 1); + await popularizeField(indexPattern, field.name, indexPatterns); } }; diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index f698ed84a8948..145d3afe23224 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -56,8 +56,8 @@ getAngularModule().directive('contextApp', function ContextApp() { }); function ContextAppController($scope, Private) { - const { filterManager, indexpatterns, uiSettings } = getServices(); - const queryParameterActions = getQueryParameterActions(filterManager, indexpatterns); + const { filterManager, indexPatterns, uiSettings } = getServices(); + const queryParameterActions = getQueryParameterActions(filterManager, indexPatterns); const queryActions = Private(QueryActionsProvider); this.state = createInitialState( parseInt(uiSettings.get(CONTEXT_STEP_SETTING), 10), diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 7871cc4b16464..92b96d11723e0 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -27,6 +27,12 @@ import { i18n } from '@kbn/i18n'; import { getState, splitState } from './discover_state'; import { RequestAdapter } from '../../../../inspector/public'; +import { + esFilters, + indexPatterns as indexPatternsUtils, + connectToQueryState, + syncQueryStateWithUrl, +} from '../../../../data/public'; import { SavedObjectSaveModal, showSaveModal } from '../../../../saved_objects/public'; import { getSortArray, getSortForSearchSource } from './doc_table'; import { createFixedScroll } from './directives/fixed_scroll'; @@ -34,7 +40,6 @@ import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; import { showOpenSearchPanel } from '../components/top_nav/show_open_search_panel'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; -import { getPainlessError } from './get_painless_error'; import { discoverResponseHandler } from './response_handler'; import { getRequestInspectorStats, @@ -65,12 +70,8 @@ const { import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; import { validateTimeRange } from '../helpers/validate_time_range'; -import { - esFilters, - indexPatterns as indexPatternsUtils, - connectToQueryState, - syncQueryStateWithUrl, -} from '../../../../data/public'; +import { popularizeField } from '../helpers/popularize_field'; + import { getIndexPatternId } from '../helpers/get_index_pattern_id'; import { addFatalError } from '../../../../kibana_legacy/public'; import { @@ -786,18 +787,10 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise // If the request was aborted then no need to surface this error in the UI if (error instanceof Error && error.name === 'AbortError') return; - const fetchError = getPainlessError(error); + $scope.fetchStatus = fetchStatuses.NO_RESULTS; + $scope.rows = []; - if (fetchError) { - $scope.fetchError = fetchError; - } else { - toastNotifications.addError(error, { - title: i18n.translate('discover.errorLoadingData', { - defaultMessage: 'Error loading data', - }), - toastMessage: error.shortMessage || error.body?.message, - }); - } + data.search.showError(error); }); }; @@ -960,7 +953,9 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise // TODO: On array fields, negating does not negate the combination, rather all terms $scope.filterQuery = function (field, values, operation) { - $scope.indexPattern.popularizeField(field, 1); + const { indexPattern } = $scope; + + popularizeField(indexPattern, field.name, indexPatterns); const newFilters = esFilters.generateFilters( filterManager, field, @@ -973,7 +968,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.addColumn = function addColumn(columnName) { if (uiCapabilities.discover.save) { - $scope.indexPattern.popularizeField(columnName, 1); + const { indexPattern } = $scope; + popularizeField(indexPattern, columnName, indexPatterns); } const columns = columnActions.addColumn($scope.state.columns, columnName); setAppState({ columns }); @@ -981,7 +977,8 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.removeColumn = function removeColumn(columnName) { if (uiCapabilities.discover.save) { - $scope.indexPattern.popularizeField(columnName, 1); + const { indexPattern } = $scope; + popularizeField(indexPattern, columnName, indexPatterns); } const columns = columnActions.removeColumn($scope.state.columns, columnName); // The state's sort property is an array of [sortByColumn,sortDirection] diff --git a/src/plugins/discover/public/application/angular/get_painless_error.ts b/src/plugins/discover/public/application/angular/get_painless_error.ts deleted file mode 100644 index 162dacd3ac3b7..0000000000000 --- a/src/plugins/discover/public/application/angular/get_painless_error.ts +++ /dev/null @@ -1,106 +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 { i18n } from '@kbn/i18n'; - -interface FailedShards { - shard: number; - index: string; - node: string; - reason: { - type: string; - reason: string; - script_stack: string[]; - script: string; - lang: string; - position: { - offset: number; - start: number; - end: number; - }; - caused_by: { - type: string; - reason: string; - }; - }; -} - -interface EsError { - body: { - statusCode: number; - error: string; - message: string; - attributes?: { - error?: { - root_cause?: [ - { - lang: string; - script: string; - } - ]; - type: string; - reason: string; - caused_by: { - type: string; - reason: string; - phase: string; - grouped: boolean; - failed_shards: FailedShards[]; - }; - }; - }; - }; -} - -export function getCause(error: EsError) { - const cause = error.body?.attributes?.error?.root_cause; - if (cause) { - return cause[0]; - } - - const failedShards = error.body?.attributes?.error?.caused_by?.failed_shards; - - if (failedShards && failedShards[0] && failedShards[0].reason) { - return error.body?.attributes?.error?.caused_by?.failed_shards[0].reason; - } -} - -export function getPainlessError(error: EsError) { - const cause = getCause(error); - - if (!cause) { - return; - } - - const { lang, script } = cause; - - if (lang !== 'painless') { - return; - } - - return { - lang, - script, - message: i18n.translate('discover.painlessError.painlessScriptedFieldErrorMessage', { - defaultMessage: "Error with Painless scripted field '{script}'.", - values: { script }, - }), - error: error.body?.message, - }; -} diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 1a98843649259..de1faaf9fc19d 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -25,13 +25,12 @@ import { IUiSettingsClient, MountPoint } from 'kibana/public'; import { HitsCounter } from './hits_counter'; import { TimechartHeader } from './timechart_header'; import { DiscoverSidebar } from './sidebar'; -import { getServices, IIndexPattern } from '../../kibana_services'; +import { getServices, IndexPattern } from '../../kibana_services'; // @ts-ignore import { DiscoverNoResults } from '../angular/directives/no_results'; import { DiscoverUninitialized } from '../angular/directives/uninitialized'; import { DiscoverHistogram } from '../angular/directives/histogram'; import { LoadingSpinner } from './loading_spinner/loading_spinner'; -import { DiscoverFetchError, FetchError } from './fetch_error/fetch_error'; import { DocTableLegacy } from '../angular/doc_table/create_doc_table_react'; import { SkipBottomButton } from './skip_bottom_button'; import { @@ -54,11 +53,10 @@ export interface DiscoverLegacyProps { addColumn: (column: string) => void; fetch: () => void; fetchCounter: number; - fetchError: FetchError; fieldCounts: Record; histogramData: Chart; hits: number; - indexPattern: IIndexPattern; + indexPattern: IndexPattern; minimumVisibleRows: number; onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; onChangeInterval: (interval: string) => void; @@ -95,7 +93,6 @@ export function DiscoverLegacy({ addColumn, fetch, fetchCounter, - fetchError, fieldCounts, histogramData, hits, @@ -216,8 +213,7 @@ export function DiscoverLegacy({ {resultState === 'uninitialized' && } {/* @TODO: Solved in the Angular way to satisfy functional test - should be improved*/} - {fetchError && } -
+
diff --git a/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss b/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss deleted file mode 100644 index a587b2897e3a0..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/fetch_error.scss +++ /dev/null @@ -1,3 +0,0 @@ -.discoverFetchError { - max-width: 1000px; -} diff --git a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx b/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx deleted file mode 100644 index dc8f1238eac6f..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/fetch_error.tsx +++ /dev/null @@ -1,96 +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 './fetch_error.scss'; -import React, { Fragment } from 'react'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; -import { getServices } from '../../../kibana_services'; - -export interface FetchError { - lang: string; - script: string; - message: string; - error: string; -} - -interface Props { - fetchError: FetchError; -} - -export const DiscoverFetchError = ({ fetchError }: Props) => { - if (!fetchError) { - return null; - } - - let body; - - if (fetchError.lang === 'painless') { - const { chrome } = getServices(); - const mangagementUrlObj = chrome.navLinks.get('kibana:stack_management'); - const managementUrl = mangagementUrlObj ? mangagementUrlObj.url : ''; - const url = `${managementUrl}/kibana/indexPatterns`; - - body = ( -

- - ), - managementLink: ( - - - - ), - }} - /> -

- ); - } - - return ( - - - - - - - - {body} - - {fetchError.error} - - - - - - - - ); -}; diff --git a/src/plugins/discover/public/application/components/fetch_error/index.ts b/src/plugins/discover/public/application/components/fetch_error/index.ts deleted file mode 100644 index 0206bc48257ac..0000000000000 --- a/src/plugins/discover/public/application/components/fetch_error/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './fetch_error'; diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index b03b37da40908..8ab296bf1af4f 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -20,13 +20,12 @@ import React from 'react'; import { findTestSubject } from '@elastic/eui/lib/test'; // @ts-ignore -import StubIndexPattern from 'test_utils/stub_index_pattern'; -// @ts-ignore import stubbedLogstashFields from 'fixtures/logstash_fields'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { DiscoverField } from './discover_field'; import { coreMock } from '../../../../../../core/public/mocks'; import { IndexPatternField } from '../../../../../data/public'; +import { getStubIndexPattern } from '../../../../../data/public/test_utils'; jest.mock('../../../kibana_services', () => ({ getServices: () => ({ @@ -52,32 +51,44 @@ jest.mock('../../../kibana_services', () => ({ }), })); -function getComponent(selected = false, showDetails = false, useShortDots = false) { - const indexPattern = new StubIndexPattern( +function getComponent({ + selected = false, + showDetails = false, + useShortDots = false, + field, +}: { + selected?: boolean; + showDetails?: boolean; + useShortDots?: boolean; + field?: IndexPatternField; +}) { + const indexPattern = getStubIndexPattern( 'logstash-*', (cfg: any) => cfg, 'time', stubbedLogstashFields(), - coreMock.createStart() + coreMock.createSetup() ); - const field = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); + const finalField = + field ?? + new IndexPatternField( + { + name: 'bytes', + type: 'number', + esTypes: ['long'], + count: 10, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + 'bytes' + ); const props = { indexPattern, - field, + field: finalField, getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: true, columns: [] })), onAddFilter: jest.fn(), onAddField: jest.fn(), @@ -92,18 +103,37 @@ function getComponent(selected = false, showDetails = false, useShortDots = fals describe('discover sidebar field', function () { it('should allow selecting fields', function () { - const { comp, props } = getComponent(); + const { comp, props } = getComponent({}); findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); expect(props.onAddField).toHaveBeenCalledWith('bytes'); }); it('should allow deselecting fields', function () { - const { comp, props } = getComponent(true); + const { comp, props } = getComponent({ selected: true }); findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); expect(props.onRemoveField).toHaveBeenCalledWith('bytes'); }); it('should trigger getDetails', function () { - const { comp, props } = getComponent(true); + const { comp, props } = getComponent({ selected: true }); findTestSubject(comp, 'field-bytes-showDetails').simulate('click'); expect(props.getDetails).toHaveBeenCalledWith(props.field); }); + it('should not allow clicking on _source', function () { + const field = new IndexPatternField( + { + name: '_source', + type: '_source', + esTypes: ['_source'], + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + '_source' + ); + const { comp, props } = getComponent({ + selected: true, + field, + }); + findTestSubject(comp, 'field-_source-showDetails').simulate('click'); + expect(props.getDetails).not.toHaveBeenCalled(); + }); }); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx index bb330cba68e2e..8ff603884239e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.tsx @@ -172,6 +172,19 @@ export function DiscoverField({ ); } + if (field.type === '_source') { + return ( + + ); + } + return ( { togglePopover(); }} - buttonProps={{ 'data-test-subj': `field-${field.name}-showDetails` }} + dataTestSubj={`field-${field.name}-showDetails`} fieldIcon={dscFieldIcon} fieldAction={actionButton} fieldName={fieldName} diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx index 9572f35641d69..6177b60a0a7ad 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.test.tsx @@ -21,8 +21,6 @@ import _ from 'lodash'; import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; // @ts-ignore -import StubIndexPattern from 'test_utils/stub_index_pattern'; -// @ts-ignore import realHits from 'fixtures/real_hits.js'; // @ts-ignore import stubbedLogstashFields from 'fixtures/logstash_fields'; @@ -31,6 +29,7 @@ import React from 'react'; import { DiscoverSidebar, DiscoverSidebarProps } from './discover_sidebar'; import { coreMock } from '../../../../../../core/public/mocks'; import { IndexPatternAttributes } from '../../../../../data/common'; +import { getStubIndexPattern } from '../../../../../data/public/test_utils'; import { SavedObject } from '../../../../../../core/types'; jest.mock('../../../kibana_services', () => ({ @@ -65,14 +64,15 @@ jest.mock('./lib/get_index_pattern_field_list', () => ({ })); function getCompProps() { - const indexPattern = new StubIndexPattern( + const indexPattern = getStubIndexPattern( 'logstash-*', (cfg: any) => cfg, 'time', stubbedLogstashFields(), - coreMock.createStart() + coreMock.createSetup() ); + // @ts-expect-error _.each() is passing additional args to flattenHit const hits = _.each(_.cloneDeep(realHits), indexPattern.flattenHit) as Array< Record >; diff --git a/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts index 8746883a5d968..94c76f2d5f012 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/field_calculator.test.ts @@ -21,11 +21,10 @@ import _ from 'lodash'; // @ts-ignore import realHits from 'fixtures/real_hits.js'; // @ts-ignore -import StubIndexPattern from 'test_utils/stub_index_pattern'; -// @ts-ignore import stubbedLogstashFields from 'fixtures/logstash_fields'; import { coreMock } from '../../../../../../../core/public/mocks'; import { IndexPattern } from '../../../../../../data/public'; +import { getStubIndexPattern } from '../../../../../../data/public/test_utils'; // @ts-ignore import { fieldCalculator } from './field_calculator'; @@ -33,12 +32,12 @@ let indexPattern: IndexPattern; describe('fieldCalculator', function () { beforeEach(function () { - indexPattern = new StubIndexPattern( + indexPattern = getStubIndexPattern( 'logstash-*', (cfg: any) => cfg, 'time', stubbedLogstashFields(), - coreMock.createStart() + coreMock.createSetup() ); }); it('should have a _countMissing that counts nulls & undefineds in an array', function () { @@ -142,7 +141,7 @@ describe('fieldCalculator', function () { let hits: any; beforeEach(function () { - hits = _.each(_.cloneDeep(realHits), indexPattern.flattenHit); + hits = _.each(_.cloneDeep(realHits), (hit) => indexPattern.flattenHit(hit)); }); it('Should return an array of values for _source fields', function () { diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index 07e9e0a129a26..2874e2483275b 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -22,7 +22,7 @@ import { findTestSubject } from '@elastic/eui/lib/test'; import { DocViewTable } from './table'; import { indexPatterns, IndexPattern } from '../../../../../data/public'; -const indexPattern = { +const indexPattern = ({ fields: { getAll: () => [ { @@ -60,7 +60,7 @@ const indexPattern = { metaFields: ['_index', '_score'], flattenHit: undefined, formatHit: jest.fn((hit) => hit._source), -} as IndexPattern; +} as unknown) as IndexPattern; indexPattern.fields.getByName = (name: string) => { return indexPattern.fields.getAll().find((field) => field.name === name); diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts index 1de62fe5a0348..af88cacfcf992 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts @@ -221,7 +221,6 @@ export class SearchEmbeddable if (!searchScope.columns) { return; } - indexPattern.popularizeField(columnName, 1); const columns = columnActions.addColumn(searchScope.columns, columnName); this.updateInput({ columns }); }; diff --git a/src/plugins/discover/public/application/helpers/popularize_field.test.ts b/src/plugins/discover/public/application/helpers/popularize_field.test.ts new file mode 100644 index 0000000000000..f1ff67c23b92b --- /dev/null +++ b/src/plugins/discover/public/application/helpers/popularize_field.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { IndexPattern, IndexPatternsService } from '../../../../data/public'; +import { popularizeField } from './popularize_field'; + +describe('Popularize field', () => { + test('returns undefined if index pattern lacks id', async () => { + const indexPattern = ({} as unknown) as IndexPattern; + const fieldName = '@timestamp'; + const indexPatternsService = ({} as unknown) as IndexPatternsService; + const result = await popularizeField(indexPattern, fieldName, indexPatternsService); + expect(result).toBeUndefined(); + }); + + test('returns undefined if field not found', async () => { + const indexPattern = ({ + fields: { + getByName: () => {}, + }, + } as unknown) as IndexPattern; + const fieldName = '@timestamp'; + const indexPatternsService = ({} as unknown) as IndexPatternsService; + const result = await popularizeField(indexPattern, fieldName, indexPatternsService); + expect(result).toBeUndefined(); + }); + + test('returns undefined if successful', async () => { + const field = { + count: 0, + }; + const indexPattern = ({ + id: 'id', + fields: { + getByName: () => field, + }, + } as unknown) as IndexPattern; + const fieldName = '@timestamp'; + const indexPatternsService = ({ + updateSavedObject: async () => {}, + } as unknown) as IndexPatternsService; + const result = await popularizeField(indexPattern, fieldName, indexPatternsService); + expect(result).toBeUndefined(); + expect(field.count).toEqual(1); + }); + + test('hides errors', async () => { + const field = { + count: 0, + }; + const indexPattern = ({ + id: 'id', + fields: { + getByName: () => field, + }, + } as unknown) as IndexPattern; + const fieldName = '@timestamp'; + const indexPatternsService = ({ + updateSavedObject: async () => { + throw new Error('unknown error'); + }, + } as unknown) as IndexPatternsService; + const result = await popularizeField(indexPattern, fieldName, indexPatternsService); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/popularize_field.ts b/src/plugins/discover/public/application/helpers/popularize_field.ts new file mode 100644 index 0000000000000..0aea86e47c954 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/popularize_field.ts @@ -0,0 +1,41 @@ +/* + * 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 { IndexPattern, IndexPatternsService } from '../../../../data/public'; + +async function popularizeField( + indexPattern: IndexPattern, + fieldName: string, + indexPatternsService: IndexPatternsService +) { + if (!indexPattern.id) return; + const field = indexPattern.fields.getByName(fieldName); + if (!field) { + return; + } + + field.count++; + // Catch 409 errors caused by user adding columns in a higher frequency that the changes can be persisted to Elasticsearch + try { + await indexPatternsService.updateSavedObject(indexPattern, 0, true); + // eslint-disable-next-line no-empty + } catch {} +} + +export { popularizeField }; diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index 440bd3fdf86d3..b1bbc89b62d9d 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -277,6 +277,14 @@ export class DiscoverPlugin return `#${path}`; }); plugins.urlForwarding.forwardApp('context', 'discover', (path) => { + const urlParts = path.split('/'); + // take care of urls containing legacy url, those split in the following way + // ["", "context", indexPatternId, _type, id + params] + if (urlParts[4]) { + // remove _type part + const newPath = [...urlParts.slice(0, 3), ...urlParts.slice(4)].join('/'); + return `#${newPath}`; + } return `#${path}`; }); plugins.urlForwarding.forwardApp('discover', 'discover', (path) => { diff --git a/src/plugins/discover/server/saved_objects/search_migrations.test.ts b/src/plugins/discover/server/saved_objects/search_migrations.test.ts index babd25c03dbb2..3324a2d93f5ad 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.test.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.test.ts @@ -22,36 +22,61 @@ import { searchMigrations } from './search_migrations'; const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; +const testMigrateMatchAllQuery = (migrationFn: Function) => { + it('should migrate obsolete match_all query', () => { + const migratedDoc = migrationFn( + { + type: 'search', + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + query: { + match_all: {}, + }, + }), + }, + }, + }, + savedObjectMigrationContext + ); + const migratedSearchSource = JSON.parse( + migratedDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + + expect(migratedSearchSource).toEqual({ + query: { + query: '', + language: 'kuery', + }, + }); + }); + + it('should return original doc if searchSourceJSON cannot be parsed', () => { + const migratedDoc = migrationFn( + { + type: 'search', + attributes: { + kibanaSavedObjectMeta: 'kibanaSavedObjectMeta', + }, + }, + savedObjectMigrationContext + ); + + expect(migratedDoc).toEqual({ + type: 'search', + attributes: { + kibanaSavedObjectMeta: 'kibanaSavedObjectMeta', + }, + }); + }); +}; + describe('migration search', () => { describe('6.7.2', () => { const migrationFn = searchMigrations['6.7.2']; - it('should migrate obsolete match_all query', () => { - const migratedDoc = migrationFn( - { - type: 'search', - attributes: { - kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - query: { - match_all: {}, - }, - }), - }, - }, - }, - savedObjectMigrationContext - ); - const migratedSearchSource = JSON.parse( - migratedDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON - ); - - expect(migratedSearchSource).toEqual({ - query: { - query: '', - language: 'kuery', - }, - }); + describe('migrateMatchAllQuery', () => { + testMigrateMatchAllQuery(migrationFn); }); }); @@ -328,4 +353,12 @@ Object { expect(migratedDoc).toEqual(doc); }); }); + + describe('7.9.3', () => { + const migrationFn = searchMigrations['7.9.3']; + + describe('migrateMatchAllQuery', () => { + testMigrateMatchAllQuery(migrationFn); + }); + }); }); diff --git a/src/plugins/discover/server/saved_objects/search_migrations.ts b/src/plugins/discover/server/saved_objects/search_migrations.ts index 0302159c43c56..fdb086bd17a2d 100644 --- a/src/plugins/discover/server/saved_objects/search_migrations.ts +++ b/src/plugins/discover/server/saved_objects/search_migrations.ts @@ -21,6 +21,12 @@ import { flow, get } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; +/** + * This migration script is related to: + * @link https://github.com/elastic/kibana/pull/62194 + * @link https://github.com/elastic/kibana/pull/14644 + * This is only a problem when you import an object from 5.x into 6.x but to be sure that all saved objects migrated we should execute it twice in 6.7.2 and 7.9.3 + */ const migrateMatchAllQuery: SavedObjectMigrationFn = (doc) => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); @@ -31,6 +37,7 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = (doc) => { searchSource = JSON.parse(searchSourceJSON); } catch (e) { // Let it go, the data is invalid and we'll leave it as is + return doc; } if (searchSource.query?.match_all) { @@ -125,4 +132,5 @@ export const searchMigrations = { '6.7.2': flow(migrateMatchAllQuery), '7.0.0': flow(setNewReferences), '7.4.0': flow(migrateSearchSortToNestedArray), + '7.9.3': flow(migrateMatchAllQuery), }; diff --git a/src/plugins/embeddable/.storybook/main.js b/src/plugins/embeddable/.storybook/main.js new file mode 100644 index 0000000000000..1818aa44a9399 --- /dev/null +++ b/src/plugins/embeddable/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.stories.tsx similarity index 100% rename from src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx rename to src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.stories.tsx diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 8c3d7ab9c30d0..ba24913c6d1c0 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -18,7 +18,7 @@ */ import { EditPanelAction } from './edit_panel_action'; -import { Embeddable, EmbeddableInput } from '../embeddables'; +import { Embeddable, EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; import { embeddablePluginMock } from '../../mocks'; @@ -53,20 +53,50 @@ test('is compatible when edit url is available, in edit mode and editable', asyn ).toBe(true); }); -test('redirects to app using state transfer', async () => { +test('redirects to app using state transfer with by value mode', async () => { applicationMock.currentAppId$ = of('superCoolCurrentApp'); const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); - const input = { id: '123', viewMode: ViewMode.EDIT }; - const embeddable = new EditableEmbeddable(input, true); + const embeddable = new EditableEmbeddable( + ({ + id: '123', + viewMode: ViewMode.EDIT, + coolInput1: 1, + coolInput2: 2, + } as unknown) as EmbeddableInput, + true + ); + embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); + await action.execute({ embeddable }); + expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { + path: '/123', + state: { + originatingApp: 'superCoolCurrentApp', + embeddableId: '123', + valueInput: { + id: '123', + viewMode: ViewMode.EDIT, + coolInput1: 1, + coolInput2: 2, + }, + }, + }); +}); + +test('redirects to app using state transfer without by value mode', async () => { + applicationMock.currentAppId$ = of('superCoolCurrentApp'); + const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); + const embeddable = new EditableEmbeddable( + { id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput, + true + ); embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); await action.execute({ embeddable }); expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { path: '/123', state: { originatingApp: 'superCoolCurrentApp', - byValueMode: true, embeddableId: '123', - valueInput: input, + valueInput: undefined, }, }); }); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 8d12ddd0299e7..cbc28713197ba 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -29,6 +29,8 @@ import { EmbeddableEditorState, EmbeddableStateTransfer, SavedObjectEmbeddableInput, + EmbeddableInput, + Container, } from '../..'; export const ACTION_EDIT_PANEL = 'editPanel'; @@ -118,8 +120,7 @@ export class EditPanelAction implements Action { const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId; const state: EmbeddableEditorState = { originatingApp: this.currentAppId, - byValueMode, - valueInput: byValueMode ? embeddable.getInput() : undefined, + valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined, embeddableId: embeddable.id, }; return { app, path, state }; @@ -132,4 +133,11 @@ export class EditPanelAction implements Action { const editUrl = embeddable ? embeddable.getOutput().editUrl : undefined; return editUrl ? editUrl : ''; } + + private getExplicitInput({ embeddable }: ActionContext): EmbeddableInput { + return ( + (embeddable.getRoot() as Container)?.getInput()?.panels?.[embeddable.id]?.explicitInput ?? + embeddable.getInput() + ); + } } diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts index 38975cc220bc2..9f701f021162a 100644 --- a/src/plugins/embeddable/public/lib/containers/container.ts +++ b/src/plugins/embeddable/public/lib/containers/container.ts @@ -199,8 +199,8 @@ export abstract class Container< return { type: factory.type, explicitInput: { - id: embeddableId, ...explicitInput, + id: embeddableId, } as TEmbeddableInput, }; } diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx index 715827a72c61b..17a2ac3b2a32b 100644 --- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx @@ -17,43 +17,36 @@ * under the License. */ import React from 'react'; +import { wait } from '@testing-library/dom'; +import { cleanup, render } from '@testing-library/react/pure'; import { ErrorEmbeddable } from './error_embeddable'; import { EmbeddableRoot } from './embeddable_root'; -import { mount } from 'enzyme'; + +afterEach(cleanup); test('ErrorEmbeddable renders an embeddable', async () => { const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' }); - const component = mount(); - expect( - component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length - ).toBe(1); - expect( - component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length - ).toBe(1); - expect( - component - .getDOMNode() - .querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0] - .innerHTML.includes('some error occurred') - ).toBe(true); + const { getByTestId, getByText } = render(); + + expect(getByTestId('embeddableStackError')).toBeVisible(); + await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component + expect(getByText(/some error occurred/i)).toBeVisible(); }); test('ErrorEmbeddable renders an embeddable with markdown message', async () => { const error = '[some link](http://localhost:5601/takeMeThere)'; const embeddable = new ErrorEmbeddable(error, { id: '123', title: 'Error' }); - const component = mount(); - expect( - component.getDOMNode().querySelectorAll('[data-test-subj="embeddableStackError"]').length - ).toBe(1); - expect( - component.getDOMNode().querySelectorAll('[data-test-subj="errorMessageMarkdown"]').length - ).toBe(1); - expect( - component - .getDOMNode() - .querySelectorAll('[data-test-subj="errorMessageMarkdown"]')[0] - .innerHTML.includes( - 'some link' - ) - ).toBe(true); + const { getByTestId, getByText } = render(); + + expect(getByTestId('embeddableStackError')).toBeVisible(); + await wait(() => getByTestId('errorMessageMarkdown')); // wait for lazy markdown component + expect(getByText(/some link/i)).toMatchInlineSnapshot(` + + some link + + `); }); diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss index f6057524cb832..36a7fee14cce1 100644 --- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss +++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss @@ -60,6 +60,12 @@ .embPanel__titleText { @include euiTextTruncate; } + + .embPanel__placeholderTitleText { + @include euiTextTruncate; + font-weight: $euiFontWeightRegular; + color: $euiColorMediumShade; + } } .embPanel__dragger:not(.embPanel__title) { @@ -159,4 +165,4 @@ pointer-events: none; filter: grayscale(100%); filter: gray; -} \ No newline at end of file +} diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index ca5cb5ca4f0d5..a2da31773696c 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -32,7 +32,12 @@ import { EmbeddableContext, contextMenuTrigger, } from '../triggers'; -import { IEmbeddable, EmbeddableOutput, EmbeddableError } from '../embeddables/i_embeddable'; +import { + IEmbeddable, + EmbeddableOutput, + EmbeddableError, + EmbeddableInput, +} from '../embeddables/i_embeddable'; import { ViewMode } from '../types'; import { RemovePanelAction } from './panel_header/panel_actions'; @@ -55,7 +60,7 @@ const removeById = (disabledActions: string[]) => ({ id }: { id: string }) => disabledActions.indexOf(id) === -1; interface Props { - embeddable: IEmbeddable; + embeddable: IEmbeddable; getActions: UiActionsService['getTriggerCompatibleActions']; getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; @@ -72,7 +77,7 @@ interface State { panels: EuiContextMenuPanelDescriptor[]; focusedPanelIndex?: string; viewMode: ViewMode; - hidePanelTitles: boolean; + hidePanelTitle: boolean; closeContextMenu: boolean; badges: Array>; notifications: Array>; @@ -90,17 +95,15 @@ export class EmbeddablePanel extends React.Component { constructor(props: Props) { super(props); const { embeddable } = this.props; - const viewMode = embeddable.getInput().viewMode - ? embeddable.getInput().viewMode - : ViewMode.EDIT; - const hidePanelTitles = embeddable.parent - ? Boolean(embeddable.parent.getInput().hidePanelTitles) - : false; + const viewMode = embeddable.getInput().viewMode ?? ViewMode.EDIT; + const hidePanelTitle = + Boolean(embeddable.parent?.getInput()?.hidePanelTitles) || + Boolean(embeddable.getInput()?.hidePanelTitles); this.state = { panels: [], viewMode, - hidePanelTitles, + hidePanelTitle, closeContextMenu: false, badges: [], notifications: [], @@ -150,9 +153,7 @@ export class EmbeddablePanel extends React.Component { embeddable.getInput$().subscribe(async () => { if (this.mounted) { this.setState({ - viewMode: embeddable.getInput().viewMode - ? embeddable.getInput().viewMode - : ViewMode.EDIT, + viewMode: embeddable.getInput().viewMode ?? ViewMode.EDIT, }); this.refreshBadges(); @@ -165,7 +166,9 @@ export class EmbeddablePanel extends React.Component { this.parentSubscription = parent.getInput$().subscribe(async () => { if (this.mounted && parent) { this.setState({ - hidePanelTitles: Boolean(parent.getInput().hidePanelTitles), + hidePanelTitle: + Boolean(embeddable.parent?.getInput()?.hidePanelTitles) || + Boolean(embeddable.getInput()?.hidePanelTitles), }); this.refreshBadges(); @@ -219,7 +222,7 @@ export class EmbeddablePanel extends React.Component { {!this.props.hideHeader && ( { const createGetUserData = (overlays: OverlayStart) => async function getUserData(context: { embeddable: IEmbeddable }) { - return new Promise<{ title: string | undefined }>((resolve) => { + return new Promise<{ title: string | undefined; hideTitle?: boolean }>((resolve) => { const session = overlays.openModal( toMountPoint( { + updateTitle={(title, hideTitle) => { session.close(); - resolve({ title }); + resolve({ title, hideTitle }); }} + cancel={() => session.close()} /> ), { diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts index 36957c3b79491..d65539e344a78 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts @@ -24,7 +24,9 @@ import { IEmbeddable } from '../../../../embeddables'; export const ACTION_CUSTOMIZE_PANEL = 'ACTION_CUSTOMIZE_PANEL'; -type GetUserData = (context: ActionContext) => Promise<{ title: string | undefined }>; +type GetUserData = ( + context: ActionContext +) => Promise<{ title: string | undefined; hideTitle?: boolean }>; interface ActionContext { embeddable: IEmbeddable; @@ -52,7 +54,8 @@ export class CustomizePanelTitleAction implements Action { } public async execute({ embeddable }: ActionContext) { - const customTitle = await this.getDataFromUser({ embeddable }); - embeddable.updateInput(customTitle); + const data = await this.getDataFromUser({ embeddable }); + const { title, hideTitle } = data; + embeddable.updateInput({ title, hidePanelTitles: hideTitle }); } } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx index b590f20092939..ad986adb619b8 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal.tsx @@ -36,31 +36,28 @@ import { IEmbeddable } from '../../../../'; interface CustomizePanelProps { embeddable: IEmbeddable; - updateTitle: (newTitle: string | undefined) => void; + updateTitle: (newTitle: string | undefined, hideTitle: boolean | undefined) => void; + cancel: () => void; } interface State { title: string | undefined; - hideTitle: boolean; + hideTitle: boolean | undefined; } export class CustomizePanelModal extends Component { constructor(props: CustomizePanelProps) { super(props); this.state = { - hideTitle: props.embeddable.getOutput().title === '', - title: props.embeddable.getInput().title, + hideTitle: props.embeddable.getInput().hidePanelTitles, + title: props.embeddable.getInput().title ?? this.props.embeddable.getOutput().defaultTitle, }; } - private updateTitle = (title: string | undefined) => { - // An empty string will mean "use the default value", which is represented by setting - // title to undefined (where as an empty string is actually used to indicate "hide title"). - this.setState({ title: title === '' ? undefined : title }); - }; - private reset = () => { - this.setState({ title: undefined }); + this.setState({ + title: this.props.embeddable.getOutput().defaultTitle, + }); }; private onHideTitleToggle = () => { @@ -70,12 +67,11 @@ export class CustomizePanelModal extends Component { }; private save = () => { - if (this.state.hideTitle) { - this.props.updateTitle(''); - } else { - const newTitle = this.state.title === '' ? undefined : this.state.title; - this.props.updateTitle(newTitle); - } + const newTitle = + this.state.title === this.props.embeddable.getOutput().defaultTitle + ? undefined + : this.state.title; + this.props.updateTitle(newTitle, this.state.hideTitle); }; public render() { @@ -116,9 +112,8 @@ export class CustomizePanelModal extends Component { name="min" type="text" disabled={this.state.hideTitle} - placeholder={this.props.embeddable.getOutput().defaultTitle} value={this.state.title || ''} - onChange={(e) => this.updateTitle(e.target.value)} + onChange={(e) => this.setState({ title: e.target.value })} aria-label={i18n.translate( 'embeddableApi.customizePanel.modal.optionsMenuForm.panelTitleInputAriaLabel', { @@ -141,9 +136,7 @@ export class CustomizePanelModal extends Component { - this.props.updateTitle(this.props.embeddable.getOutput().title)} - > + this.props.cancel()}> Promise; closeContextMenu: boolean; badges: Array>; notifications: Array>; embeddable: IEmbeddable; headerId?: string; + showPlaceholderTitle?: boolean; } function renderBadges(badges: Array>, embeddable: IEmbeddable) { @@ -126,7 +127,7 @@ function getViewDescription(embeddable: IEmbeddable | VisualizeEmbeddable) { export function PanelHeader({ title, isViewMode, - hidePanelTitles, + hidePanelTitle, getActionContextMenuPanel, closeContextMenu, badges, @@ -135,12 +136,30 @@ export function PanelHeader({ headerId, }: PanelHeaderProps) { const viewDescription = getViewDescription(embeddable); - const showTitle = !isViewMode || (title && !hidePanelTitles) || viewDescription !== ''; - const showPanelBar = badges.length > 0 || showTitle; + const showTitle = !hidePanelTitle && (!isViewMode || title || viewDescription !== ''); + const showPanelBar = badges.length > 0 || notifications.length > 0 || showTitle; const classes = classNames('embPanel__header', { // eslint-disable-next-line @typescript-eslint/naming-convention 'embPanel__header--floater': !showPanelBar, }); + const placeholderTitle = i18n.translate('embeddableApi.panel.placeholderTitle', { + defaultMessage: '[No Title]', + }); + + const getAriaLabel = () => { + return ( + + {showPanelBar && title + ? i18n.translate('embeddableApi.panel.enhancedDashboardPanelAriaLabel', { + defaultMessage: 'Dashboard panel: {title}', + values: { title: title || placeholderTitle }, + }) + : i18n.translate('embeddableApi.panel.dashboardPanelAriaLabel', { + defaultMessage: 'Dashboard panel', + })} + + ); + }; if (!showPanelBar) { return ( @@ -151,6 +170,7 @@ export function PanelHeader({ closeContextMenu={closeContextMenu} title={title} /> + {getAriaLabel()}
); } @@ -160,34 +180,20 @@ export function PanelHeader({ className={classes} data-test-subj={`embeddablePanelHeading-${(title || '').replace(/\s/g, '')}`} > -

+

{showTitle ? ( - ) : ( - - - {i18n.translate('embeddableApi.panel.dashboardPanelAriaLabel', { - defaultMessage: 'Dashboard panel', - })} - - + {getAriaLabel()} )} {renderBadges(badges, embeddable)}

diff --git a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts index ef79b18acd4f5..4155cb4d3b60c 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/embeddable_state_transfer.test.ts @@ -85,10 +85,10 @@ describe('embeddable state transfer', () => { it('can send an outgoing embeddable package state', async () => { await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, }); }); @@ -96,12 +96,16 @@ describe('embeddable state transfer', () => { const historyMock = mockHistoryState({ kibanaIsNowForSports: 'extremeSportsKibana' }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); await stateTransfer.navigateToWithEmbeddablePackage(destinationApp, { - state: { type: 'coolestType', id: '150' }, + state: { type: 'coolestType', input: { savedObjectId: '150' } }, appendToExistingState: true, }); expect(application.navigateToApp).toHaveBeenCalledWith('superUltraVisualize', { path: undefined, - state: { kibanaIsNowForSports: 'extremeSportsKibana', type: 'coolestType', id: '150' }, + state: { + kibanaIsNowForSports: 'extremeSportsKibana', + type: 'coolestType', + input: { savedObjectId: '150' }, + }, }); }); @@ -120,10 +124,13 @@ describe('embeddable state transfer', () => { }); it('can fetch an incoming embeddable package state', async () => { - const historyMock = mockHistoryState({ type: 'skisEmbeddable', id: '123' }); + const historyMock = mockHistoryState({ + type: 'skisEmbeddable', + input: { savedObjectId: '123' }, + }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); const fetchedState = stateTransfer.getIncomingEmbeddablePackage(); - expect(fetchedState).toEqual({ type: 'skisEmbeddable', id: '123' }); + expect(fetchedState).toEqual({ type: 'skisEmbeddable', input: { savedObjectId: '123' } }); }); it('returns undefined when embeddable package is not in the right shape', async () => { @@ -136,12 +143,12 @@ describe('embeddable state transfer', () => { it('removes all keys in the keysToRemoveAfterFetch array', async () => { const historyMock = mockHistoryState({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); stateTransfer = new EmbeddableStateTransfer(application.navigateToApp, historyMock); - stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'id'] }); + stateTransfer.getIncomingEmbeddablePackage({ keysToRemoveAfterFetch: ['type', 'input'] }); expect(historyMock.replace).toHaveBeenCalledWith( expect.objectContaining({ state: { test1: 'test1', test2: 'test2' } }) ); @@ -150,7 +157,7 @@ describe('embeddable state transfer', () => { it('leaves state as is when no keysToRemove are supplied', async () => { const historyMock = mockHistoryState({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); @@ -158,7 +165,7 @@ describe('embeddable state transfer', () => { stateTransfer.getIncomingEmbeddablePackage(); expect(historyMock.location.state).toEqual({ type: 'skisEmbeddable', - id: '123', + input: { savedObjectId: '123' }, test1: 'test1', test2: 'test2', }); diff --git a/src/plugins/embeddable/public/lib/state_transfer/types.ts b/src/plugins/embeddable/public/lib/state_transfer/types.ts index 3f3456d914728..d8b4f4801bba3 100644 --- a/src/plugins/embeddable/public/lib/state_transfer/types.ts +++ b/src/plugins/embeddable/public/lib/state_transfer/types.ts @@ -17,17 +17,17 @@ * under the License. */ -import { EmbeddableInput } from '..'; +import { Optional } from '@kbn/utility-types'; +import { EmbeddableInput, SavedObjectEmbeddableInput } from '..'; /** - * Represents a state package that contains the last active app id. + * A state package that contains information an editor will need to create or edit an embeddable then redirect back. * @public */ export interface EmbeddableEditorState { originatingApp: string; - byValueMode?: boolean; - valueInput?: EmbeddableInput; embeddableId?: string; + valueInput?: EmbeddableInput; } export function isEmbeddableEditorState(state: unknown): state is EmbeddableEditorState { @@ -35,32 +35,18 @@ export function isEmbeddableEditorState(state: unknown): state is EmbeddableEdit } /** - * Represents a state package that contains all fields necessary to create an embeddable by reference in a container. + * A state package that contains all fields necessary to create or update an embeddable by reference or by value in a container. * @public */ -export interface EmbeddablePackageByReferenceState { +export interface EmbeddablePackageState { type: string; - id: string; -} - -/** - * Represents a state package that contains all fields necessary to create an embeddable by value in a container. - * @public - */ -export interface EmbeddablePackageByValueState { - type: string; - input: EmbeddableInput; + input: Optional | Optional; embeddableId?: string; } -export type EmbeddablePackageState = - | EmbeddablePackageByReferenceState - | EmbeddablePackageByValueState; - export function isEmbeddablePackageState(state: unknown): state is EmbeddablePackageState { return ( - (ensureFieldOfTypeExists('type', state, 'string') && - ensureFieldOfTypeExists('id', state, 'string')) || + ensureFieldOfTypeExists('type', state, 'string') && ensureFieldOfTypeExists('input', state, 'object') ); } diff --git a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx index d12a55dd827e6..24d94020af4dc 100644 --- a/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx +++ b/src/plugins/embeddable/public/tests/customize_panel_modal.test.tsx @@ -33,9 +33,9 @@ import { HelloWorldContainer } from '../lib/test_samples/embeddables/hello_world import { coreMock } from '../../../../core/public/mocks'; import { testPlugin } from './test_plugin'; import { CustomizePanelModal } from '../lib/panel/panel_header/panel_actions/customize_title/customize_panel_modal'; -import { mount } from 'enzyme'; import { EmbeddableStart } from '../plugin'; import { createEmbeddablePanelMock } from '../mocks'; +import { mountWithIntl } from '../../../../test_utils/public/enzyme_helpers'; let api: EmbeddableStart; let container: Container; @@ -84,19 +84,20 @@ beforeEach(async () => { } }); -test('Is initialized with the embeddables title', async () => { - const component = mount( {}} />); +test('Value is initialized with the embeddables title', async () => { + const component = mountWithIntl( + {}} cancel={() => {}} /> + ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); - expect(inputField.props().placeholder).toBe(embeddable.getOutput().title); - expect(inputField.props().placeholder).toBe(embeddable.getOutput().defaultTitle); - expect(inputField.props().value).toBe(''); + expect(inputField.props().value).toBe(embeddable.getOutput().title); + expect(inputField.props().value).toBe(embeddable.getOutput().defaultTitle); }); test('Calls updateTitle with a new title', async () => { const updateTitle = jest.fn(); - const component = mount( - + const component = mountWithIntl( + {}} /> ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); @@ -105,15 +106,15 @@ test('Calls updateTitle with a new title', async () => { findTestSubject(component, 'saveNewTitleButton').simulate('click'); - expect(updateTitle).toBeCalledWith('new title'); + expect(updateTitle).toBeCalledWith('new title', undefined); }); test('Input value shows custom title if one given', async () => { embeddable.updateInput({ title: 'new title' }); const updateTitle = jest.fn(); - const component = mount( - + const component = mountWithIntl( + {}} /> ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); @@ -122,12 +123,12 @@ test('Input value shows custom title if one given', async () => { expect(inputField.props().value).toBe('new title'); }); -test('Reset updates the input with the default title when the embeddable has no title override', async () => { +test('Reset updates the input value with the default title when the embeddable has a title override', async () => { const updateTitle = jest.fn(); embeddable.updateInput({ title: 'my custom title' }); - const component = mount( - + const component = mountWithIntl( + {}} /> ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); @@ -135,13 +136,14 @@ test('Reset updates the input with the default title when the embeddable has no inputField.simulate('change', event); findTestSubject(component, 'resetCustomEmbeddablePanelTitle').simulate('click'); - expect(inputField.props().placeholder).toBe(embeddable.getOutput().defaultTitle); + const inputAfter = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); + expect(inputAfter.props().value).toBe(embeddable.getOutput().defaultTitle); }); -test('Reset updates the input with the default title when the embeddable has a title override', async () => { +test('Reset updates the input with the default title when the embeddable has no title override', async () => { const updateTitle = jest.fn(); - const component = mount( - + const component = mountWithIntl( + {}} /> ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); @@ -149,13 +151,14 @@ test('Reset updates the input with the default title when the embeddable has a t inputField.simulate('change', event); findTestSubject(component, 'resetCustomEmbeddablePanelTitle').simulate('click'); - expect(inputField.props().placeholder).toBe(embeddable.getOutput().defaultTitle); + await component.update(); + expect(inputField.props().value).toBe(embeddable.getOutput().defaultTitle); }); test('Reset calls updateTitle with undefined', async () => { const updateTitle = jest.fn(); - const component = mount( - + const component = mountWithIntl( + {}} /> ); const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input'); @@ -165,19 +168,21 @@ test('Reset calls updateTitle with undefined', async () => { findTestSubject(component, 'resetCustomEmbeddablePanelTitle').simulate('click'); findTestSubject(component, 'saveNewTitleButton').simulate('click'); - expect(updateTitle).toBeCalledWith(undefined); + expect(updateTitle).toBeCalledWith(undefined, undefined); }); test('Can set title to an empty string', async () => { const updateTitle = jest.fn(); - const component = mount( - + const component = mountWithIntl( + {}} /> ); - const inputField = findTestSubject(component, 'customizePanelHideTitle'); - inputField.simulate('click'); + const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput'); + const event = { target: { value: '' } }; + inputField.simulate('change', event); findTestSubject(component, 'saveNewTitleButton').simulate('click'); - expect(inputField.props().value).toBeUndefined(); - expect(updateTitle).toBeCalledWith(''); + const inputFieldAfter = findTestSubject(component, 'customEmbeddablePanelTitleInput'); + expect(inputFieldAfter.props().value).toBe(''); + expect(updateTitle).toBeCalledWith('', undefined); }); diff --git a/src/plugins/embeddable/scripts/storybook.js b/src/plugins/embeddable/scripts/storybook.js deleted file mode 100644 index 0d7712fe973f4..0000000000000 --- a/src/plugins/embeddable/scripts/storybook.js +++ /dev/null @@ -1,26 +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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'embeddable', - storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.examples.tsx')], -}); diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/index.ts deleted file mode 100644 index a9c6ea1e01d54..0000000000000 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { useXJsonMode } from './use_xjson_mode'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/use_xjson_mode.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/use_xjson_mode.ts deleted file mode 100644 index b783045492f05..0000000000000 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/monaco/use_xjson_mode.ts +++ /dev/null @@ -1,32 +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 { XJsonLang } from '@kbn/monaco'; -import { useXJsonMode as useBaseXJsonMode } from '../xjson'; - -interface ReturnValue extends ReturnType { - XJsonLang: typeof XJsonLang; -} - -export const useXJsonMode = (json: Parameters[0]): ReturnValue => { - return { - ...useBaseXJsonMode(json), - XJsonLang, - }; -}; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/index.ts index a9c6ea1e01d54..adbdbe97c4a07 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/index.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/index.ts @@ -18,3 +18,5 @@ */ export { useXJsonMode } from './use_xjson_mode'; + +export { collapseLiteralStrings, expandLiteralStrings } from './json_xjson_translation_tools'; diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts rename to src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt rename to src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/utils_string_expanding.txt similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt rename to src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/__tests__/utils_string_expanding.txt diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/index.ts rename to src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/parser.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/parser.ts similarity index 100% rename from src/plugins/es_ui_shared/public/console_lang/lib/json_xjson_translation_tools/parser.ts rename to src/plugins/es_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/parser.ts diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/use_xjson_mode.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/use_xjson_mode.ts index 7dcc7c9ed83bc..1d4c473ed14e4 100644 --- a/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/use_xjson_mode.ts +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/xjson/use_xjson_mode.ts @@ -18,7 +18,8 @@ */ import { useState, Dispatch } from 'react'; -import { collapseLiteralStrings, expandLiteralStrings } from '../../public'; + +import { collapseLiteralStrings, expandLiteralStrings } from './json_xjson_translation_tools'; interface ReturnValue { xJson: string; diff --git a/src/plugins/es_ui_shared/kibana.json b/src/plugins/es_ui_shared/kibana.json index eab7355d66f09..d442bfb93d5af 100644 --- a/src/plugins/es_ui_shared/kibana.json +++ b/src/plugins/es_ui_shared/kibana.json @@ -4,7 +4,6 @@ "ui": true, "server": true, "extraPublicDirs": [ - "static/ace_x_json/hooks", "static/validators/string", "static/forms/hook_form_lib", "static/forms/helpers", diff --git a/src/plugins/es_ui_shared/public/console_lang/index.ts b/src/plugins/es_ui_shared/public/console_lang/index.ts deleted file mode 100644 index 7d83191569622..0000000000000 --- a/src/plugins/es_ui_shared/public/console_lang/index.ts +++ /dev/null @@ -1,32 +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. - */ - -// Lib is intentionally not included in this barrel export file to separate worker logic -// from being imported with pure functions - -export { - ElasticsearchSqlHighlightRules, - ScriptHighlightRules, - XJsonHighlightRules, - addXJsonToRules, - XJsonMode, - installXJsonMode, -} from './ace/modes'; - -export { expandLiteralStrings, collapseLiteralStrings } from './lib'; diff --git a/src/plugins/es_ui_shared/public/console_lang/lib/index.ts b/src/plugins/es_ui_shared/public/console_lang/lib/index.ts deleted file mode 100644 index bf7f0290d4158..0000000000000 --- a/src/plugins/es_ui_shared/public/console_lang/lib/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { collapseLiteralStrings, expandLiteralStrings } from './json_xjson_translation_tools'; diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index 5a1c13658604a..94b084e7d3f20 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -22,9 +22,9 @@ * In the future, each top level folder should be exported like that to avoid naming collision */ import * as Forms from './forms'; -import * as Monaco from './monaco'; import * as ace from './ace'; import * as GlobalFlyout from './global_flyout'; +import * as XJson from './xjson'; export { JsonEditor, OnJsonEditorUpdateHandler, JsonEditorState } from './components/json_editor'; @@ -43,17 +43,6 @@ export { export { indices } from './indices'; -export { - installXJsonMode, - XJsonMode, - ElasticsearchSqlHighlightRules, - addXJsonToRules, - ScriptHighlightRules, - XJsonHighlightRules, - collapseLiteralStrings, - expandLiteralStrings, -} from './console_lang'; - export { AuthorizationContext, AuthorizationProvider, @@ -66,7 +55,7 @@ export { useAuthorizationContext, } from './authorization'; -export { Monaco, Forms, ace, GlobalFlyout }; +export { Forms, ace, GlobalFlyout, XJson }; export { extractQueryParams } from './url'; diff --git a/src/plugins/es_ui_shared/public/monaco/index.ts b/src/plugins/es_ui_shared/public/monaco/index.ts deleted file mode 100644 index 23ba93e913234..0000000000000 --- a/src/plugins/es_ui_shared/public/monaco/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { useXJsonMode } from '../../__packages_do_not_import__/monaco'; diff --git a/src/plugins/es_ui_shared/public/xjson/index.ts b/src/plugins/es_ui_shared/public/xjson/index.ts new file mode 100644 index 0000000000000..d505cbe0c6348 --- /dev/null +++ b/src/plugins/es_ui_shared/public/xjson/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from '../../__packages_do_not_import__/xjson'; diff --git a/src/plugins/es_ui_shared/static/ace_x_json/hooks/index.ts b/src/plugins/es_ui_shared/static/ace_x_json/hooks/index.ts deleted file mode 100644 index 1d2c33a9f0f47..0000000000000 --- a/src/plugins/es_ui_shared/static/ace_x_json/hooks/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { useXJsonMode } from './use_x_json'; diff --git a/src/plugins/es_ui_shared/static/ace_x_json/hooks/use_x_json.ts b/src/plugins/es_ui_shared/static/ace_x_json/hooks/use_x_json.ts deleted file mode 100644 index 3a093ac6869d0..0000000000000 --- a/src/plugins/es_ui_shared/static/ace_x_json/hooks/use_x_json.ts +++ /dev/null @@ -1,33 +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 { XJsonMode } from '../../../public'; -import { useXJsonMode as useBaseXJsonMode } from '../../../__packages_do_not_import__/xjson'; - -const xJsonMode = new XJsonMode(); - -interface ReturnValue extends ReturnType { - xJsonMode: typeof xJsonMode; -} - -export const useXJsonMode = (json: Parameters[0]): ReturnValue => { - return { - ...useBaseXJsonMode(json), - xJsonMode, - }; -}; diff --git a/src/plugins/expressions/README.asciidoc b/src/plugins/expressions/README.asciidoc new file mode 100644 index 0000000000000..e07f6e2909ab8 --- /dev/null +++ b/src/plugins/expressions/README.asciidoc @@ -0,0 +1,55 @@ +[[kibana-expressions-plugin]] +== `expressions` plugin + +Expression pipeline is a chain of functions that *pipe* its output to the +input of the next function. Functions can be configured using arguments provided +by the user. The final output of the expression pipeline can be rendered using +one of the *renderers* registered in `expressions` plugin. + +All the arguments to expression functions need to be serializable, as well as input and output. +Expression functions should try to stay 'pure'. This makes functions easy to reuse and also +make it possible to serialize the whole chain as well as output at every step of execution. + +Expressions power visualizations in Dashboard and Lens, as well as, every +*element* in Canvas is backed by an expression. + +This plugin provides methods which will parse & execute an *expression pipeline* +string for you, as well as a series of registries for advanced users who might +want to incorporate their own functions, types, and renderers into the service +for use in their own application. + +=== Examples + +Below is an example of serialized expression for one Canvas element that fetches +data using `essql` function, pipes it further to `math` and `metric` functions, +and final `render` function renders the result. + +[source] +filters +| essql + query="SELECT COUNT(timestamp) as total_errors + FROM kibana_sample_data_logs + WHERE tags LIKE '%warning%' OR tags LIKE '%error%'" +| math "total_errors" +| metric "TOTAL ISSUES" + metricFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=48 align="left" color="#FFFFFF" weight="normal" underline=false italic=false} + labelFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=30 align="left" color="#FFFFFF" weight="lighter" underline=false italic=false} +| render +[/source] + +[role="screenshot"] +image::https://user-images.githubusercontent.com/9773803/74162514-3250a880-4c21-11ea-9e68-86f66862a183.png[] + +=== API documentation + +==== Server API +https://github.com/elastic/kibana/blob/master/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserversetup.md[Server Setup contract] +https://github.com/elastic/kibana/blob/master/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionsserverstart.md[Server Start contract] + +===== Browser API +https://github.com/elastic/kibana/blob/master/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsservicesetup.md[Browser Setup contract] +https://github.com/elastic/kibana/blob/master/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsstart.md[Browser Start contract] + + +==== Other documentation +https://www.elastic.co/guide/en/kibana/current/canvas-function-arguments.html[See Canvas documentation about expressions] diff --git a/src/plugins/expressions/README.md b/src/plugins/expressions/README.md deleted file mode 100644 index c1f032ace37c9..0000000000000 --- a/src/plugins/expressions/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# `expressions` plugin - -This plugin provides methods which will parse & execute an *expression pipeline* -string for you, as well as a series of registries for advanced users who might -want to incorporate their own functions, types, and renderers into the service -for use in their own application. - -Expression pipeline is a chain of functions that *pipe* its output to the -input of the next function. Functions can be configured using arguments provided -by the user. The final output of the expression pipeline can be rendered using -one of the *renderers* registered in `expressions` plugin. - -Expressions power visualizations in Dashboard and Lens, as well as, every -*element* in Canvas is backed by an expression. - -Below is an example of one Canvas element that fetches data using `essql` function, -pipes it further to `math` and `metric` functions, and final `render` function -renders the result. - -``` -filters -| essql - query="SELECT COUNT(timestamp) as total_errors - FROM kibana_sample_data_logs - WHERE tags LIKE '%warning%' OR tags LIKE '%error%'" -| math "total_errors" -| metric "TOTAL ISSUES" - metricFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=48 align="left" color="#FFFFFF" weight="normal" underline=false italic=false} - labelFont={font family="'Open Sans', Helvetica, Arial, sans-serif" size=30 align="left" color="#FFFFFF" weight="lighter" underline=false italic=false} -| render -``` - -![image](https://user-images.githubusercontent.com/9773803/74162514-3250a880-4c21-11ea-9e68-86f66862a183.png) - -[See Canvas documentation about expressions](https://www.elastic.co/guide/en/kibana/current/canvas-function-arguments.html). diff --git a/src/plugins/expressions/common/ast/types.ts b/src/plugins/expressions/common/ast/types.ts index d5039d0adb318..09fb4fae3f201 100644 --- a/src/plugins/expressions/common/ast/types.ts +++ b/src/plugins/expressions/common/ast/types.ts @@ -18,7 +18,6 @@ */ import { ExpressionValue, ExpressionValueError } from '../expression_types'; -import { ExpressionFunction } from '../../common'; export type ExpressionAstNode = | ExpressionAstExpression @@ -48,9 +47,9 @@ export interface ExpressionAstFunctionDebug { success: boolean; /** - * Reference to the expression function this AST node represents. + * Id of expression function. */ - fn: ExpressionFunction; + fn: string; /** * Input that expression function received as its first argument. diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 2b8aa4b5e68f0..ff331d7c5ddaf 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -491,7 +491,7 @@ describe('Execution', () => { await execution.result; for (const node of execution.state.get().ast.chain) { - expect(node.debug?.fn.name).toBe('add'); + expect(node.debug?.fn).toBe('add'); } }); @@ -667,7 +667,7 @@ describe('Execution', () => { expect(node2.debug).toMatchObject({ success: false, - fn: expect.any(Object), + fn: 'throws', input: expect.any(Object), args: expect.any(Object), error: expect.any(Object), diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 3533500a2fbc5..d4c9b0a25d45b 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -235,7 +235,7 @@ export class Execution< const timeEnd: number = now(); (link as ExpressionAstFunction).debug = { success: true, - fn, + fn: fn.name, input, args: resolvedArgs, output, @@ -253,7 +253,7 @@ export class Execution< if (this.params.debug) { (link as ExpressionAstFunction).debug = { success: false, - fn, + fn: fn.name, input, args, error, diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts index 8c784352b9fdf..20c5b2dd434b5 100644 --- a/src/plugins/expressions/common/execution/execution_contract.ts +++ b/src/plugins/expressions/common/execution/execution_contract.ts @@ -18,6 +18,8 @@ */ import { Execution } from './execution'; +import { ExpressionValueError } from '../expression_types/specs'; +import { ExpressionAstExpression } from '../ast'; /** * `ExecutionContract` is a wrapper around `Execution` class. It provides the @@ -53,7 +55,7 @@ export class ExecutionContract< * wraps that error into `ExpressionValueError` type and returns that. * This function never throws. */ - getData = async () => { + getData = async (): Promise => { try { return await this.execution.result; } catch (e) { @@ -80,7 +82,7 @@ export class ExecutionContract< /** * Get AST used to execute the expression. */ - getAst = () => this.execution.state.get().ast; + getAst = (): ExpressionAstExpression => this.execution.state.get().ast; /** * Get Inspector adapters provided to all functions of expression through diff --git a/src/plugins/expressions/common/expression_types/specs/error.ts b/src/plugins/expressions/common/expression_types/specs/error.ts index 35554954d0828..ebaedcbba0d23 100644 --- a/src/plugins/expressions/common/expression_types/specs/error.ts +++ b/src/plugins/expressions/common/expression_types/specs/error.ts @@ -28,8 +28,10 @@ export type ExpressionValueError = ExpressionValueBoxed< { error: { message: string; + type?: string; name?: string; stack?: string; + original?: Error; }; info?: unknown; } diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index f1053c7bb8411..b5c98fada07c4 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -18,9 +18,11 @@ */ import { Executor } from '../executor'; -import { ExpressionRendererRegistry } from '../expression_renderers'; +import { AnyExpressionRenderDefinition, ExpressionRendererRegistry } from '../expression_renderers'; import { ExpressionAstExpression } from '../ast'; import { ExecutionContract } from '../execution/execution_contract'; +import { AnyExpressionTypeDefinition } from '../expression_types'; +import { AnyExpressionFunctionDefinition } from '../expression_functions'; /** * The public contract that `ExpressionsService` provides to other plugins @@ -45,18 +47,88 @@ export type ExpressionsServiceSetup = Pick< * The public contract that `ExpressionsService` provides to other plugins * in Kibana Platform in *start* life-cycle. */ -export type ExpressionsServiceStart = Pick< - ExpressionsService, - | 'getFunction' - | 'getFunctions' - | 'getRenderer' - | 'getRenderers' - | 'getType' - | 'getTypes' - | 'run' - | 'execute' - | 'fork' ->; +export interface ExpressionsServiceStart { + /** + * Get a registered `ExpressionFunction` by its name, which was registered + * using the `registerFunction` method. The returned `ExpressionFunction` + * instance is an internal representation of the function in Expressions + * service - do not mutate that object. + */ + getFunction: (name: string) => ReturnType; + + /** + * Get a registered `ExpressionRenderer` by its name, which was registered + * using the `registerRenderer` method. The returned `ExpressionRenderer` + * instance is an internal representation of the renderer in Expressions + * service - do not mutate that object. + */ + getRenderer: (name: string) => ReturnType; + + /** + * Get a registered `ExpressionType` by its name, which was registered + * using the `registerType` method. The returned `ExpressionType` + * instance is an internal representation of the type in Expressions + * service - do not mutate that object. + */ + getType: (name: string) => ReturnType; + + /** + * Executes expression string or a parsed expression AST and immediately + * returns the result. + * + * Below example will execute `sleep 100 | clog` expression with `123` initial + * input to the first function. + * + * ```ts + * expressions.run('sleep 100 | clog', 123); + * ``` + * + * - `sleep 100` will delay execution by 100 milliseconds and pass the `123` input as + * its output. + * - `clog` will print to console `123` and pass it as its output. + * - The final result of the execution will be `123`. + * + * Optionally, you can pass an object as the third argument which will be used + * to extend the `ExecutionContext`—an object passed to each function + * as the third argument, that allows functions to perform side-effects. + * + * ```ts + * expressions.run('...', null, { elasticsearchClient }); + * ``` + */ + run: = Record>( + ast: string | ExpressionAstExpression, + input: Input, + context?: ExtraContext + ) => Promise; + + /** + * Starts expression execution and immediately returns `ExecutionContract` + * instance that tracks the progress of the execution and can be used to + * interact with the execution. + */ + execute: < + Input = unknown, + Output = unknown, + ExtraContext extends Record = Record + >( + ast: string | ExpressionAstExpression, + // This any is for legacy reasons. + input: Input, + context?: ExtraContext + ) => ExecutionContract; + + /** + * Create a new instance of `ExpressionsService`. The new instance inherits + * all state of the original `ExpressionsService`, including all expression + * types, expression functions and context. Also, all new types and functions + * registered in the original services AFTER the forking event will be + * available in the forked instance. However, all new types and functions + * registered in the forked instances will NOT be available to the original + * service. + */ + fork: () => ExpressionsService; +} export interface ExpressionServiceParams { executor?: Executor; @@ -127,58 +199,21 @@ export class ExpressionsService { * passed to all functions that can be used for side-effects. */ public readonly registerFunction = ( - ...args: Parameters - ): ReturnType => this.executor.registerFunction(...args); + functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition) + ): void => this.executor.registerFunction(functionDefinition); public readonly registerType = ( - ...args: Parameters - ): ReturnType => this.executor.registerType(...args); + typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition) + ): void => this.executor.registerType(typeDefinition); public readonly registerRenderer = ( - ...args: Parameters - ): ReturnType => this.renderers.register(...args); + definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition) + ): void => this.renderers.register(definition); - /** - * Executes expression string or a parsed expression AST and immediately - * returns the result. - * - * Below example will execute `sleep 100 | clog` expression with `123` initial - * input to the first function. - * - * ```ts - * expressions.run('sleep 100 | clog', 123); - * ``` - * - * - `sleep 100` will delay execution by 100 milliseconds and pass the `123` input as - * its output. - * - `clog` will print to console `123` and pass it as its output. - * - The final result of the execution will be `123`. - * - * Optionally, you can pass an object as the third argument which will be used - * to extend the `ExecutionContext`—an object passed to each function - * as the third argument, that allows functions to perform side-effects. - * - * ```ts - * expressions.run('...', null, { elasticsearchClient }); - * ``` - */ - public readonly run = < - Input, - Output, - ExtraContext extends Record = Record - >( - ast: string | ExpressionAstExpression, - input: Input, - context?: ExtraContext - ): Promise => this.executor.run(ast, input, context); + public readonly run: ExpressionsServiceStart['run'] = (ast, input, context) => + this.executor.run(ast, input, context); - /** - * Get a registered `ExpressionFunction` by its name, which was registered - * using the `registerFunction` method. The returned `ExpressionFunction` - * instance is an internal representation of the function in Expressions - * service - do not mutate that object. - */ - public readonly getFunction = (name: string): ReturnType => + public readonly getFunction: ExpressionsServiceStart['getFunction'] = (name) => this.executor.getFunction(name); /** @@ -188,13 +223,7 @@ export class ExpressionsService { public readonly getFunctions = (): ReturnType => this.executor.getFunctions(); - /** - * Get a registered `ExpressionRenderer` by its name, which was registered - * using the `registerRenderer` method. The returned `ExpressionRenderer` - * instance is an internal representation of the renderer in Expressions - * service - do not mutate that object. - */ - public readonly getRenderer = (name: string): ReturnType => + public readonly getRenderer: ExpressionsServiceStart['getRenderer'] = (name) => this.renderers.get(name); /** @@ -204,13 +233,7 @@ export class ExpressionsService { public readonly getRenderers = (): ReturnType => this.renderers.toJS(); - /** - * Get a registered `ExpressionType` by its name, which was registered - * using the `registerType` method. The returned `ExpressionType` - * instance is an internal representation of the type in Expressions - * service - do not mutate that object. - */ - public readonly getType = (name: string): ReturnType => + public readonly getType: ExpressionsServiceStart['getType'] = (name) => this.executor.getType(name); /** @@ -219,36 +242,13 @@ export class ExpressionsService { */ public readonly getTypes = (): ReturnType => this.executor.getTypes(); - /** - * Starts expression execution and immediately returns `ExecutionContract` - * instance that tracks the progress of the execution and can be used to - * interact with the execution. - */ - public readonly execute = < - Input = unknown, - Output = unknown, - ExtraContext extends Record = Record - >( - ast: string | ExpressionAstExpression, - // This any is for legacy reasons. - input: Input = { type: 'null' } as any, - context?: ExtraContext - ): ExecutionContract => { - const execution = this.executor.createExecution(ast, context); + public readonly execute: ExpressionsServiceStart['execute'] = ((ast, input, context) => { + const execution = this.executor.createExecution(ast, context); execution.start(input); return execution.contract; - }; + }) as ExpressionsServiceStart['execute']; - /** - * Create a new instance of `ExpressionsService`. The new instance inherits - * all state of the original `ExpressionsService`, including all expression - * types, expression functions and context. Also, all new types and functions - * registered in the original services AFTER the forking event will be - * available in the forked instance. However, all new types and functions - * registered in the forked instances will NOT be available to the original - * service. - */ - public readonly fork = (): ExpressionsService => { + public readonly fork = () => { const executor = this.executor.fork(); const renderers = this.renderers; const fork = new ExpressionsService({ executor, renderers }); diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 876e7dfec799c..9bdab74efd6f9 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -21,7 +21,7 @@ import { ExpressionValueError } from '../../common'; type ErrorLike = Partial>; -export const createError = (err: string | ErrorLike): ExpressionValueError => ({ +export const createError = (err: string | Error | ErrorLike): ExpressionValueError => ({ type: 'error', error: { stack: @@ -32,5 +32,6 @@ export const createError = (err: string | ErrorLike): ExpressionValueError => ({ : undefined, message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', + original: err instanceof Error ? err : undefined, }, }); diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 87406db89a2a8..039890c9233cf 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -122,4 +122,7 @@ export { TypeToString, UnmappedTypeStrings, ExpressionValueRender as Render, + ExpressionsService, + ExpressionsServiceSetup, + ExpressionsServiceStart, } from '../common'; diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index 3865b4d20620a..f6546a6b7ff35 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -49,11 +49,8 @@ const createStartContract = (): Start => { ExpressionRenderHandler: jest.fn(), fork: jest.fn(), getFunction: jest.fn(), - getFunctions: jest.fn(), getRenderer: jest.fn(), - getRenderers: jest.fn(), getType: jest.fn(), - getTypes: jest.fn(), loader: jest.fn(), ReactExpressionRenderer: jest.fn((props) => <>), render: jest.fn(), diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts index 08f7135f033f1..d9dde1f6def68 100644 --- a/src/plugins/expressions/public/plugin.test.ts +++ b/src/plugins/expressions/public/plugin.test.ts @@ -67,7 +67,7 @@ describe('ExpressionsPublicPlugin', () => { const { doStart } = await expressionsPluginMock.createPlugin(); const start = await doStart(); - const handler = start.execute('clog'); + const handler = start.execute('clog', null); expect(handler.getAst()).toMatchInlineSnapshot(` Object { "chain": Array [ @@ -85,7 +85,7 @@ describe('ExpressionsPublicPlugin', () => { test('"kibana" function return value of type "kibana_context"', async () => { const { doStart } = await expressionsPluginMock.createPlugin(); const start = await doStart(); - const execution = start.execute('kibana'); + const execution = start.execute('kibana', null); const result = await execution.getData(); expect((result as any).type).toBe('kibana_context'); diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts index 9768ece899dd4..4ad0e53cdd9c0 100644 --- a/src/plugins/expressions/public/plugin.ts +++ b/src/plugins/expressions/public/plugin.ts @@ -21,20 +21,26 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core import { ExpressionsService, ExpressionsServiceSetup, - ExpressionsServiceStart, ExecutionContext, + ExpressionsServiceStart, } from '../common'; import { setRenderersRegistry, setNotifications, setExpressionsService } from './services'; import { ReactExpressionRenderer } from './react_expression_renderer'; -import { ExpressionLoader, loader } from './loader'; +import { ExpressionLoader, IExpressionLoader, loader } from './loader'; import { render, ExpressionRenderHandler } from './render'; +/** + * Expressions public setup contract, extends {@link ExpressionsServiceSetup} + */ export type ExpressionsSetup = ExpressionsServiceSetup; +/** + * Expressions public start contrect, extends {@link ExpressionServiceStart} + */ export interface ExpressionsStart extends ExpressionsServiceStart { ExpressionLoader: typeof ExpressionLoader; ExpressionRenderHandler: typeof ExpressionRenderHandler; - loader: typeof loader; + loader: IExpressionLoader; ReactExpressionRenderer: typeof ReactExpressionRenderer; render: typeof render; } diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md new file mode 100644 index 0000000000000..162f0ef6824f5 --- /dev/null +++ b/src/plugins/expressions/public/public.api.md @@ -0,0 +1,1164 @@ +## API Report File for "kibana" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CoreSetup } from 'src/core/public'; +import { CoreStart } from 'src/core/public'; +import { Ensure } from '@kbn/utility-types'; +import { EnvironmentMode } from '@kbn/config'; +import { EventEmitter } from 'events'; +import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; +import { Plugin as Plugin_2 } from 'src/core/public'; +import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; +import React from 'react'; +import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; + +// Warning: (ae-missing-release-tag) "AnyExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type AnyExpressionFunctionDefinition = ExpressionFunctionDefinition, any>; + +// Warning: (ae-missing-release-tag) "AnyExpressionTypeDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AnyExpressionTypeDefinition = ExpressionTypeDefinition; + +// Warning: (ae-forgotten-export) The symbol "SingleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MultipleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UnresolvedSingleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UnresolvedMultipleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ArgumentType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ArgumentType = SingleArgumentType | MultipleArgumentType | UnresolvedSingleArgumentType | UnresolvedMultipleArgumentType; + +// Warning: (ae-missing-release-tag) "buildExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function buildExpression(initialState?: ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string): ExpressionAstExpressionBuilder; + +// Warning: (ae-forgotten-export) The symbol "InferFunctionDefinition" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FunctionArgs" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "buildExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function buildExpressionFunction(fnName: InferFunctionDefinition['name'], +initialArgs: { + [K in keyof FunctionArgs]: FunctionArgs[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[]; +}): ExpressionAstFunctionBuilder; + +// Warning: (ae-missing-release-tag) "Datatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Datatable { + // (undocumented) + columns: DatatableColumn[]; + // (undocumented) + rows: DatatableRow[]; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name; +} + +// Warning: (ae-missing-release-tag) "DatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface DatatableColumn { + // (undocumented) + id: string; + // Warning: (ae-forgotten-export) The symbol "DatatableColumnMeta" needs to be exported by the entry point index.d.ts + // + // (undocumented) + meta: DatatableColumnMeta; + // (undocumented) + name: string; +} + +// Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; + +// Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type DatatableRow = Record; + +// Warning: (ae-forgotten-export) The symbol "Adapters" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DefaultInspectorAdapters" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Execution" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class Execution = Record, Input = unknown, Output = unknown, InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object ? ExtraContext['inspectorAdapters'] : DefaultInspectorAdapters> { + constructor(params: ExecutionParams); + cancel(): void; + // (undocumented) + cast(value: any, toTypeNames?: string[]): any; + readonly context: ExecutionContext & ExtraContext; + readonly contract: ExecutionContract; + // (undocumented) + readonly expression: string; + input: Input; + // (undocumented) + get inspectorAdapters(): InspectorAdapters; + // Warning: (ae-forgotten-export) The symbol "ExpressionExecOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interpret(ast: ExpressionAstNode, input: T, options?: ExpressionExecOptions): Promise; + // (undocumented) + invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Promise; + // (undocumented) + invokeFunction(fn: ExpressionFunction, input: unknown, args: Record): Promise; + // (undocumented) + readonly params: ExecutionParams; + // (undocumented) + resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Promise; + // (undocumented) + get result(): Promise; + start(input?: Input): void; + readonly state: ExecutionContainer; +} + +// Warning: (ae-forgotten-export) The symbol "StateContainer" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutionPureTransitions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExecutionContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExecutionContainer = StateContainer, ExecutionPureTransitions>; + +// Warning: (ae-missing-release-tag) "ExecutionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExecutionContext { + abortSignal: AbortSignal; + getInitialInput: () => Input; + // Warning: (ae-forgotten-export) The symbol "SavedObjectAttributes" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "SavedObject" needs to be exported by the entry point index.d.ts + getSavedObject?: (type: string, id: string) => Promise>; + inspectorAdapters: InspectorAdapters; + // Warning: (ae-forgotten-export) The symbol "ExecutionContextSearch" needs to be exported by the entry point index.d.ts + search?: ExecutionContextSearch; + types: Record; + variables: Record; +} + +// Warning: (ae-missing-release-tag) "ExecutionContract" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class ExecutionContract = Record, Input = unknown, Output = unknown, InspectorAdapters = unknown> { + constructor(execution: Execution); + cancel: () => void; + // (undocumented) + protected readonly execution: Execution; + getAst: () => ExpressionAstExpression; + getData: () => Promise; + getExpression: () => string; + inspect: () => InspectorAdapters; + // (undocumented) + get isPending(): boolean; +} + +// Warning: (ae-missing-release-tag) "ExecutionParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutionParams = Record> { + // (undocumented) + ast?: ExpressionAstExpression; + // (undocumented) + context?: ExtraContext; + debug?: boolean; + // (undocumented) + executor: Executor; + // (undocumented) + expression?: string; +} + +// Warning: (ae-missing-release-tag) "ExecutionState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutionState extends ExecutorState { + // (undocumented) + ast: ExpressionAstExpression; + error?: Error; + result?: Output; + state: 'not-started' | 'pending' | 'result' | 'error'; +} + +// Warning: (ae-missing-release-tag) "Executor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class Executor = Record> { + constructor(state?: ExecutorState); + // (undocumented) + get context(): Record; + // (undocumented) + createExecution = Record, Input = unknown, Output = unknown>(ast: string | ExpressionAstExpression, context?: ExtraContext, { debug }?: ExpressionExecOptions): Execution; + // (undocumented) + static createWithDefaults = Record>(state?: ExecutorState): Executor; + // (undocumented) + extendContext(extraContext: Record): void; + // (undocumented) + fork(): Executor; + // @deprecated (undocumented) + readonly functions: FunctionsRegistry; + // (undocumented) + getFunction(name: string): ExpressionFunction | undefined; + // (undocumented) + getFunctions(): Record; + // (undocumented) + getType(name: string): ExpressionType | undefined; + // (undocumented) + getTypes(): Record; + // (undocumented) + registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; + // (undocumented) + registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; + run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; + // (undocumented) + readonly state: ExecutorContainer; + // @deprecated (undocumented) + readonly types: TypesRegistry; +} + +// Warning: (ae-forgotten-export) The symbol "ExecutorPureTransitions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutorPureSelectors" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExecutorContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExecutorContainer = Record> = StateContainer, ExecutorPureTransitions, ExecutorPureSelectors>; + +// Warning: (ae-missing-release-tag) "ExecutorState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutorState = Record> { + // (undocumented) + context: Context; + // (undocumented) + functions: Record; + // (undocumented) + types: Record; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstArgument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; + +// Warning: (ae-missing-release-tag) "ExpressionAstExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstExpression { + // (undocumented) + chain: ExpressionAstFunction[]; + // (undocumented) + type: 'expression'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstExpressionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstExpressionBuilder { + findFunction: (fnName: InferFunctionDefinition['name']) => Array> | []; + functions: ExpressionAstFunctionBuilder[]; + toAst: () => ExpressionAstExpression; + toString: () => string; + type: 'expression_builder'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstFunction { + // (undocumented) + arguments: Record; + // Warning: (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts + debug?: ExpressionAstFunctionDebug; + // (undocumented) + function: string; + // (undocumented) + type: 'function'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstFunctionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstFunctionBuilder { + // Warning: (ae-forgotten-export) The symbol "FunctionArgName" needs to be exported by the entry point index.d.ts + addArgument: >(name: A, value: FunctionArgs[A] | ExpressionAstExpressionBuilder) => this; + // Warning: (ae-forgotten-export) The symbol "FunctionBuilderArguments" needs to be exported by the entry point index.d.ts + arguments: FunctionBuilderArguments; + getArgument: >(name: A) => Array[A] | ExpressionAstExpressionBuilder> | undefined; + name: InferFunctionDefinition['name']; + // Warning: (ae-forgotten-export) The symbol "OptionalKeys" needs to be exported by the entry point index.d.ts + removeArgument: >>(name: A) => this; + replaceArgument: >(name: A, value: Array[A] | ExpressionAstExpressionBuilder>) => this; + toAst: () => ExpressionAstFunction; + toString: () => string; + type: 'expression_function_builder'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionAstNode = ExpressionAstExpression | ExpressionAstFunction | ExpressionAstArgument; + +// Warning: (ae-missing-release-tag) "ExpressionExecutor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public @deprecated (undocumented) +export interface ExpressionExecutor { + // Warning: (ae-forgotten-export) The symbol "ExpressionInterpreter" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interpreter: ExpressionInterpreter; +} + +// Warning: (ae-missing-release-tag) "ExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionFunction { + constructor(functionDefinition: AnyExpressionFunctionDefinition); + // (undocumented) + accepts: (type: string) => boolean; + aliases: string[]; + args: Record; + fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; + help: string; + inputTypes: string[] | undefined; + name: string; + type: string; +} + +// Warning: (ae-missing-release-tag) "ExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> { + aliases?: string[]; + args: { + [key in keyof Arguments]: ArgumentType; + }; + // @deprecated (undocumented) + context?: { + types: AnyExpressionFunctionDefinition['inputTypes']; + }; + fn(input: Input, args: Arguments, context: Context): Output; + help: string; + inputTypes?: Array>; + name: Name; + type?: TypeToString>; +} + +// @public +export interface ExpressionFunctionDefinitions { + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionClog" needs to be exported by the entry point index.d.ts + // + // (undocumented) + clog: ExpressionFunctionClog; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts + // + // (undocumented) + font: ExpressionFunctionFont; + // (undocumented) + kibana: ExpressionFunctionKibana; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionKibanaContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + kibana_context: ExpressionFunctionKibanaContext; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionTheme" needs to be exported by the entry point index.d.ts + // + // (undocumented) + theme: ExpressionFunctionTheme; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionVar" needs to be exported by the entry point index.d.ts + // + // (undocumented) + var: ExpressionFunctionVar; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionVarSet" needs to be exported by the entry point index.d.ts + // + // (undocumented) + var_set: ExpressionFunctionVarSet; +} + +// Warning: (ae-missing-release-tag) "ExpressionFunctionKibana" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionFunctionKibana = ExpressionFunctionDefinition<'kibana', ExpressionValueSearchContext | null, object, ExpressionValueSearchContext>; + +// Warning: (ae-missing-release-tag) "ExpressionFunctionParameter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionFunctionParameter { + constructor(name: string, arg: ArgumentType); + // (undocumented) + accepts(type: string): boolean; + // (undocumented) + aliases: string[]; + // (undocumented) + default: any; + // (undocumented) + help: string; + // (undocumented) + multi: boolean; + // (undocumented) + name: string; + // (undocumented) + options: any[]; + // (undocumented) + required: boolean; + // (undocumented) + resolve: boolean; + // (undocumented) + types: string[]; +} + +// Warning: (ae-missing-release-tag) "ExpressionImage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionImage { + // (undocumented) + dataurl: string; + // (undocumented) + mode: string; + // (undocumented) + type: 'image'; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionRenderDefinition { + displayName: string; + help?: string; + name: string; + render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; + reuseDomNode: boolean; + validate?: () => undefined | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionRenderer { + constructor(config: ExpressionRenderDefinition); + // (undocumented) + readonly displayName: string; + // (undocumented) + readonly help: string; + // (undocumented) + readonly name: string; + // (undocumented) + readonly render: ExpressionRenderDefinition['render']; + // (undocumented) + readonly reuseDomNode: boolean; + // (undocumented) + readonly validate: () => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionRendererComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionRendererComponent = React.FC; + +// Warning: (ae-missing-release-tag) "ExpressionRendererEvent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionRendererEvent { + // (undocumented) + data: any; + // (undocumented) + name: string; +} + +// Warning: (ae-missing-release-tag) "ExpressionRendererRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionRendererRegistry implements IRegistry { + // (undocumented) + get(id: string): ExpressionRenderer | null; + // Warning: (ae-forgotten-export) The symbol "AnyExpressionRenderDefinition" needs to be exported by the entry point index.d.ts + // + // (undocumented) + register(definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)): void; + // (undocumented) + toArray(): ExpressionRenderer[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionRenderError extends Error { + // (undocumented) + type?: string; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderHandler" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionRenderHandler { + // Warning: (ae-forgotten-export) The symbol "ExpressionRenderHandlerParams" needs to be exported by the entry point index.d.ts + constructor(element: HTMLElement, { onRenderError }?: Partial); + // (undocumented) + destroy: () => void; + // (undocumented) + events$: Observable; + // (undocumented) + getElement: () => HTMLElement; + // (undocumented) + handleRenderError: (error: ExpressionRenderError) => void; + // (undocumented) + render$: Observable; + // (undocumented) + render: (data: any, uiState?: any) => Promise; + // Warning: (ae-forgotten-export) The symbol "UpdateValue" needs to be exported by the entry point index.d.ts + // + // (undocumented) + update$: Observable; + } + +// Warning: (ae-missing-release-tag) "ExpressionsPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +class ExpressionsPublicPlugin implements Plugin_2 { + constructor(initializerContext: PluginInitializerContext_2); + // (undocumented) + setup(core: CoreSetup): ExpressionsSetup; + // (undocumented) + start(core: CoreStart): ExpressionsStart; + // (undocumented) + stop(): void; +} + +export { ExpressionsPublicPlugin } + +export { ExpressionsPublicPlugin as Plugin } + +// Warning: (ae-missing-release-tag) "ExpressionsService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export class ExpressionsService { + // Warning: (ae-forgotten-export) The symbol "ExpressionServiceParams" needs to be exported by the entry point index.d.ts + constructor({ executor, renderers, }?: ExpressionServiceParams); + // (undocumented) + readonly execute: ExpressionsServiceStart['execute']; + // (undocumented) + readonly executor: Executor; + // (undocumented) + readonly fork: () => ExpressionsService; + // (undocumented) + readonly getFunction: ExpressionsServiceStart['getFunction']; + readonly getFunctions: () => ReturnType; + // (undocumented) + readonly getRenderer: ExpressionsServiceStart['getRenderer']; + readonly getRenderers: () => ReturnType; + // (undocumented) + readonly getType: ExpressionsServiceStart['getType']; + readonly getTypes: () => ReturnType; + readonly registerFunction: (functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)) => void; + // (undocumented) + readonly registerRenderer: (definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)) => void; + // (undocumented) + readonly registerType: (typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)) => void; + // (undocumented) + readonly renderers: ExpressionRendererRegistry; + // (undocumented) + readonly run: ExpressionsServiceStart['run']; + setup(): ExpressionsServiceSetup; + start(): ExpressionsServiceStart; + // (undocumented) + stop(): void; +} + +// Warning: (ae-missing-release-tag) "ExpressionsServiceSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ExpressionsServiceSetup = Pick; + +// Warning: (ae-missing-release-tag) "ExpressionsServiceStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionsServiceStart { + execute: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => ExecutionContract; + fork: () => ExpressionsService; + getFunction: (name: string) => ReturnType; + getRenderer: (name: string) => ReturnType; + getType: (name: string) => ReturnType; + run: = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext) => Promise; +} + +// Warning: (ae-missing-release-tag) "ExpressionsSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ExpressionsSetup = ExpressionsServiceSetup; + +// Warning: (ae-missing-release-tag) "ExpressionsStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "ExpressionServiceStart" +// +// @public +export interface ExpressionsStart extends ExpressionsServiceStart { + // Warning: (ae-forgotten-export) The symbol "ExpressionLoader" needs to be exported by the entry point index.d.ts + // + // (undocumented) + ExpressionLoader: typeof ExpressionLoader; + // (undocumented) + ExpressionRenderHandler: typeof ExpressionRenderHandler; + // Warning: (ae-forgotten-export) The symbol "IExpressionLoader" needs to be exported by the entry point index.d.ts + // + // (undocumented) + loader: IExpressionLoader; + // (undocumented) + ReactExpressionRenderer: typeof ReactExpressionRenderer; + // Warning: (ae-forgotten-export) The symbol "render" needs to be exported by the entry point index.d.ts + // + // (undocumented) + render: typeof render; +} + +// Warning: (ae-missing-release-tag) "ExpressionType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionType { + constructor(definition: AnyExpressionTypeDefinition); + // (undocumented) + castsFrom: (value: ExpressionValue) => boolean; + // (undocumented) + castsTo: (value: ExpressionValue) => boolean; + // (undocumented) + create: unknown; + // (undocumented) + deserialize?: (serialized: any) => ExpressionValue; + // (undocumented) + from: (value: ExpressionValue, types: Record) => any; + // (undocumented) + getFromFn: (typeName: string) => undefined | ExpressionValueConverter; + // (undocumented) + getToFn: (typeName: string) => undefined | ExpressionValueConverter; + help: string; + // (undocumented) + name: string; + serialize?: (value: ExpressionValue) => any; + // (undocumented) + to: (value: ExpressionValue, toTypeName: string, types: Record) => any; + validate: (type: any) => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionTypeDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionTypeDefinition { + // (undocumented) + deserialize?: (type: SerializedType) => Value; + // (undocumented) + from?: { + [type: string]: ExpressionValueConverter; + }; + // (undocumented) + help?: string; + // (undocumented) + name: Name; + // (undocumented) + serialize?: (type: Value) => SerializedType; + // (undocumented) + to?: { + [type: string]: ExpressionValueConverter; + }; + // (undocumented) + validate?: (type: any) => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionTypeStyle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionTypeStyle { + // (undocumented) + css: string; + // Warning: (ae-forgotten-export) The symbol "CSSStyle" needs to be exported by the entry point index.d.ts + // + // (undocumented) + spec: CSSStyle; + // (undocumented) + type: 'style'; +} + +// Warning: (ae-missing-release-tag) "ExpressionValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValue = ExpressionValueUnboxed | ExpressionValueBoxed; + +// Warning: (ae-missing-release-tag) "ExpressionValueBoxed" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueBoxed = { + type: Type; +} & Value; + +// Warning: (ae-missing-release-tag) "ExpressionValueConverter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueConverter = (input: I, availableTypes: Record) => O; + +// Warning: (ae-missing-release-tag) "ExpressionValueError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueError = ExpressionValueBoxed<'error', { + error: { + message: string; + type?: string; + name?: string; + stack?: string; + original?: Error; + }; + info?: unknown; +}>; + +// Warning: (ae-missing-release-tag) "ExpressionValueFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ExpressionValueFilter = ExpressionValueBoxed<'filter', { + filterType?: string; + value?: string; + column?: string; + and: ExpressionValueFilter[]; + to?: string; + from?: string; + query?: string | null; +}>; + +// Warning: (ae-missing-release-tag) "ExpressionValueNum" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueNum = ExpressionValueBoxed<'num', { + value: number; +}>; + +// Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExpressionValueRender" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +type ExpressionValueRender = ExpressionValueBoxed; + +export { ExpressionValueRender } + +export { ExpressionValueRender as Render } + +// Warning: (ae-missing-release-tag) "ExpressionValueSearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueSearchContext = ExpressionValueBoxed<'kibana_context', ExecutionContextSearch>; + +// Warning: (ae-missing-release-tag) "ExpressionValueUnboxed" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueUnboxed = any; + +// Warning: (ae-missing-release-tag) "Font" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Font { + // (undocumented) + label: FontLabel; + // (undocumented) + value: FontValue; +} + +// Warning: (ae-forgotten-export) The symbol "fonts" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "FontLabel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type FontLabel = typeof fonts[number]['label']; + +// Warning: (ae-missing-release-tag) "FontStyle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum FontStyle { + // (undocumented) + ITALIC = "italic", + // (undocumented) + NORMAL = "normal" +} + +// Warning: (ae-missing-release-tag) "FontValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type FontValue = typeof fonts[number]['value']; + +// Warning: (ae-missing-release-tag) "FontWeight" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum FontWeight { + // (undocumented) + BOLD = "bold", + // (undocumented) + BOLDER = "bolder", + // (undocumented) + EIGHT = "800", + // (undocumented) + FIVE = "500", + // (undocumented) + FOUR = "400", + // (undocumented) + LIGHTER = "lighter", + // (undocumented) + NINE = "900", + // (undocumented) + NORMAL = "normal", + // (undocumented) + ONE = "100", + // (undocumented) + SEVEN = "700", + // (undocumented) + SIX = "600", + // (undocumented) + THREE = "300", + // (undocumented) + TWO = "200" +} + +// Warning: (ae-missing-release-tag) "format" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function format(ast: T, type: T extends ExpressionAstExpression ? 'expression' : 'argument'): string; + +// Warning: (ae-missing-release-tag) "formatExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function formatExpression(ast: ExpressionAstExpression): string; + +// Warning: (ae-missing-release-tag) "FunctionsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class FunctionsRegistry implements IRegistry { + constructor(executor: Executor); + // (undocumented) + get(id: string): ExpressionFunction | null; + // (undocumented) + register(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; + // (undocumented) + toArray(): ExpressionFunction[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "IExpressionLoaderParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IExpressionLoaderParams { + // (undocumented) + context?: ExpressionValue; + // (undocumented) + customFunctions?: []; + // (undocumented) + customRenderers?: []; + // (undocumented) + disableCaching?: boolean; + // (undocumented) + inspectorAdapters?: Adapters; + // Warning: (ae-forgotten-export) The symbol "RenderErrorHandlerFnType" needs to be exported by the entry point index.d.ts + // + // (undocumented) + onRenderError?: RenderErrorHandlerFnType; + // (undocumented) + searchContext?: ExecutionContextSearch; + // (undocumented) + uiState?: unknown; + // (undocumented) + variables?: Record; +} + +// Warning: (ae-missing-release-tag) "IInterpreterRenderHandlers" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IInterpreterRenderHandlers { + done: () => void; + // (undocumented) + event: (event: any) => void; + // (undocumented) + onDestroy: (fn: () => void) => void; + // (undocumented) + reload: () => void; + // (undocumented) + update: (params: any) => void; +} + +// Warning: (ae-missing-release-tag) "InterpreterErrorType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public @deprecated (undocumented) +export type InterpreterErrorType = ExpressionValueError; + +// Warning: (ae-missing-release-tag) "IRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IRegistry { + // (undocumented) + get(id: string): T | null; + // (undocumented) + toArray(): T[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "isExpressionAstBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function isExpressionAstBuilder(val: any): val is ExpressionAstExpressionBuilder; + +// Warning: (ae-missing-release-tag) "KIBANA_CONTEXT_NAME" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type KIBANA_CONTEXT_NAME = 'kibana_context'; + +// Warning: (ae-missing-release-tag) "KibanaContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type KibanaContext = ExpressionValueSearchContext; + +// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatable { + // (undocumented) + columns: KibanaDatatableColumn[]; + // (undocumented) + rows: KibanaDatatableRow[]; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name_3; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableColumn { + // (undocumented) + formatHint?: SerializedFieldFormat; + // (undocumented) + id: string; + // (undocumented) + meta?: KibanaDatatableColumnMeta; + // (undocumented) + name: string; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableColumnMeta { + // (undocumented) + aggConfigParams?: Record; + // (undocumented) + indexPatternId?: string; + // (undocumented) + type: string; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableRow { + // (undocumented) + [key: string]: unknown; +} + +// Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type KnownTypeToString = T extends string ? 'string' : T extends boolean ? 'boolean' : T extends number ? 'number' : T extends null ? 'null' : T extends { + type: string; +} ? T['type'] : never; + +// Warning: (ae-missing-release-tag) "Overflow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum Overflow { + // (undocumented) + AUTO = "auto", + // (undocumented) + HIDDEN = "hidden", + // (undocumented) + SCROLL = "scroll", + // (undocumented) + VISIBLE = "visible" +} + +// Warning: (ae-missing-release-tag) "parse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function parse(expression: E, startRule: S): S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument; + +// Warning: (ae-missing-release-tag) "parseExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function parseExpression(expression: string): ExpressionAstExpression; + +// Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function plugin(initializerContext: PluginInitializerContext): ExpressionsPublicPlugin; + +// Warning: (ae-missing-release-tag) "PointSeries" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeries = ExpressionValueBoxed<'pointseries', { + columns: PointSeriesColumns; + rows: PointSeriesRow[]; +}>; + +// Warning: (ae-missing-release-tag) "PointSeriesColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface PointSeriesColumn { + // (undocumented) + expression: string; + // (undocumented) + role: 'measure' | 'dimension'; + // (undocumented) + type: 'number' | 'string'; +} + +// Warning: (ae-missing-release-tag) "PointSeriesColumnName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeriesColumnName = 'x' | 'y' | 'color' | 'size' | 'text'; + +// Warning: (ae-missing-release-tag) "PointSeriesColumns" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeriesColumns = Record | {}; + +// Warning: (ae-missing-release-tag) "PointSeriesRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PointSeriesRow = Record; + +// Warning: (ae-missing-release-tag) "Range" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface Range { + // (undocumented) + from: number; + // (undocumented) + to: number; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name_4; +} + +// Warning: (ae-missing-release-tag) "ReactExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ReactExpressionRenderer: ({ className, dataAttrs, padding, renderError, expression, onEvent, reload$, ...expressionLoaderOptions }: ReactExpressionRendererProps) => JSX.Element; + +// Warning: (ae-missing-release-tag) "ReactExpressionRendererProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ReactExpressionRendererProps extends IExpressionLoaderParams { + // (undocumented) + className?: string; + // (undocumented) + dataAttrs?: string[]; + // (undocumented) + expression: string | ExpressionAstExpression; + // (undocumented) + onEvent?: (event: ExpressionRendererEvent) => void; + // (undocumented) + padding?: 'xs' | 's' | 'm' | 'l' | 'xl'; + reload$?: Observable; + // (undocumented) + renderError?: (error?: string | null) => React.ReactElement | React.ReactElement[]; +} + +// Warning: (ae-missing-release-tag) "ReactExpressionRendererType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ReactExpressionRendererType = React.ComponentType; + +// Warning: (ae-missing-release-tag) "SerializedDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SerializedDatatable extends Datatable { + // (undocumented) + rows: string[][]; +} + +// Warning: (ae-missing-release-tag) "SerializedFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface SerializedFieldFormat> { + // (undocumented) + id?: string; + // (undocumented) + params?: TParams; +} + +// Warning: (ae-missing-release-tag) "Style" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Style = ExpressionTypeStyle; + +// Warning: (ae-missing-release-tag) "TextAlignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum TextAlignment { + // (undocumented) + CENTER = "center", + // (undocumented) + JUSTIFY = "justify", + // (undocumented) + LEFT = "left", + // (undocumented) + RIGHT = "right" +} + +// Warning: (ae-missing-release-tag) "TextDecoration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum TextDecoration { + // (undocumented) + NONE = "none", + // (undocumented) + UNDERLINE = "underline" +} + +// Warning: (ae-missing-release-tag) "TypesRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class TypesRegistry implements IRegistry { + constructor(executor: Executor); + // (undocumented) + get(id: string): ExpressionType | null; + // (undocumented) + register(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; + // (undocumented) + toArray(): ExpressionType[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "TypeString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type TypeString = KnownTypeToString>; + +// Warning: (ae-missing-release-tag) "TypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type TypeToString = KnownTypeToString | UnmappedTypeStrings; + +// Warning: (ae-missing-release-tag) "UnmappedTypeStrings" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type UnmappedTypeStrings = 'date' | 'filter'; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/src/plugins/expressions/server/mocks.ts b/src/plugins/expressions/server/mocks.ts index 0512789d76ab0..0beb4750c0d10 100644 --- a/src/plugins/expressions/server/mocks.ts +++ b/src/plugins/expressions/server/mocks.ts @@ -46,11 +46,8 @@ const createStartContract = (): Start => { execute: jest.fn(), fork: jest.fn(), getFunction: jest.fn(), - getFunctions: jest.fn(), getRenderer: jest.fn(), - getRenderers: jest.fn(), getType: jest.fn(), - getTypes: jest.fn(), run: jest.fn(), }; diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md new file mode 100644 index 0000000000000..6ac251ea005b4 --- /dev/null +++ b/src/plugins/expressions/server/server.api.md @@ -0,0 +1,968 @@ +## API Report File for "kibana" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CoreSetup } from 'src/core/server'; +import { CoreStart } from 'src/core/server'; +import { Ensure } from '@kbn/utility-types'; +import { EventEmitter } from 'events'; +import { Observable } from 'rxjs'; +import { Plugin as Plugin_2 } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; +import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; + +// Warning: (ae-missing-release-tag) "AnyExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type AnyExpressionFunctionDefinition = ExpressionFunctionDefinition, any>; + +// Warning: (ae-missing-release-tag) "AnyExpressionTypeDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AnyExpressionTypeDefinition = ExpressionTypeDefinition; + +// Warning: (ae-forgotten-export) The symbol "SingleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "MultipleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UnresolvedSingleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UnresolvedMultipleArgumentType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ArgumentType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ArgumentType = SingleArgumentType | MultipleArgumentType | UnresolvedSingleArgumentType | UnresolvedMultipleArgumentType; + +// Warning: (ae-missing-release-tag) "buildExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function buildExpression(initialState?: ExpressionAstFunctionBuilder[] | ExpressionAstExpression | string): ExpressionAstExpressionBuilder; + +// Warning: (ae-forgotten-export) The symbol "InferFunctionDefinition" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FunctionArgs" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "buildExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function buildExpressionFunction(fnName: InferFunctionDefinition['name'], +initialArgs: { + [K in keyof FunctionArgs]: FunctionArgs[K] | ExpressionAstExpressionBuilder | ExpressionAstExpressionBuilder[]; +}): ExpressionAstFunctionBuilder; + +// Warning: (ae-missing-release-tag) "Datatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Datatable { + // (undocumented) + columns: DatatableColumn[]; + // (undocumented) + rows: DatatableRow[]; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name; +} + +// Warning: (ae-missing-release-tag) "DatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface DatatableColumn { + // (undocumented) + id: string; + // Warning: (ae-forgotten-export) The symbol "DatatableColumnMeta" needs to be exported by the entry point index.d.ts + // + // (undocumented) + meta: DatatableColumnMeta; + // (undocumented) + name: string; +} + +// Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; + +// Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type DatatableRow = Record; + +// Warning: (ae-forgotten-export) The symbol "Adapters" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DefaultInspectorAdapters" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Execution" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class Execution = Record, Input = unknown, Output = unknown, InspectorAdapters extends Adapters = ExtraContext['inspectorAdapters'] extends object ? ExtraContext['inspectorAdapters'] : DefaultInspectorAdapters> { + constructor(params: ExecutionParams); + cancel(): void; + // (undocumented) + cast(value: any, toTypeNames?: string[]): any; + readonly context: ExecutionContext & ExtraContext; + // Warning: (ae-forgotten-export) The symbol "ExecutionContract" needs to be exported by the entry point index.d.ts + readonly contract: ExecutionContract; + // (undocumented) + readonly expression: string; + input: Input; + // (undocumented) + get inspectorAdapters(): InspectorAdapters; + // Warning: (ae-forgotten-export) The symbol "ExpressionExecOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + interpret(ast: ExpressionAstNode, input: T, options?: ExpressionExecOptions): Promise; + // (undocumented) + invokeChain(chainArr: ExpressionAstFunction[], input: unknown): Promise; + // (undocumented) + invokeFunction(fn: ExpressionFunction, input: unknown, args: Record): Promise; + // (undocumented) + readonly params: ExecutionParams; + // (undocumented) + resolveArgs(fnDef: ExpressionFunction, input: unknown, argAsts: any): Promise; + // (undocumented) + get result(): Promise; + start(input?: Input): void; + readonly state: ExecutionContainer; +} + +// Warning: (ae-forgotten-export) The symbol "StateContainer" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutionPureTransitions" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExecutionContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExecutionContainer = StateContainer, ExecutionPureTransitions>; + +// Warning: (ae-missing-release-tag) "ExecutionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExecutionContext { + abortSignal: AbortSignal; + getInitialInput: () => Input; + // Warning: (ae-forgotten-export) The symbol "SavedObjectAttributes" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "SavedObject" needs to be exported by the entry point index.d.ts + getSavedObject?: (type: string, id: string) => Promise>; + inspectorAdapters: InspectorAdapters; + // Warning: (ae-forgotten-export) The symbol "ExecutionContextSearch" needs to be exported by the entry point index.d.ts + search?: ExecutionContextSearch; + types: Record; + variables: Record; +} + +// Warning: (ae-missing-release-tag) "ExecutionParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutionParams = Record> { + // (undocumented) + ast?: ExpressionAstExpression; + // (undocumented) + context?: ExtraContext; + debug?: boolean; + // (undocumented) + executor: Executor; + // (undocumented) + expression?: string; +} + +// Warning: (ae-missing-release-tag) "ExecutionState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutionState extends ExecutorState { + // (undocumented) + ast: ExpressionAstExpression; + error?: Error; + result?: Output; + state: 'not-started' | 'pending' | 'result' | 'error'; +} + +// Warning: (ae-missing-release-tag) "Executor" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class Executor = Record> { + constructor(state?: ExecutorState); + // (undocumented) + get context(): Record; + // (undocumented) + createExecution = Record, Input = unknown, Output = unknown>(ast: string | ExpressionAstExpression, context?: ExtraContext, { debug }?: ExpressionExecOptions): Execution; + // (undocumented) + static createWithDefaults = Record>(state?: ExecutorState): Executor; + // (undocumented) + extendContext(extraContext: Record): void; + // (undocumented) + fork(): Executor; + // @deprecated (undocumented) + readonly functions: FunctionsRegistry; + // (undocumented) + getFunction(name: string): ExpressionFunction | undefined; + // (undocumented) + getFunctions(): Record; + // (undocumented) + getType(name: string): ExpressionType | undefined; + // (undocumented) + getTypes(): Record; + // (undocumented) + registerFunction(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; + // (undocumented) + registerType(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; + run = Record>(ast: string | ExpressionAstExpression, input: Input, context?: ExtraContext): Promise; + // (undocumented) + readonly state: ExecutorContainer; + // @deprecated (undocumented) + readonly types: TypesRegistry; +} + +// Warning: (ae-forgotten-export) The symbol "ExecutorPureTransitions" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "ExecutorPureSelectors" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExecutorContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExecutorContainer = Record> = StateContainer, ExecutorPureTransitions, ExecutorPureSelectors>; + +// Warning: (ae-missing-release-tag) "ExecutorState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExecutorState = Record> { + // (undocumented) + context: Context; + // (undocumented) + functions: Record; + // (undocumented) + types: Record; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstArgument" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionAstArgument = string | boolean | number | ExpressionAstExpression; + +// Warning: (ae-missing-release-tag) "ExpressionAstExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstExpression { + // (undocumented) + chain: ExpressionAstFunction[]; + // (undocumented) + type: 'expression'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstExpressionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstExpressionBuilder { + findFunction: (fnName: InferFunctionDefinition['name']) => Array> | []; + functions: ExpressionAstFunctionBuilder[]; + toAst: () => ExpressionAstExpression; + toString: () => string; + type: 'expression_builder'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstFunction { + // (undocumented) + arguments: Record; + // Warning: (ae-forgotten-export) The symbol "ExpressionAstFunctionDebug" needs to be exported by the entry point index.d.ts + debug?: ExpressionAstFunctionDebug; + // (undocumented) + function: string; + // (undocumented) + type: 'function'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstFunctionBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionAstFunctionBuilder { + // Warning: (ae-forgotten-export) The symbol "FunctionArgName" needs to be exported by the entry point index.d.ts + addArgument: >(name: A, value: FunctionArgs[A] | ExpressionAstExpressionBuilder) => this; + // Warning: (ae-forgotten-export) The symbol "FunctionBuilderArguments" needs to be exported by the entry point index.d.ts + arguments: FunctionBuilderArguments; + getArgument: >(name: A) => Array[A] | ExpressionAstExpressionBuilder> | undefined; + name: InferFunctionDefinition['name']; + // Warning: (ae-forgotten-export) The symbol "OptionalKeys" needs to be exported by the entry point index.d.ts + removeArgument: >>(name: A) => this; + replaceArgument: >(name: A, value: Array[A] | ExpressionAstExpressionBuilder>) => this; + toAst: () => ExpressionAstFunction; + toString: () => string; + type: 'expression_function_builder'; +} + +// Warning: (ae-missing-release-tag) "ExpressionAstNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionAstNode = ExpressionAstExpression | ExpressionAstFunction | ExpressionAstArgument; + +// Warning: (ae-missing-release-tag) "ExpressionFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionFunction { + constructor(functionDefinition: AnyExpressionFunctionDefinition); + // (undocumented) + accepts: (type: string) => boolean; + aliases: string[]; + args: Record; + fn: (input: ExpressionValue, params: Record, handlers: object) => ExpressionValue; + help: string; + inputTypes: string[] | undefined; + name: string; + type: string; +} + +// Warning: (ae-missing-release-tag) "ExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionFunctionDefinition, Output, Context extends ExecutionContext = ExecutionContext> { + aliases?: string[]; + args: { + [key in keyof Arguments]: ArgumentType; + }; + // @deprecated (undocumented) + context?: { + types: AnyExpressionFunctionDefinition['inputTypes']; + }; + fn(input: Input, args: Arguments, context: Context): Output; + help: string; + inputTypes?: Array>; + name: Name; + type?: TypeToString>; +} + +// @public +export interface ExpressionFunctionDefinitions { + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionClog" needs to be exported by the entry point index.d.ts + // + // (undocumented) + clog: ExpressionFunctionClog; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionFont" needs to be exported by the entry point index.d.ts + // + // (undocumented) + font: ExpressionFunctionFont; + // (undocumented) + kibana: ExpressionFunctionKibana; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionKibanaContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + kibana_context: ExpressionFunctionKibanaContext; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionTheme" needs to be exported by the entry point index.d.ts + // + // (undocumented) + theme: ExpressionFunctionTheme; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionVar" needs to be exported by the entry point index.d.ts + // + // (undocumented) + var: ExpressionFunctionVar; + // Warning: (ae-forgotten-export) The symbol "ExpressionFunctionVarSet" needs to be exported by the entry point index.d.ts + // + // (undocumented) + var_set: ExpressionFunctionVarSet; +} + +// Warning: (ae-missing-release-tag) "ExpressionFunctionKibana" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionFunctionKibana = ExpressionFunctionDefinition<'kibana', ExpressionValueSearchContext | null, object, ExpressionValueSearchContext>; + +// Warning: (ae-missing-release-tag) "ExpressionFunctionParameter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionFunctionParameter { + constructor(name: string, arg: ArgumentType); + // (undocumented) + accepts(type: string): boolean; + // (undocumented) + aliases: string[]; + // (undocumented) + default: any; + // (undocumented) + help: string; + // (undocumented) + multi: boolean; + // (undocumented) + name: string; + // (undocumented) + options: any[]; + // (undocumented) + required: boolean; + // (undocumented) + resolve: boolean; + // (undocumented) + types: string[]; +} + +// Warning: (ae-missing-release-tag) "ExpressionImage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionImage { + // (undocumented) + dataurl: string; + // (undocumented) + mode: string; + // (undocumented) + type: 'image'; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ExpressionRenderDefinition { + displayName: string; + help?: string; + name: string; + render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; + reuseDomNode: boolean; + validate?: () => undefined | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionRenderer { + constructor(config: ExpressionRenderDefinition); + // (undocumented) + readonly displayName: string; + // (undocumented) + readonly help: string; + // (undocumented) + readonly name: string; + // (undocumented) + readonly render: ExpressionRenderDefinition['render']; + // (undocumented) + readonly reuseDomNode: boolean; + // (undocumented) + readonly validate: () => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionRendererRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionRendererRegistry implements IRegistry { + // (undocumented) + get(id: string): ExpressionRenderer | null; + // Warning: (ae-forgotten-export) The symbol "AnyExpressionRenderDefinition" needs to be exported by the entry point index.d.ts + // + // (undocumented) + register(definition: AnyExpressionRenderDefinition | (() => AnyExpressionRenderDefinition)): void; + // (undocumented) + toArray(): ExpressionRenderer[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "ExpressionsServerPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +class ExpressionsServerPlugin implements Plugin_2 { + constructor(initializerContext: PluginInitializerContext); + // Warning: (ae-forgotten-export) The symbol "ExpressionsService" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly expressions: ExpressionsService; + // (undocumented) + setup(core: CoreSetup): ExpressionsServerSetup; + // (undocumented) + start(core: CoreStart): ExpressionsServerStart; + // (undocumented) + stop(): void; +} + +export { ExpressionsServerPlugin } + +export { ExpressionsServerPlugin as Plugin } + +// Warning: (ae-forgotten-export) The symbol "ExpressionsServiceSetup" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExpressionsServerSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionsServerSetup = ExpressionsServiceSetup; + +// Warning: (ae-forgotten-export) The symbol "ExpressionsServiceStart" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExpressionsServerStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionsServerStart = ExpressionsServiceStart; + +// Warning: (ae-missing-release-tag) "ExpressionType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionType { + constructor(definition: AnyExpressionTypeDefinition); + // (undocumented) + castsFrom: (value: ExpressionValue) => boolean; + // (undocumented) + castsTo: (value: ExpressionValue) => boolean; + // (undocumented) + create: unknown; + // (undocumented) + deserialize?: (serialized: any) => ExpressionValue; + // (undocumented) + from: (value: ExpressionValue, types: Record) => any; + // (undocumented) + getFromFn: (typeName: string) => undefined | ExpressionValueConverter; + // (undocumented) + getToFn: (typeName: string) => undefined | ExpressionValueConverter; + help: string; + // (undocumented) + name: string; + serialize?: (value: ExpressionValue) => any; + // (undocumented) + to: (value: ExpressionValue, toTypeName: string, types: Record) => any; + validate: (type: any) => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionTypeDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionTypeDefinition { + // (undocumented) + deserialize?: (type: SerializedType) => Value; + // (undocumented) + from?: { + [type: string]: ExpressionValueConverter; + }; + // (undocumented) + help?: string; + // (undocumented) + name: Name; + // (undocumented) + serialize?: (type: Value) => SerializedType; + // (undocumented) + to?: { + [type: string]: ExpressionValueConverter; + }; + // (undocumented) + validate?: (type: any) => void | Error; +} + +// Warning: (ae-missing-release-tag) "ExpressionTypeStyle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ExpressionTypeStyle { + // (undocumented) + css: string; + // Warning: (ae-forgotten-export) The symbol "CSSStyle" needs to be exported by the entry point index.d.ts + // + // (undocumented) + spec: CSSStyle; + // (undocumented) + type: 'style'; +} + +// Warning: (ae-missing-release-tag) "ExpressionValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValue = ExpressionValueUnboxed | ExpressionValueBoxed; + +// Warning: (ae-missing-release-tag) "ExpressionValueBoxed" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueBoxed = { + type: Type; +} & Value; + +// Warning: (ae-missing-release-tag) "ExpressionValueConverter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueConverter = (input: I, availableTypes: Record) => O; + +// Warning: (ae-missing-release-tag) "ExpressionValueError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueError = ExpressionValueBoxed<'error', { + error: { + message: string; + type?: string; + name?: string; + stack?: string; + original?: Error; + }; + info?: unknown; +}>; + +// Warning: (ae-missing-release-tag) "ExpressionValueFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ExpressionValueFilter = ExpressionValueBoxed<'filter', { + filterType?: string; + value?: string; + column?: string; + and: ExpressionValueFilter[]; + to?: string; + from?: string; + query?: string | null; +}>; + +// Warning: (ae-missing-release-tag) "ExpressionValueNum" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueNum = ExpressionValueBoxed<'num', { + value: number; +}>; + +// Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ExpressionValueRender" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +type ExpressionValueRender = ExpressionValueBoxed; + +export { ExpressionValueRender } + +export { ExpressionValueRender as Render } + +// Warning: (ae-missing-release-tag) "ExpressionValueSearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueSearchContext = ExpressionValueBoxed<'kibana_context', ExecutionContextSearch>; + +// Warning: (ae-missing-release-tag) "ExpressionValueUnboxed" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ExpressionValueUnboxed = any; + +// Warning: (ae-missing-release-tag) "Font" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Font { + // (undocumented) + label: FontLabel; + // (undocumented) + value: FontValue; +} + +// Warning: (ae-forgotten-export) The symbol "fonts" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "FontLabel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type FontLabel = typeof fonts[number]['label']; + +// Warning: (ae-missing-release-tag) "FontStyle" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum FontStyle { + // (undocumented) + ITALIC = "italic", + // (undocumented) + NORMAL = "normal" +} + +// Warning: (ae-missing-release-tag) "FontValue" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type FontValue = typeof fonts[number]['value']; + +// Warning: (ae-missing-release-tag) "FontWeight" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum FontWeight { + // (undocumented) + BOLD = "bold", + // (undocumented) + BOLDER = "bolder", + // (undocumented) + EIGHT = "800", + // (undocumented) + FIVE = "500", + // (undocumented) + FOUR = "400", + // (undocumented) + LIGHTER = "lighter", + // (undocumented) + NINE = "900", + // (undocumented) + NORMAL = "normal", + // (undocumented) + ONE = "100", + // (undocumented) + SEVEN = "700", + // (undocumented) + SIX = "600", + // (undocumented) + THREE = "300", + // (undocumented) + TWO = "200" +} + +// Warning: (ae-missing-release-tag) "format" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function format(ast: T, type: T extends ExpressionAstExpression ? 'expression' : 'argument'): string; + +// Warning: (ae-missing-release-tag) "formatExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function formatExpression(ast: ExpressionAstExpression): string; + +// Warning: (ae-missing-release-tag) "FunctionsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class FunctionsRegistry implements IRegistry { + constructor(executor: Executor); + // (undocumented) + get(id: string): ExpressionFunction | null; + // (undocumented) + register(functionDefinition: AnyExpressionFunctionDefinition | (() => AnyExpressionFunctionDefinition)): void; + // (undocumented) + toArray(): ExpressionFunction[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "IInterpreterRenderHandlers" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IInterpreterRenderHandlers { + done: () => void; + // (undocumented) + event: (event: any) => void; + // (undocumented) + onDestroy: (fn: () => void) => void; + // (undocumented) + reload: () => void; + // (undocumented) + update: (params: any) => void; +} + +// Warning: (ae-missing-release-tag) "InterpreterErrorType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public @deprecated (undocumented) +export type InterpreterErrorType = ExpressionValueError; + +// Warning: (ae-missing-release-tag) "IRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface IRegistry { + // (undocumented) + get(id: string): T | null; + // (undocumented) + toArray(): T[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "isExpressionAstBuilder" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function isExpressionAstBuilder(val: any): val is ExpressionAstExpressionBuilder; + +// Warning: (ae-missing-release-tag) "KIBANA_CONTEXT_NAME" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type KIBANA_CONTEXT_NAME = 'kibana_context'; + +// Warning: (ae-missing-release-tag) "KibanaContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type KibanaContext = ExpressionValueSearchContext; + +// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatable { + // (undocumented) + columns: KibanaDatatableColumn[]; + // (undocumented) + rows: KibanaDatatableRow[]; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name_3; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableColumn { + // (undocumented) + formatHint?: SerializedFieldFormat; + // (undocumented) + id: string; + // (undocumented) + meta?: KibanaDatatableColumnMeta; + // (undocumented) + name: string; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableColumnMeta { + // (undocumented) + aggConfigParams?: Record; + // (undocumented) + indexPatternId?: string; + // (undocumented) + type: string; +} + +// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface KibanaDatatableRow { + // (undocumented) + [key: string]: unknown; +} + +// Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type KnownTypeToString = T extends string ? 'string' : T extends boolean ? 'boolean' : T extends number ? 'number' : T extends null ? 'null' : T extends { + type: string; +} ? T['type'] : never; + +// Warning: (ae-missing-release-tag) "Overflow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum Overflow { + // (undocumented) + AUTO = "auto", + // (undocumented) + HIDDEN = "hidden", + // (undocumented) + SCROLL = "scroll", + // (undocumented) + VISIBLE = "visible" +} + +// Warning: (ae-missing-release-tag) "parse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function parse(expression: E, startRule: S): S extends 'expression' ? ExpressionAstExpression : ExpressionAstArgument; + +// Warning: (ae-missing-release-tag) "parseExpression" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function parseExpression(expression: string): ExpressionAstExpression; + +// Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function plugin(initializerContext: PluginInitializerContext): ExpressionsServerPlugin; + +// Warning: (ae-missing-release-tag) "PointSeries" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeries = ExpressionValueBoxed<'pointseries', { + columns: PointSeriesColumns; + rows: PointSeriesRow[]; +}>; + +// Warning: (ae-missing-release-tag) "PointSeriesColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface PointSeriesColumn { + // (undocumented) + expression: string; + // (undocumented) + role: 'measure' | 'dimension'; + // (undocumented) + type: 'number' | 'string'; +} + +// Warning: (ae-missing-release-tag) "PointSeriesColumnName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeriesColumnName = 'x' | 'y' | 'color' | 'size' | 'text'; + +// Warning: (ae-missing-release-tag) "PointSeriesColumns" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type PointSeriesColumns = Record | {}; + +// Warning: (ae-missing-release-tag) "PointSeriesRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PointSeriesRow = Record; + +// Warning: (ae-missing-release-tag) "Range" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface Range { + // (undocumented) + from: number; + // (undocumented) + to: number; + // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts + // + // (undocumented) + type: typeof name_4; +} + +// Warning: (ae-missing-release-tag) "SerializedDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SerializedDatatable extends Datatable { + // (undocumented) + rows: string[][]; +} + +// Warning: (ae-missing-release-tag) "SerializedFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface SerializedFieldFormat> { + // (undocumented) + id?: string; + // (undocumented) + params?: TParams; +} + +// Warning: (ae-missing-release-tag) "Style" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type Style = ExpressionTypeStyle; + +// Warning: (ae-missing-release-tag) "TextAlignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum TextAlignment { + // (undocumented) + CENTER = "center", + // (undocumented) + JUSTIFY = "justify", + // (undocumented) + LEFT = "left", + // (undocumented) + RIGHT = "right" +} + +// Warning: (ae-missing-release-tag) "TextDecoration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export enum TextDecoration { + // (undocumented) + NONE = "none", + // (undocumented) + UNDERLINE = "underline" +} + +// Warning: (ae-missing-release-tag) "TypesRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class TypesRegistry implements IRegistry { + constructor(executor: Executor); + // (undocumented) + get(id: string): ExpressionType | null; + // (undocumented) + register(typeDefinition: AnyExpressionTypeDefinition | (() => AnyExpressionTypeDefinition)): void; + // (undocumented) + toArray(): ExpressionType[]; + // (undocumented) + toJS(): Record; +} + +// Warning: (ae-missing-release-tag) "TypeString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type TypeString = KnownTypeToString>; + +// Warning: (ae-missing-release-tag) "TypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type TypeToString = KnownTypeToString | UnmappedTypeStrings; + +// Warning: (ae-missing-release-tag) "UnmappedTypeStrings" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type UnmappedTypeStrings = 'date' | 'filter'; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/src/plugins/home/public/application/components/app_navigation_handler.ts b/src/plugins/home/public/application/components/app_navigation_handler.ts index 61d85c033b544..91407ffcaf226 100644 --- a/src/plugins/home/public/application/components/app_navigation_handler.ts +++ b/src/plugins/home/public/application/components/app_navigation_handler.ts @@ -24,12 +24,6 @@ export const createAppNavigationHandler = (targetUrl: string) => (event: MouseEv if (event.altKey || event.metaKey || event.ctrlKey) { return; } - if (targetUrl.startsWith('/app/')) { - const [, appId, path] = /\/app\/(.*?)((\/|\?|#|$).*)/.exec(targetUrl) || []; - if (!appId) { - return; - } - event.preventDefault(); - getServices().application.navigateToApp(appId, { path }); - } + event.preventDefault(); + getServices().application.navigateToUrl(targetUrl); }; diff --git a/src/plugins/home/server/services/sample_data/usage/collector.ts b/src/plugins/home/server/services/sample_data/usage/collector.ts index d819d67a8d432..1cece375ce59b 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector.ts @@ -38,12 +38,12 @@ export async function makeSampleDataUsageCollector( fetch: fetchProvider(index), isReady: () => true, schema: { - installed: { type: 'keyword' }, + installed: { type: 'array', items: { type: 'keyword' } }, last_install_date: { type: 'date' }, last_install_set: { type: 'keyword' }, last_uninstall_date: { type: 'date' }, last_uninstall_set: { type: 'keyword' }, - uninstalled: { type: 'keyword' }, + uninstalled: { type: 'array', items: { type: 'keyword' } }, }, }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap index 6cc92d20cfdcc..544e3ba983122 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/__snapshots__/step_time_field.test.tsx.snap @@ -27,7 +27,7 @@ exports[`StepTimeField should render "Custom index pattern ID already exists" wh /> ({ - fieldsFetcher: { - fetchForWildcard: jest.fn().mockReturnValue(Promise.resolve(fields)), - }, - }), + create: () => ({}), + getFieldsForWildcard: jest.fn().mockReturnValue(Promise.resolve(fields)), } as any; describe('StepTimeField', () => { diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx index 5d33a08557fed..cacabb6d7623b 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx @@ -108,12 +108,12 @@ export class StepTimeField extends Component { }); test('invokes the provided services when creating an index pattern', async () => { - const create = jest.fn().mockImplementation(() => 'id'); + const newIndexPatternAndSave = jest.fn().mockImplementation(async () => { + return indexPattern; + }); const clear = jest.fn(); mockContext.data.indexPatterns.clearCache = clear; const indexPattern = ({ @@ -151,11 +153,10 @@ describe('CreateIndexPatternWizard', () => { title: 'my-fake-index-pattern', timeFieldName: 'timestamp', fields: [], - create, + _fetchFields: jest.fn(), } as unknown) as IndexPattern; - mockContext.data.indexPatterns.make = async () => { - return indexPattern; - }; + mockContext.data.indexPatterns.createAndSave = newIndexPatternAndSave; + mockContext.data.indexPatterns.setDefault = jest.fn(); const component = createComponentWithContext( CreateIndexPatternWizard, @@ -165,9 +166,8 @@ describe('CreateIndexPatternWizard', () => { component.setState({ indexPattern: 'foo' }); await (component.instance() as CreateIndexPatternWizard).createIndexPattern(undefined, 'id'); - expect(mockContext.uiSettings.get).toBeCalled(); - expect(create).toBeCalled(); - expect(clear).toBeCalledWith('id'); - expect(routeComponentPropsMock.history.push).toBeCalledWith(`/patterns/id`); + expect(newIndexPatternAndSave).toBeCalled(); + expect(clear).toBeCalledWith('1'); + expect(routeComponentPropsMock.history.push).toBeCalledWith(`/patterns/1`); }); }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx index a789ebbfadbce..aa97c21d766b9 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx @@ -40,6 +40,7 @@ import { ensureMinimumTime, getIndices } from './lib'; import { IndexPatternCreationConfig } from '../..'; import { IndexPatternManagmentContextValue } from '../../types'; import { MatchedItem } from './types'; +import { DuplicateIndexPatternError, IndexPattern } from '../../../../data/public'; interface CreateIndexPatternWizardState { step: number; @@ -156,50 +157,50 @@ export class CreateIndexPatternWizard extends Component< }; createIndexPattern = async (timeFieldName: string | undefined, indexPatternId: string) => { + let emptyPattern: IndexPattern; const { history } = this.props; const { indexPattern } = this.state; - const emptyPattern = await this.context.services.data.indexPatterns.make(); - - Object.assign(emptyPattern, { - id: indexPatternId, - title: indexPattern, - timeFieldName, - ...this.state.indexPatternCreationType.getIndexPatternMappings(), - }); - - const createdId = await emptyPattern.create(); - if (!createdId) { - const confirmMessage = i18n.translate( - 'indexPatternManagement.indexPattern.titleExistsLabel', - { - values: { title: emptyPattern.title }, - defaultMessage: "An index pattern with the title '{title}' already exists.", - } - ); - - const isConfirmed = await this.context.services.overlays.openConfirm(confirmMessage, { - confirmButtonText: i18n.translate( - 'indexPatternManagement.indexPattern.goToPatternButtonLabel', + try { + emptyPattern = await this.context.services.data.indexPatterns.createAndSave({ + id: indexPatternId, + title: indexPattern, + timeFieldName, + ...this.state.indexPatternCreationType.getIndexPatternMappings(), + }); + } catch (err) { + if (err instanceof DuplicateIndexPatternError) { + const confirmMessage = i18n.translate( + 'indexPatternManagement.indexPattern.titleExistsLabel', { - defaultMessage: 'Go to existing pattern', + values: { title: emptyPattern!.title }, + defaultMessage: "An index pattern with the title '{title}' already exists.", } - ), - }); + ); + + const isConfirmed = await this.context.services.overlays.openConfirm(confirmMessage, { + confirmButtonText: i18n.translate( + 'indexPatternManagement.indexPattern.goToPatternButtonLabel', + { + defaultMessage: 'Go to existing pattern', + } + ), + }); - if (isConfirmed) { - return history.push(`/patterns/${indexPatternId}`); + if (isConfirmed) { + return history.push(`/patterns/${indexPatternId}`); + } else { + return; + } } else { - return; + throw err; } } - if (!this.context.services.uiSettings.get('defaultIndex')) { - await this.context.services.uiSettings.set('defaultIndex', createdId); - } + await this.context.services.data.indexPatterns.setDefault(emptyPattern.id as string); - this.context.services.data.indexPatterns.clearCache(createdId); - history.push(`/patterns/${createdId}`); + this.context.services.data.indexPatterns.clearCache(emptyPattern.id as string); + history.push(`/patterns/${emptyPattern.id}`); }; goToTimeFieldStep = (indexPattern: string, selectedTimeField?: string) => { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx index 13be9ca6c9c25..08edf42df60d8 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx @@ -96,7 +96,7 @@ export const CreateEditField = withRouter( indexPattern={indexPattern} spec={spec} services={{ - saveIndexPattern: data.indexPatterns.save.bind(data.indexPatterns), + saveIndexPattern: data.indexPatterns.updateSavedObject.bind(data.indexPatterns), redirectAway, }} /> diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx index d09836019b0bc..67a20c428040f 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx @@ -121,7 +121,8 @@ export const EditIndexPattern = withRouter( const refreshFields = () => { overlays.openConfirm(confirmMessage, confirmModalOptionsRefresh).then(async (isConfirmed) => { if (isConfirmed) { - await indexPattern.refreshFields(); + await data.indexPatterns.refreshFields(indexPattern); + await data.indexPatterns.updateSavedObject(indexPattern); setFields(indexPattern.getNonScriptedFields()); } }); @@ -236,7 +237,7 @@ export const EditIndexPattern = withRouter( ({ @@ -43,7 +43,7 @@ const helpers = { const indexPattern = ({ getNonScriptedFields: () => fields, -} as unknown) as IIndexPattern; +} as unknown) as IndexPattern; const mockFieldToIndexPatternField = (spec: Record) => { return new IndexPatternField( diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index 23977aac7fa7a..7be420e2af50d 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -19,23 +19,19 @@ import React, { Component } from 'react'; import { createSelector } from 'reselect'; -import { - IndexPatternField, - IIndexPattern, - IFieldType, -} from '../../../../../../plugins/data/public'; +import { IndexPatternField, IndexPattern, IFieldType } from '../../../../../../plugins/data/public'; import { Table } from './components/table'; import { getFieldFormat } from './lib'; import { IndexedFieldItem } from './types'; interface IndexedFieldsTableProps { fields: IndexPatternField[]; - indexPattern: IIndexPattern; + indexPattern: IndexPattern; fieldFilter?: string; indexedFieldTypeFilter?: string; helpers: { redirectToRoute: (obj: any) => void; - getFieldInfo: (indexPattern: IIndexPattern, field: IFieldType) => string[]; + getFieldInfo: (indexPattern: IndexPattern, field: IFieldType) => string[]; }; fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; } diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx index 08cc90faf75fa..c7ea20c700086 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx @@ -39,7 +39,7 @@ interface ScriptedFieldsTableProps { }; onRemoveField?: () => void; painlessDocLink: string; - saveIndexPattern: DataPublicPluginStart['indexPatterns']['save']; + saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject']; } interface ScriptedFieldsTableState { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx index e43ee2e55eeca..2d3a61b42c3a4 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.test.tsx @@ -22,13 +22,13 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Table, TableProps, TableState } from './table'; import { EuiTableFieldDataColumnType, keys } from '@elastic/eui'; -import { IIndexPattern } from 'src/plugins/data/public'; +import { IndexPattern } from 'src/plugins/data/public'; import { SourceFiltersTableFilter } from '../../types'; -const indexPattern = {} as IIndexPattern; +const indexPattern = {} as IndexPattern; const items: SourceFiltersTableFilter[] = [{ value: 'tim*', clientId: '' }]; -const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as IIndexPattern); +const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as IndexPattern); const getTableColumnRender = ( component: ShallowWrapper, diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.tsx index f73d756f28116..c5b09961f25fc 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/components/table/table.tsx @@ -30,7 +30,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { IIndexPattern } from 'src/plugins/data/public'; +import { IndexPattern } from 'src/plugins/data/public'; import { SourceFiltersTableFilter } from '../../types'; const filterHeader = i18n.translate( @@ -80,7 +80,7 @@ const cancelAria = i18n.translate( ); export interface TableProps { - indexPattern: IIndexPattern; + indexPattern: IndexPattern; items: SourceFiltersTableFilter[]; deleteFilter: Function; fieldWildcardMatcher: Function; diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx index b00648f124716..cd311db513c09 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx @@ -30,7 +30,7 @@ export interface SourceFiltersTableProps { filterFilter: string; fieldWildcardMatcher: Function; onAddOrRemoveFilter?: Function; - saveIndexPattern: DataPublicPluginStart['indexPatterns']['save']; + saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject']; } export interface SourceFiltersTableState { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx index 101399ef02b73..5c29dfafd3c07 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx @@ -49,7 +49,7 @@ import { getTabs, getPath, convertToEuiSelectOption } from './utils'; interface TabsProps extends Pick { indexPattern: IndexPattern; fields: IndexPatternField[]; - saveIndexPattern: DataPublicPluginStart['indexPatterns']['save']; + saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject']; } const searchAriaLabel = i18n.translate( diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx index 2b484d1d837bf..4fae91e78f8f9 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx @@ -133,7 +133,7 @@ export interface FieldEdiorProps { spec: IndexPatternField['spec']; services: { redirectAway: () => void; - saveIndexPattern: DataPublicPluginStart['indexPatterns']['save']; + saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject']; }; } @@ -825,7 +825,7 @@ export class FieldEditor extends PureComponent { + .catch(() => { if (oldField) { indexPattern.fields.update(oldField); } else { diff --git a/src/plugins/input_control_vis/public/input_control_vis_type.ts b/src/plugins/input_control_vis/public/input_control_vis_type.ts index 9f415f2100004..782df67f5c58a 100644 --- a/src/plugins/input_control_vis/public/input_control_vis_type.ts +++ b/src/plugins/input_control_vis/public/input_control_vis_type.ts @@ -19,12 +19,15 @@ import { i18n } from '@kbn/i18n'; +import { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; import { createInputControlVisController } from './vis_controller'; import { getControlsTab } from './components/editor/controls_tab'; import { OptionsTab } from './components/editor/options_tab'; import { InputControlVisDependencies } from './plugin'; -export function createInputControlVisTypeDefinition(deps: InputControlVisDependencies) { +export function createInputControlVisTypeDefinition( + deps: InputControlVisDependencies +): BaseVisTypeOptions { const InputControlVisController = createInputControlVisController(deps); const ControlsTab = getControlsTab(deps); diff --git a/src/plugins/input_control_vis/public/vis_controller.tsx b/src/plugins/input_control_vis/public/vis_controller.tsx index faea98b792291..6f35e17866120 100644 --- a/src/plugins/input_control_vis/public/vis_controller.tsx +++ b/src/plugins/input_control_vis/public/vis_controller.tsx @@ -31,12 +31,12 @@ import { RangeControl } from './control/range_control_factory'; import { ListControl } from './control/list_control_factory'; import { InputControlVisDependencies } from './plugin'; import { FilterManager, Filter } from '../../data/public'; -import { VisParams, Vis } from '../../visualizations/public'; +import { VisParams, ExprVis } from '../../visualizations/public'; export const createInputControlVisController = (deps: InputControlVisDependencies) => { return class InputControlVisController { private I18nContext?: I18nStart['Context']; - private isLoaded = false; + private _isLoaded = false; controls: Array; queryBarUpdateHandler: () => void; @@ -45,7 +45,7 @@ export const createInputControlVisController = (deps: InputControlVisDependencie timeFilterSubscription: Subscription; visParams?: VisParams; - constructor(public el: Element, public vis: Vis) { + constructor(public el: Element, public vis: ExprVis) { this.controls = []; this.queryBarUpdateHandler = this.updateControlsFromKbn.bind(this); @@ -58,7 +58,7 @@ export const createInputControlVisController = (deps: InputControlVisDependencie .getTimeUpdate$() .subscribe(() => { if (this.visParams?.useTimeFilter) { - this.isLoaded = false; + this._isLoaded = false; } }); } @@ -68,11 +68,11 @@ export const createInputControlVisController = (deps: InputControlVisDependencie const [{ i18n }] = await deps.core.getStartServices(); this.I18nContext = i18n.Context; } - if (!this.isLoaded || !isEqual(visParams, this.visParams)) { + if (!this._isLoaded || !isEqual(visParams, this.visParams)) { this.visParams = visParams; this.controls = []; this.controls = await this.initControls(); - this.isLoaded = true; + this._isLoaded = true; } this.drawVis(); } diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json index a507fe457b633..c05490c349917 100644 --- a/src/plugins/kibana_react/kibana.json +++ b/src/plugins/kibana_react/kibana.json @@ -2,5 +2,6 @@ "id": "kibanaReact", "version": "kibana", "ui": true, + "server": false, "requiredBundles": ["kibanaUtils"] } diff --git a/src/plugins/kibana_react/public/code_editor/.storybook/main.js b/src/plugins/kibana_react/public/code_editor/.storybook/main.js new file mode 100644 index 0000000000000..1818aa44a9399 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.examples.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.stories.tsx similarity index 100% rename from src/plugins/kibana_react/public/code_editor/code_editor.examples.tsx rename to src/plugins/kibana_react/public/code_editor/code_editor.stories.tsx diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index f049085ccff61..85b2c327e8b21 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -186,3 +186,7 @@ export class CodeEditor extends React.Component { } }; } + +// React.lazy requires default export +// eslint-disable-next-line import/no-default-export +export default CodeEditor; diff --git a/src/plugins/kibana_react/public/code_editor/index.tsx b/src/plugins/kibana_react/public/code_editor/index.tsx index 0ef83811d96d3..63e3d330a1c52 100644 --- a/src/plugins/kibana_react/public/code_editor/index.tsx +++ b/src/plugins/kibana_react/public/code_editor/index.tsx @@ -17,11 +17,23 @@ * under the License. */ import React from 'react'; +import { EuiDelayRender, EuiLoadingContent } from '@elastic/eui'; import { useUiSetting } from '../ui_settings'; -import { CodeEditor as BaseEditor, Props } from './code_editor'; +import type { Props } from './code_editor'; + +const LazyBaseEditor = React.lazy(() => import('./code_editor')); + +const Fallback = () => ( + + + +); export const CodeEditor: React.FunctionComponent = (props) => { const darkMode = useUiSetting('theme:darkMode'); - - return ; + return ( + }> + + + ); }; diff --git a/src/plugins/kibana_react/public/code_editor/scripts/storybook.ts b/src/plugins/kibana_react/public/code_editor/scripts/storybook.ts deleted file mode 100644 index 4fe7286987397..0000000000000 --- a/src/plugins/kibana_react/public/code_editor/scripts/storybook.ts +++ /dev/null @@ -1,26 +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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'code-editor', - storyGlobs: [join(__dirname, '..', '*.examples.tsx')], -}); diff --git a/src/plugins/kibana_react/public/field_button/field_button.tsx b/src/plugins/kibana_react/public/field_button/field_button.tsx index 26e6453e4c48b..97d1b32746120 100644 --- a/src/plugins/kibana_react/public/field_button/field_button.tsx +++ b/src/plugins/kibana_react/public/field_button/field_button.tsx @@ -19,8 +19,7 @@ import './field_button.scss'; import classNames from 'classnames'; -import React, { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react'; -import { CommonProps } from '@elastic/eui'; +import React, { ReactNode, HTMLAttributes } from 'react'; export interface FieldButtonProps extends HTMLAttributes { /** @@ -54,13 +53,10 @@ export interface FieldButtonProps extends HTMLAttributes { size?: ButtonSize; className?: string; /** - * The component always renders a ` +
+ {onClick ? ( + + ) : ( +
+ {fieldIcon && {fieldIcon}} + {fieldName && {fieldName}} + {fieldInfoIcon &&
{fieldInfoIcon}
} +
+ )} + {fieldAction &&
{fieldAction}
}
); diff --git a/src/plugins/kibana_react/public/markdown/index.tsx b/src/plugins/kibana_react/public/markdown/index.tsx index cacf223cf33ed..4d6fd0b742ee4 100644 --- a/src/plugins/kibana_react/public/markdown/index.tsx +++ b/src/plugins/kibana_react/public/markdown/index.tsx @@ -17,5 +17,27 @@ * under the License. */ -export { MarkdownSimple } from './markdown_simple'; -export { Markdown } from './markdown'; +import React from 'react'; +import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; +import type { MarkdownSimpleProps } from './markdown_simple'; +import type { MarkdownProps } from './markdown'; + +const Fallback = () => ( + + + +); + +const LazyMarkdownSimple = React.lazy(() => import('./markdown_simple')); +export const MarkdownSimple = (props: MarkdownSimpleProps) => ( + }> + + +); + +const LazyMarkdown = React.lazy(() => import('./markdown')); +export const Markdown = (props: MarkdownProps) => ( + }> + + +); diff --git a/src/plugins/kibana_react/public/markdown/markdown.tsx b/src/plugins/kibana_react/public/markdown/markdown.tsx index 15d1c4931e60b..8bb61bc71862e 100644 --- a/src/plugins/kibana_react/public/markdown/markdown.tsx +++ b/src/plugins/kibana_react/public/markdown/markdown.tsx @@ -84,7 +84,7 @@ export const markdownFactory = memoize( } ); -interface MarkdownProps extends React.HTMLAttributes { +export interface MarkdownProps extends React.HTMLAttributes { className?: string; markdown?: string; openLinksInNewTab?: boolean; @@ -112,3 +112,7 @@ export class Markdown extends PureComponent { ); } } + +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default Markdown; diff --git a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx index a5465fd1c6fc9..71ae4e031abca 100644 --- a/src/plugins/kibana_react/public/markdown/markdown_simple.tsx +++ b/src/plugins/kibana_react/public/markdown/markdown_simple.tsx @@ -24,7 +24,7 @@ const markdownRenderers = { root: Fragment, }; -interface MarkdownSimpleProps { +export interface MarkdownSimpleProps { children: string; } @@ -32,3 +32,7 @@ interface MarkdownSimpleProps { export const MarkdownSimple = ({ children }: MarkdownSimpleProps) => ( {children} ); + +// Needed for React.lazy +// eslint-disable-next-line import/no-default-export +export default MarkdownSimple; diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 2fa1debf51b5c..e0e295723a69d 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -556,3 +556,6 @@ class TableListView extends React.Component { const getUsageCollector = jest.fn(); const registerType = jest.fn(); const callCluster = jest.fn(); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; beforeAll(() => registerApplicationUsageCollector(logger, usageCollectionMock, registerType, getUsageCollector) @@ -62,7 +67,7 @@ describe('telemetry_application_usage', () => { test('if no savedObjectClient initialised, return undefined', async () => { expect(collector.isReady()).toBe(false); - expect(await collector.fetch(callCluster)).toBeUndefined(); + expect(await collector.fetch(callCluster, esClient)).toBeUndefined(); jest.runTimersToTime(ROLL_INDICES_START); }); @@ -80,7 +85,7 @@ describe('telemetry_application_usage', () => { jest.runTimersToTime(ROLL_TOTAL_INDICES_INTERVAL); // Force rollTotals to run expect(collector.isReady()).toBe(true); - expect(await collector.fetch(callCluster)).toStrictEqual({}); + expect(await collector.fetch(callCluster, esClient)).toStrictEqual({}); expect(savedObjectClient.bulkCreate).not.toHaveBeenCalled(); }); @@ -137,7 +142,7 @@ describe('telemetry_application_usage', () => { jest.runTimersToTime(ROLL_TOTAL_INDICES_INTERVAL); // Force rollTotals to run - expect(await collector.fetch(callCluster)).toStrictEqual({ + expect(await collector.fetch(callCluster, esClient)).toStrictEqual({ appId: { clicks_total: total + 1 + 10, clicks_7_days: total + 1, @@ -197,7 +202,7 @@ describe('telemetry_application_usage', () => { getUsageCollector.mockImplementation(() => savedObjectClient); - expect(await collector.fetch(callCluster)).toStrictEqual({ + expect(await collector.fetch(callCluster, esClient)).toStrictEqual({ appId: { clicks_total: 1, clicks_7_days: 0, diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts index 1adc0dc6896fd..e88d90fe5b24b 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/get_saved_object_counts.ts @@ -39,9 +39,12 @@ const TYPES = [ ]; export interface KibanaSavedObjectCounts { - [pluginName: string]: { - total: number; - }; + dashboard: { total: number }; + visualization: { total: number }; + search: { total: number }; + index_pattern: { total: number }; + graph_workspace: { total: number }; + timelion_sheet: { total: number }; } export async function getSavedObjectsCounts( @@ -71,7 +74,7 @@ export async function getSavedObjectsCounts( // Initialise the object with all zeros for all the types const allZeros: KibanaSavedObjectCounts = TYPES.reduce( (acc, type) => ({ ...acc, [snakeCase(type)]: { total: 0 } }), - {} + {} as KibanaSavedObjectCounts ); // Add the doc_count from each bucket diff --git a/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts index 9cc079a9325d5..5b56e1a9b596f 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/kibana/kibana_usage_collector.ts @@ -22,15 +22,28 @@ import { take } from 'rxjs/operators'; import { SharedGlobalConfig } from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { KIBANA_STATS_TYPE } from '../../../common/constants'; -import { getSavedObjectsCounts } from './get_saved_object_counts'; +import { getSavedObjectsCounts, KibanaSavedObjectCounts } from './get_saved_object_counts'; + +interface KibanaUsage extends KibanaSavedObjectCounts { + index: string; +} export function getKibanaUsageCollector( usageCollection: UsageCollectionSetup, legacyConfig$: Observable ) { - return usageCollection.makeUsageCollector({ + return usageCollection.makeUsageCollector({ type: 'kibana', isReady: () => true, + schema: { + index: { type: 'keyword' }, + dashboard: { total: { type: 'long' } }, + visualization: { total: { type: 'long' } }, + search: { total: { type: 'long' } }, + index_pattern: { total: { type: 'long' } }, + graph_workspace: { total: { type: 'long' } }, + timelion_sheet: { total: { type: 'long' } }, + }, async fetch(callCluster) { const { kibana: { index }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts new file mode 100644 index 0000000000000..792ac24b4de3d --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -0,0 +1,116 @@ +/* + * 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 { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { UsageStats } from './telemetry_management_collector'; + +// Retrieved by changing all the current settings in Kibana (we'll need to revisit it in the future). +// I would suggest we use flattened type for the mappings of this collector. +export const stackManagementSchema: MakeSchemaFrom = { + 'visualize:enableLabs': { type: 'boolean' }, + 'visualization:heatmap:maxBuckets': { type: 'long' }, + 'visualization:colorMapping': { type: 'text' }, + 'visualization:regionmap:showWarnings': { type: 'boolean' }, + 'visualization:dimmingOpacity': { type: 'float' }, + 'visualization:tileMap:maxPrecision': { type: 'long' }, + 'securitySolution:ipReputationLinks': { type: 'text' }, + 'csv:separator': { type: 'keyword' }, + 'visualization:tileMap:WMSdefaults': { type: 'text' }, + 'timelion:target_buckets': { type: 'long' }, + 'timelion:max_buckets': { type: 'long' }, + 'timelion:es.timefield': { type: 'keyword' }, + 'timelion:min_interval': { type: 'keyword' }, + 'timelion:default_rows': { type: 'long' }, + 'timelion:default_columns': { type: 'long' }, + 'timelion:quandl.key': { type: 'keyword' }, + 'timelion:es.default_index': { type: 'keyword' }, + 'timelion:showTutorial': { type: 'boolean' }, + 'securitySolution:timeDefaults': { type: 'keyword' }, + 'securitySolution:defaultAnomalyScore': { type: 'long' }, + 'securitySolution:defaultIndex': { type: 'keyword' }, // it's an array + 'securitySolution:refreshIntervalDefaults': { type: 'keyword' }, + 'securitySolution:newsFeedUrl': { type: 'keyword' }, + 'securitySolution:enableNewsFeed': { type: 'boolean' }, + 'search:includeFrozen': { type: 'boolean' }, + 'courier:maxConcurrentShardRequests': { type: 'long' }, + 'courier:batchSearches': { type: 'boolean' }, + 'courier:setRequestPreference': { type: 'keyword' }, + 'courier:customRequestPreference': { type: 'keyword' }, + 'courier:ignoreFilterIfFieldNotInIndex': { type: 'boolean' }, + 'rollups:enableIndexPatterns': { type: 'boolean' }, + 'xpackReporting:customPdfLogo': { type: 'text' }, + 'notifications:lifetime:warning': { type: 'long' }, + 'notifications:lifetime:banner': { type: 'long' }, + 'notifications:lifetime:info': { type: 'long' }, + 'notifications:banner': { type: 'text' }, + 'notifications:lifetime:error': { type: 'long' }, + 'doc_table:highlight': { type: 'boolean' }, + 'discover:searchOnPageLoad': { type: 'boolean' }, + // eslint-disable-next-line @typescript-eslint/naming-convention + 'doc_table:hideTimeColumn': { type: 'boolean' }, + 'discover:sampleSize': { type: 'long' }, + defaultColumns: { type: 'keyword' }, // it's an array + 'context:defaultSize': { type: 'long' }, + 'discover:aggs:terms:size': { type: 'long' }, + 'context:tieBreakerFields': { type: 'keyword' }, // it's an array + 'discover:sort:defaultOrder': { type: 'keyword' }, + 'context:step': { type: 'long' }, + 'accessibility:disableAnimations': { type: 'boolean' }, + 'ml:fileDataVisualizerMaxFileSize': { type: 'keyword' }, + 'ml:anomalyDetection:results:enableTimeDefaults': { type: 'boolean' }, + 'ml:anomalyDetection:results:timeDefaults': { type: 'keyword' }, + 'truncate:maxHeight': { type: 'long' }, + 'timepicker:timeDefaults': { type: 'keyword' }, + 'timepicker:refreshIntervalDefaults': { type: 'keyword' }, + 'timepicker:quickRanges': { type: 'keyword' }, + 'theme:version': { type: 'keyword' }, + 'theme:darkMode': { type: 'boolean' }, + 'state:storeInSessionStorage': { type: 'boolean' }, + 'savedObjects:perPage': { type: 'long' }, + 'search:queryLanguage': { type: 'keyword' }, + 'shortDots:enable': { type: 'boolean' }, + 'sort:options': { type: 'keyword' }, + 'savedObjects:listingLimit': { type: 'long' }, + 'query:queryString:options': { type: 'keyword' }, + pageNavigation: { type: 'keyword' }, + 'metrics:max_buckets': { type: 'long' }, + 'query:allowLeadingWildcards': { type: 'boolean' }, + metaFields: { type: 'keyword' }, // it's an array + 'indexPattern:placeholder': { type: 'keyword' }, + 'histogram:barTarget': { type: 'long' }, + 'histogram:maxBars': { type: 'long' }, + 'format:number:defaultLocale': { type: 'keyword' }, + 'format:percent:defaultPattern': { type: 'keyword' }, + 'format:number:defaultPattern': { type: 'keyword' }, + 'history:limit': { type: 'long' }, + 'format:defaultTypeMap': { type: 'keyword' }, + 'format:currency:defaultPattern': { type: 'keyword' }, + defaultIndex: { type: 'keyword' }, + 'format:bytes:defaultPattern': { type: 'keyword' }, + 'filters:pinnedByDefault': { type: 'boolean' }, + 'filterEditor:suggestValues': { type: 'boolean' }, + 'fields:popularLimit': { type: 'long' }, + dateNanosFormat: { type: 'keyword' }, + defaultRoute: { type: 'keyword' }, + 'dateFormat:tz': { type: 'keyword' }, + 'dateFormat:scaled': { type: 'keyword' }, + 'csv:quoteValues': { type: 'boolean' }, + 'dateFormat:dow': { type: 'keyword' }, + dateFormat: { type: 'keyword' }, +}; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts index 3a777beebd90a..612b1714020ef 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts @@ -19,8 +19,13 @@ import { IUiSettingsClient } from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { stackManagementSchema } from './schema'; -export type UsageStats = Record; +export interface UsageStats extends Record { + // We don't support `type` yet. Only interfaces. So I added at least 1 known key to the generic + // Record extension to avoid eslint reverting it back to a `type` + 'visualize:enableLabs': boolean; +} export function createCollectorFetch(getUiSettingsClient: () => IUiSettingsClient | undefined) { return async function fetchUsageStats(): Promise { @@ -45,10 +50,11 @@ export function registerManagementUsageCollector( usageCollection: UsageCollectionSetup, getUiSettingsClient: () => IUiSettingsClient | undefined ) { - const collector = usageCollection.makeUsageCollector({ + const collector = usageCollection.makeUsageCollector({ type: 'stack_management', isReady: () => typeof getUiSettingsClient() !== 'undefined', fetch: createCollectorFetch(getUiSettingsClient), + schema: stackManagementSchema, }); usageCollection.registerCollector(collector); diff --git a/src/plugins/kibana_utils/kibana.json b/src/plugins/kibana_utils/kibana.json index 7e2127c27548e..3e20b68bca431 100644 --- a/src/plugins/kibana_utils/kibana.json +++ b/src/plugins/kibana_utils/kibana.json @@ -2,6 +2,7 @@ "id": "kibanaUtils", "version": "kibana", "ui": true, + "server": false, "extraPublicDirs": [ "common", "demos/state_containers/todomvc", diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx index d8121b656deeb..80e30d2b5dffe 100644 --- a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx +++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx @@ -20,12 +20,19 @@ import React, { Fragment } from 'react'; import { History } from 'history'; import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner } from '@elastic/eui'; import ReactDOM from 'react-dom'; -import ReactMarkdown from 'react-markdown'; import { ApplicationStart, HttpStart, ToastsSetup } from 'kibana/public'; import { SavedObjectNotFound } from '..'; +const ReactMarkdown = React.lazy(() => import('react-markdown')); +const ErrorRenderer = (props: { children: string }) => ( + }> + + +); + interface Mapping { [key: string]: string | { app: string; path: string }; } @@ -96,16 +103,7 @@ export function redirectWhenMissing({ defaultMessage: 'Saved object is missing', }), text: (element: HTMLElement) => { - ReactDOM.render( - - {error.message} - , - element - ); + ReactDOM.render({error.message}, element); return () => ReactDOM.unmountComponentAtNode(element); }, }); diff --git a/src/plugins/kibana_utils/tsconfig.json b/src/plugins/kibana_utils/tsconfig.json new file mode 100644 index 0000000000000..bd65e06c78608 --- /dev/null +++ b/src/plugins/kibana_utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "demos/**/*", + "public/**/*", + "server/**/*", + "index.ts", + "../../../typings/**/*" + ], + "references": [ + { "path": "../../test_utils/tsconfig.json" }, + { "path": "../../core/tsconfig.json" } + ] +} diff --git a/src/plugins/management/common/contants.ts b/src/plugins/management/common/contants.ts new file mode 100644 index 0000000000000..6ff585510dab1 --- /dev/null +++ b/src/plugins/management/common/contants.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const MANAGEMENT_APP_ID = 'management'; diff --git a/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx b/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx index 02da2a46540c2..bcfb86c331084 100644 --- a/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx +++ b/src/plugins/management/public/components/management_app_wrapper/management_app_wrapper.tsx @@ -32,7 +32,7 @@ interface ManagementSectionWrapperProps { export class ManagementAppWrapper extends Component { private unmount?: Unmount; - private mountElementRef = createRef(); + private mountElementRef = createRef(); componentDidMount() { const { setBreadcrumbs, app, onAppMounted, history } = this.props; @@ -64,6 +64,6 @@ export class ManagementAppWrapper extends Component; + return
; } } diff --git a/src/plugins/management/public/index.ts b/src/plugins/management/public/index.ts index f6c23ccf0143f..f3e25b90b73c7 100644 --- a/src/plugins/management/public/index.ts +++ b/src/plugins/management/public/index.ts @@ -32,3 +32,5 @@ export { ManagementStart, DefinedSections, } from './types'; + +export { MANAGEMENT_APP_ID } from '../common/contants'; diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index 808578c470ae1..122e73796753c 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -33,6 +33,7 @@ import { AppNavLinkStatus, } from '../../../core/public'; +import { MANAGEMENT_APP_ID } from '../common/contants'; import { ManagementSectionsService, getSectionsServiceStartPrivate, @@ -72,7 +73,7 @@ export class ManagementPlugin implements Plugin) => Promise; + visualizationLabel: string; +} + +export function LegacyMapDeprecationMessage(props: Props) { + const getMapsMessage = !props.isMapsAvailable ? ( + + default distribution + + ), + }} + /> + ) : null; + + const button = props.isMapsAvailable ? ( +
+ + + +
+ ) : null; + + return ( + +

+ +

+ {button} +
+ ); +} diff --git a/src/plugins/maps_legacy/public/get_service_settings.ts b/src/plugins/maps_legacy/public/get_service_settings.ts new file mode 100644 index 0000000000000..8d0656237976d --- /dev/null +++ b/src/plugins/maps_legacy/public/get_service_settings.ts @@ -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 { lazyLoadMapsLegacyModules } from './lazy_load_bundle'; +// @ts-expect-error +import { getMapsLegacyConfig } from './kibana_services'; +import { IServiceSettings } from './map/service_settings_types'; + +let loadPromise: Promise; + +export async function getServiceSettings(): Promise { + if (typeof loadPromise !== 'undefined') { + return loadPromise; + } + + loadPromise = new Promise(async (resolve) => { + const modules = await lazyLoadMapsLegacyModules(); + const config = getMapsLegacyConfig(); + // @ts-expect-error + resolve(new modules.ServiceSettings(config, config.tilemap)); + }); + return loadPromise; +} diff --git a/src/plugins/maps_legacy/public/index.ts b/src/plugins/maps_legacy/public/index.ts index 8f14cd1b15e2c..fe5338b890ec8 100644 --- a/src/plugins/maps_legacy/public/index.ts +++ b/src/plugins/maps_legacy/public/index.ts @@ -19,8 +19,6 @@ // @ts-ignore import { PluginInitializerContext } from 'kibana/public'; -// @ts-ignore -import { L } from './leaflet'; import { MapsLegacyPlugin } from './plugin'; // @ts-ignore import * as colorUtil from './map/color_util'; @@ -29,14 +27,14 @@ import { KibanaMapLayer } from './map/kibana_map_layer'; // @ts-ignore import { convertToGeoJson } from './map/convert_to_geojson'; // @ts-ignore -import { scaleBounds, getPrecision, geoContains } from './map/decode_geo_hash'; +import { getPrecision, geoContains } from './map/decode_geo_hash'; import { VectorLayer, FileLayerField, FileLayer, TmsLayer, IServiceSettings, -} from './map/service_settings'; +} from './map/service_settings_types'; // @ts-ignore import { mapTooltipProvider } from './tooltip_provider'; @@ -48,7 +46,6 @@ export function plugin(initializerContext: PluginInitializerContext) { /** @public */ export { - scaleBounds, getPrecision, geoContains, colorUtil, @@ -60,13 +57,15 @@ export { FileLayer, TmsLayer, mapTooltipProvider, - L, }; export * from './common/types'; export { ORIGIN } from './common/constants/origin'; export { WmsOptions } from './components/wms_options'; +export { LegacyMapDeprecationMessage } from './components/legacy_map_deprecation_message'; + +export { lazyLoadMapsLegacyModules } from './lazy_load_bundle'; export type MapsLegacyPluginSetup = ReturnType; export type MapsLegacyPluginStart = ReturnType; diff --git a/src/plugins/maps_legacy/public/lazy_load_bundle/index.ts b/src/plugins/maps_legacy/public/lazy_load_bundle/index.ts new file mode 100644 index 0000000000000..292949503a616 --- /dev/null +++ b/src/plugins/maps_legacy/public/lazy_load_bundle/index.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +let loadModulesPromise: Promise; + +interface LazyLoadedMapsLegacyModules { + KibanaMap: unknown; + L: unknown; + ServiceSettings: unknown; +} + +export async function lazyLoadMapsLegacyModules(): Promise { + if (typeof loadModulesPromise !== 'undefined') { + return loadModulesPromise; + } + + loadModulesPromise = new Promise(async (resolve) => { + const { KibanaMap, L, ServiceSettings } = await import('./lazy'); + + resolve({ + KibanaMap, + L, + ServiceSettings, + }); + }); + return loadModulesPromise; +} diff --git a/src/plugins/maps_legacy/public/lazy_load_bundle/lazy/index.ts b/src/plugins/maps_legacy/public/lazy_load_bundle/lazy/index.ts new file mode 100644 index 0000000000000..5031b29e74b9a --- /dev/null +++ b/src/plugins/maps_legacy/public/lazy_load_bundle/lazy/index.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// @ts-expect-error +export { KibanaMap } from '../../map/kibana_map'; +// @ts-expect-error +export { ServiceSettings } from '../../map/service_settings'; +// @ts-expect-error +export { L } from '../../leaflet'; diff --git a/src/plugins/maps_legacy/public/map/base_maps_visualization.js b/src/plugins/maps_legacy/public/map/base_maps_visualization.js index 2d78fdc246e19..406dae43c9b5e 100644 --- a/src/plugins/maps_legacy/public/map/base_maps_visualization.js +++ b/src/plugins/maps_legacy/public/map/base_maps_visualization.js @@ -17,25 +17,22 @@ * under the License. */ -import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { filter, first } from 'rxjs/operators'; import { getEmsTileLayerId, getUiSettings, getToasts } from '../kibana_services'; +import { lazyLoadMapsLegacyModules } from '../lazy_load_bundle'; +import { getServiceSettings } from '../get_service_settings'; const WMS_MINZOOM = 0; const WMS_MAXZOOM = 22; //increase this to 22. Better for WMS -export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) { +export function BaseMapsVisualizationProvider() { /** * Abstract base class for a visualization consisting of a map with a single baselayer. * @class BaseMapsVisualization * @constructor */ - - const serviceSettings = mapServiceSettings; - const toastService = getToasts(); - return class BaseMapsVisualization { constructor(element, vis) { this.vis = vis; @@ -95,9 +92,9 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) const centerFromUIState = uiState.get('mapCenter'); options.zoom = !isNaN(zoomFromUiState) ? zoomFromUiState : this.vis.params.mapZoom; options.center = centerFromUIState ? centerFromUIState : this.vis.params.mapCenter; - const services = { toastService }; - this._kibanaMap = getKibanaMap(this._container, options, services); + const modules = await lazyLoadMapsLegacyModules(); + this._kibanaMap = new modules.KibanaMap(this._container, options); this._kibanaMap.setMinZoom(WMS_MINZOOM); //use a default this._kibanaMap.setMaxZoom(WMS_MAXZOOM); //use a default @@ -138,6 +135,7 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) const mapParams = this._getMapsParams(); if (!this._tmsConfigured()) { try { + const serviceSettings = await getServiceSettings(); const tmsServices = await serviceSettings.getTMSServices(); const userConfiguredTmsLayer = tmsServices[0]; const initBasemapLayer = userConfiguredTmsLayer @@ -147,7 +145,7 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) this._setTmsLayer(initBasemapLayer); } } catch (e) { - toastService.addWarning(e.message); + getToasts().addWarning(e.message); return; } return; @@ -174,7 +172,7 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) this._setTmsLayer(selectedTmsLayer); } } catch (tmsLoadingError) { - toastService.addWarning(tmsLoadingError.message); + getToasts().addWarning(tmsLoadingError.message); } } @@ -189,13 +187,14 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) isDesaturated = true; } const isDarkMode = getUiSettings().get('theme:darkMode'); + const serviceSettings = await getServiceSettings(); const meta = await serviceSettings.getAttributesForTMSLayer( tmsLayer, isDesaturated, isDarkMode ); const showZoomMessage = serviceSettings.shouldShowZoomMessage(tmsLayer); - const options = _.cloneDeep(tmsLayer); + const options = { ...tmsLayer }; delete options.id; delete options.subdomains; this._kibanaMap.setBaseLayer({ @@ -228,12 +227,11 @@ export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) } _getMapsParams() { - return _.assign( - {}, - this.vis.type.visConfig.defaults, - { type: this.vis.type.name }, - this._params - ); + return { + ...this.vis.type.visConfig.defaults, + type: this.vis.type.name, + ...this._params, + }; } _whenBaseLayerIsLoaded() { diff --git a/src/plugins/maps_legacy/public/map/decode_geo_hash.ts b/src/plugins/maps_legacy/public/map/decode_geo_hash.ts index 8c39ada03a46b..65184a8244777 100644 --- a/src/plugins/maps_legacy/public/map/decode_geo_hash.ts +++ b/src/plugins/maps_legacy/public/map/decode_geo_hash.ts @@ -17,8 +17,6 @@ * under the License. */ -import _ from 'lodash'; - interface DecodedGeoHash { latitude: number[]; longitude: number[]; @@ -101,33 +99,6 @@ interface GeoBoundingBox { bottom_right: GeoBoundingBoxCoordinate; } -export function scaleBounds(bounds: GeoBoundingBox): GeoBoundingBox { - const scale = 0.5; // scale bounds by 50% - - const topLeft = bounds.top_left; - const bottomRight = bounds.bottom_right; - let latDiff = _.round(Math.abs(topLeft.lat - bottomRight.lat), 5); - const lonDiff = _.round(Math.abs(bottomRight.lon - topLeft.lon), 5); - // map height can be zero when vis is first created - if (latDiff === 0) latDiff = lonDiff; - - const latDelta = latDiff * scale; - let topLeftLat = _.round(topLeft.lat, 5) + latDelta; - if (topLeftLat > 90) topLeftLat = 90; - let bottomRightLat = _.round(bottomRight.lat, 5) - latDelta; - if (bottomRightLat < -90) bottomRightLat = -90; - const lonDelta = lonDiff * scale; - let topLeftLon = _.round(topLeft.lon, 5) - lonDelta; - if (topLeftLon < -180) topLeftLon = -180; - let bottomRightLon = _.round(bottomRight.lon, 5) + lonDelta; - if (bottomRightLon > 180) bottomRightLon = 180; - - return { - top_left: { lat: topLeftLat, lon: topLeftLon }, - bottom_right: { lat: bottomRightLat, lon: bottomRightLon }, - }; -} - export function geoContains(collar?: GeoBoundingBox, bounds?: GeoBoundingBox) { if (!bounds || !collar) return false; // test if bounds top_left is outside collar diff --git a/src/plugins/maps_legacy/public/map/grid_dimensions.js b/src/plugins/maps_legacy/public/map/grid_dimensions.js index d146adf2ca67f..0f84e972104ba 100644 --- a/src/plugins/maps_legacy/public/map/grid_dimensions.js +++ b/src/plugins/maps_legacy/public/map/grid_dimensions.js @@ -17,8 +17,6 @@ * under the License. */ -import _ from 'lodash'; - // geohash precision mapping of geohash grid cell dimensions (width x height, in meters) at equator. // https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator const gridAtEquator = { @@ -37,5 +35,5 @@ const gridAtEquator = { }; export function gridDimensions(precision) { - return _.get(gridAtEquator, precision); + return gridAtEquator[precision]; } diff --git a/src/plugins/maps_legacy/public/map/kibana_map.js b/src/plugins/maps_legacy/public/map/kibana_map.js index ad5d2c089b875..3948692e55676 100644 --- a/src/plugins/maps_legacy/public/map/kibana_map.js +++ b/src/plugins/maps_legacy/public/map/kibana_map.js @@ -20,7 +20,7 @@ import { EventEmitter } from 'events'; import { createZoomWarningMsg } from './map_messages'; import $ from 'jquery'; -import _ from 'lodash'; +import { get, isEqual, escape } from 'lodash'; import { zoomToPrecision } from './zoom_to_precision'; import { i18n } from '@kbn/i18n'; import { ORIGIN } from '../common/constants/origin'; @@ -380,7 +380,7 @@ export class KibanaMap extends EventEmitter { const distanceX = latLngC.distanceTo(latLngX); // calculate distance between c and x (latitude) const distanceY = latLngC.distanceTo(latLngY); // calculate distance between c and y (longitude) - return _.min([distanceX, distanceY]); + return Math.min(distanceX, distanceY); } _getLeafletBounds(resizeOnFail) { @@ -544,7 +544,7 @@ export class KibanaMap extends EventEmitter { } setBaseLayer(settings) { - if (_.isEqual(settings, this._baseLayerSettings)) { + if (isEqual(settings, this._baseLayerSettings)) { return; } @@ -567,7 +567,7 @@ export class KibanaMap extends EventEmitter { let baseLayer; if (settings.baseLayerType === 'wms') { //This is user-input that is rendered with the Leaflet attribution control. Needs to be sanitized. - this._baseLayerSettings.options.attribution = _.escape(settings.options.attribution); + this._baseLayerSettings.options.attribution = escape(settings.options.attribution); baseLayer = this._getWMSBaseLayer(settings.options); } else if (settings.baseLayerType === 'tms') { baseLayer = this._getTMSBaseLayer(settings.options); @@ -661,7 +661,7 @@ export class KibanaMap extends EventEmitter { _updateDesaturation() { const tiles = $('img.leaflet-tile-loaded'); // Don't apply client-side styling to EMS basemaps - if (_.get(this._baseLayerSettings, 'options.origin') === ORIGIN.EMS) { + if (get(this._baseLayerSettings, 'options.origin') === ORIGIN.EMS) { tiles.addClass('filters-off'); } else { if (this._baseLayerIsDesaturated) { diff --git a/src/plugins/maps_legacy/public/map/service_settings.d.ts b/src/plugins/maps_legacy/public/map/service_settings_types.ts similarity index 100% rename from src/plugins/maps_legacy/public/map/service_settings.d.ts rename to src/plugins/maps_legacy/public/map/service_settings_types.ts diff --git a/src/plugins/maps_legacy/public/plugin.ts b/src/plugins/maps_legacy/public/plugin.ts index 8c9f1e9cef194..17cee226cb70c 100644 --- a/src/plugins/maps_legacy/public/plugin.ts +++ b/src/plugins/maps_legacy/public/plugin.ts @@ -22,15 +22,12 @@ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/p // @ts-ignore import { setToasts, setUiSettings, setKibanaVersion, setMapsLegacyConfig } from './kibana_services'; // @ts-ignore -import { ServiceSettings } from './map/service_settings'; -// @ts-ignore import { getPrecision, getZoomPrecision } from './map/precision'; -// @ts-ignore -import { KibanaMap } from './map/kibana_map'; import { MapsLegacyPluginSetup, MapsLegacyPluginStart } from './index'; import { MapsLegacyConfig } from '../config'; // @ts-ignore import { BaseMapsVisualizationProvider } from './map/base_maps_visualization'; +import { getServiceSettings } from './get_service_settings'; /** * These are the interfaces with your public contracts. You should export these @@ -67,17 +64,13 @@ export class MapsLegacyPlugin implements Plugin new KibanaMap(...args); - const getBaseMapsVis = () => - new BaseMapsVisualizationProvider(getKibanaMapFactoryProvider, serviceSettings); + const getBaseMapsVis = () => new BaseMapsVisualizationProvider(); return { - serviceSettings, + getServiceSettings, getZoomPrecision, getPrecision, config, - getKibanaMapFactoryProvider, getBaseMapsVis, }; } diff --git a/src/plugins/region_map/kibana.json b/src/plugins/region_map/kibana.json index bd5517d2a5bf7..e679baf6d6f06 100644 --- a/src/plugins/region_map/kibana.json +++ b/src/plugins/region_map/kibana.json @@ -10,7 +10,8 @@ "expressions", "mapsLegacy", "kibanaLegacy", - "data" + "data", + "share" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/region_map/public/choropleth_layer.js b/src/plugins/region_map/public/choropleth_layer.js index 30fa8b544cdec..14a91e189e0d5 100644 --- a/src/plugins/region_map/public/choropleth_layer.js +++ b/src/plugins/region_map/public/choropleth_layer.js @@ -33,7 +33,7 @@ const EMPTY_STYLE = { fillOpacity: 0, }; -export default class ChoroplethLayer extends KibanaMapLayer { +export class ChoroplethLayer extends KibanaMapLayer { static _doInnerJoin(sortedMetrics, sortedGeojsonFeatures, joinField) { let j = 0; for (let i = 0; i < sortedGeojsonFeatures.length; i++) { @@ -71,7 +71,16 @@ export default class ChoroplethLayer extends KibanaMapLayer { } } - constructor(name, attribution, format, showAllShapes, meta, layerConfig, serviceSettings) { + constructor( + name, + attribution, + format, + showAllShapes, + meta, + layerConfig, + serviceSettings, + leaflet + ) { super(); this._serviceSettings = serviceSettings; this._metrics = null; @@ -84,9 +93,10 @@ export default class ChoroplethLayer extends KibanaMapLayer { this._showAllShapes = showAllShapes; this._layerName = name; this._layerConfig = layerConfig; + this._leaflet = leaflet; // eslint-disable-next-line no-undef - this._leafletLayer = L.geoJson(null, { + this._leafletLayer = this._leaflet.geoJson(null, { onEachFeature: (feature, layer) => { layer.on('click', () => { this.emit('select', feature.properties[this._joinField]); @@ -97,7 +107,7 @@ export default class ChoroplethLayer extends KibanaMapLayer { const tooltipContents = this._tooltipFormatter(feature); if (!location) { // eslint-disable-next-line no-undef - const leafletGeojson = L.geoJson(feature); + const leafletGeojson = this._leaflet.geoJson(feature); location = leafletGeojson.getBounds().getCenter(); } this.emit('showTooltip', { @@ -425,7 +435,7 @@ CORS configuration of the server permits requests from the Kibana application on const { min, max } = getMinMax(this._metrics); // eslint-disable-next-line no-undef - const boundsOfAllFeatures = new L.LatLngBounds(); + const boundsOfAllFeatures = new this._leaflet.LatLngBounds(); return { leafletStyleFunction: (geojsonFeature) => { const match = geojsonFeature.__kbnJoinedMetric; @@ -433,7 +443,7 @@ CORS configuration of the server permits requests from the Kibana application on return emptyStyle(); } // eslint-disable-next-line no-undef - const boundsOfFeature = L.geoJson(geojsonFeature).getBounds(); + const boundsOfFeature = this._leaflet.geoJson(geojsonFeature).getBounds(); boundsOfAllFeatures.extend(boundsOfFeature); return { diff --git a/src/plugins/region_map/public/components/region_map_options.tsx b/src/plugins/region_map/public/components/region_map_options.tsx index be3d7fe86ab3f..4d564d7347a1e 100644 --- a/src/plugins/region_map/public/components/region_map_options.tsx +++ b/src/plugins/region_map/public/components/region_map_options.tsx @@ -37,11 +37,11 @@ const mapFieldForOption = ({ description, name }: FileLayerField) => ({ }); export type RegionMapOptionsProps = { - serviceSettings: IServiceSettings; + getServiceSettings: () => Promise; } & VisOptionsProps; function RegionMapOptions(props: RegionMapOptionsProps) { - const { serviceSettings, stateParams, vis, setValue } = props; + const { getServiceSettings, stateParams, vis, setValue } = props; const { vectorLayers } = vis.type.editorConfig.collections; const vectorLayerOptions = useMemo(() => vectorLayers.map(mapLayerForOption), [vectorLayers]); const fieldOptions = useMemo( @@ -54,10 +54,11 @@ function RegionMapOptions(props: RegionMapOptionsProps) { const setEmsHotLink = useCallback( async (layer: VectorLayer) => { + const serviceSettings = await getServiceSettings(); const emsHotLink = await serviceSettings.getEMSHotLink(layer); setValue('emsHotLink', emsHotLink); }, - [setValue, serviceSettings] + [setValue, getServiceSettings] ); const setLayer = useCallback( diff --git a/src/plugins/region_map/public/get_deprecation_message.tsx b/src/plugins/region_map/public/get_deprecation_message.tsx new file mode 100644 index 0000000000000..ea5cdf42c3111 --- /dev/null +++ b/src/plugins/region_map/public/get_deprecation_message.tsx @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { UrlGeneratorContract } from 'src/plugins/share/public'; +import { getCoreService, getQueryService, getShareService } from './kibana_services'; +import { Vis } from '../../visualizations/public'; +import { LegacyMapDeprecationMessage } from '../../maps_legacy/public'; + +function getEmsLayerId(id: string | number, layerId: string) { + if (typeof id === 'string') { + return id; + } + + // Region maps from 6.x will have numerical EMS id refering to S3 bucket id. + // In this case, use layerId with contains the EMS layer name. + const split = layerId.split('.'); + return split.length === 2 ? split[1] : undefined; +} + +export function getDeprecationMessage(vis: Vis) { + let mapsRegionMapUrlGenerator: + | UrlGeneratorContract<'MAPS_APP_REGION_MAP_URL_GENERATOR'> + | undefined; + try { + mapsRegionMapUrlGenerator = getShareService().urlGenerators.getUrlGenerator( + 'MAPS_APP_REGION_MAP_URL_GENERATOR' + ); + } catch (error) { + // ignore error thrown when url generator is not available + } + + const title = i18n.translate('regionMap.mapVis.regionMapTitle', { defaultMessage: 'Region Map' }); + + async function onClick(e: React.MouseEvent) { + e.preventDefault(); + + const query = getQueryService(); + const createUrlParams: { [key: string]: any } = { + label: vis.title ? vis.title : title, + emsLayerId: vis.params.selectedLayer.isEMS + ? getEmsLayerId(vis.params.selectedLayer.id, vis.params.selectedLayer.layerId) + : undefined, + leftFieldName: vis.params.selectedLayer.isEMS ? vis.params.selectedJoinField.name : undefined, + colorSchema: vis.params.colorSchema, + indexPatternId: vis.data.indexPattern?.id, + indexPatternTitle: vis.data.indexPattern?.title, + metricAgg: 'count', + filters: query.filterManager.getFilters(), + query: query.queryString.getQuery(), + timeRange: query.timefilter.timefilter.getTime(), + }; + + const bucketAggs = vis.data?.aggs?.byType('buckets'); + if (bucketAggs?.length && bucketAggs[0].type.dslName === 'terms') { + createUrlParams.termsFieldName = bucketAggs[0].getField()?.name; + } + + const metricAggs = vis.data?.aggs?.byType('metrics'); + if (metricAggs?.length) { + createUrlParams.metricAgg = metricAggs[0].type.dslName; + createUrlParams.metricFieldName = metricAggs[0].getField()?.name; + } + + const url = await mapsRegionMapUrlGenerator!.createUrl(createUrlParams); + getCoreService().application.navigateToUrl(url); + } + + return ( + + ); +} diff --git a/src/plugins/region_map/public/kibana_services.ts b/src/plugins/region_map/public/kibana_services.ts index 8367325c7415b..7edbf2da36fc7 100644 --- a/src/plugins/region_map/public/kibana_services.ts +++ b/src/plugins/region_map/public/kibana_services.ts @@ -17,10 +17,14 @@ * under the License. */ +import { CoreStart } from 'kibana/public'; import { NotificationsStart } from 'kibana/public'; import { createGetterSetter } from '../../kibana_utils/public'; import { DataPublicPluginStart } from '../../data/public'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { SharePluginStart } from '../../share/public'; + +export const [getCoreService, setCoreService] = createGetterSetter('Core'); export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] @@ -30,6 +34,12 @@ export const [getNotifications, setNotifications] = createGetterSetter('Query'); + +export const [getShareService, setShareService] = createGetterSetter('Share'); + export const [getKibanaLegacy, setKibanaLegacy] = createGetterSetter( 'KibanaLegacy' ); diff --git a/src/plugins/region_map/public/plugin.ts b/src/plugins/region_map/public/plugin.ts index ec9ee94310578..e9978803ad5e2 100644 --- a/src/plugins/region_map/public/plugin.ts +++ b/src/plugins/region_map/public/plugin.ts @@ -31,17 +31,25 @@ import { createRegionMapFn } from './region_map_fn'; // @ts-ignore import { createRegionMapTypeDefinition } from './region_map_type'; import { IServiceSettings, MapsLegacyPluginSetup } from '../../maps_legacy/public'; -import { setFormatService, setNotifications, setKibanaLegacy } from './kibana_services'; +import { + setCoreService, + setFormatService, + setNotifications, + setKibanaLegacy, + setQueryService, + setShareService, +} from './kibana_services'; import { DataPublicPluginStart } from '../../data/public'; import { RegionMapsConfigType } from './index'; import { MapsLegacyConfig } from '../../maps_legacy/config'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { SharePluginStart } from '../../share/public'; /** @private */ interface RegionMapVisualizationDependencies { uiSettings: IUiSettingsClient; regionmapsConfig: RegionMapsConfig; - serviceSettings: IServiceSettings; + getServiceSettings: () => Promise; BaseMapsVisualization: any; } @@ -57,6 +65,7 @@ export interface RegionMapPluginStartDependencies { data: DataPublicPluginStart; notifications: NotificationsStart; kibanaLegacy: KibanaLegacyStart; + share: SharePluginStart; } /** @internal */ @@ -93,7 +102,7 @@ export class RegionMapPlugin implements Plugin = { uiSettings: core.uiSettings, regionmapsConfig: config as RegionMapsConfig, - serviceSettings: mapsLegacy.serviceSettings, + getServiceSettings: mapsLegacy.getServiceSettings, BaseMapsVisualization: mapsLegacy.getBaseMapsVis(), }; @@ -108,10 +117,13 @@ export class RegionMapPlugin implements Plugin , + optionsTemplate: (props) => ( + + ), collections: { colorSchemas: truncatedColorSchemas, vectorLayers: [], @@ -97,6 +101,7 @@ provided base maps, or add your own. Darker colors represent higher values.', ]), }, setup: async (vis) => { + const serviceSettings = await getServiceSettings(); const tmsLayers = await serviceSettings.getTMSServices(); vis.type.editorConfig.collections.tmsLayers = tmsLayers; if (!vis.params.wms.selectedTmsLayer && tmsLayers.length) { diff --git a/src/plugins/region_map/public/region_map_visualization.js b/src/plugins/region_map/public/region_map_visualization.js index 43959c367558f..9b20a35630c86 100644 --- a/src/plugins/region_map/public/region_map_visualization.js +++ b/src/plugins/region_map/public/region_map_visualization.js @@ -18,18 +18,16 @@ */ import { i18n } from '@kbn/i18n'; -import ChoroplethLayer from './choropleth_layer'; import { getFormatService, getNotifications, getKibanaLegacy } from './kibana_services'; import { truncatedColorMaps } from '../../charts/public'; import { tooltipFormatter } from './tooltip_formatter'; -import { mapTooltipProvider, ORIGIN } from '../../maps_legacy/public'; -import _ from 'lodash'; +import { mapTooltipProvider, ORIGIN, lazyLoadMapsLegacyModules } from '../../maps_legacy/public'; export function createRegionMapVisualization({ regionmapsConfig, - serviceSettings, uiSettings, BaseMapsVisualization, + getServiceSettings, }) { return class RegionMapsVisualization extends BaseMapsVisualization { constructor(container, vis) { @@ -71,7 +69,7 @@ export function createRegionMapVisualization({ return; } - this._updateChoroplethLayerForNewMetrics( + await this._updateChoroplethLayerForNewMetrics( selectedLayer.name, selectedLayer.attribution, this._params.showAllShapes, @@ -98,10 +96,13 @@ export function createRegionMapVisualization({ // Do not use the selectedLayer from the visState. // These settings are stored in the URL and can be used to inject dirty display content. + const { escape } = await import('lodash'); + if ( fileLayerConfig.isEMS || //Hosted by EMS. Metadata needs to be resolved through EMS (fileLayerConfig.layerId && fileLayerConfig.layerId.startsWith(`${ORIGIN.EMS}.`)) //fallback for older saved objects ) { + const serviceSettings = await getServiceSettings(); return await serviceSettings.loadFileLayerConfig(fileLayerConfig); } @@ -113,7 +114,7 @@ export function createRegionMapVisualization({ if (configuredLayer) { return { ...configuredLayer, - attribution: _.escape(configuredLayer.attribution ? configuredLayer.attribution : ''), + attribution: escape(configuredLayer.attribution ? configuredLayer.attribution : ''), }; } @@ -133,7 +134,7 @@ export function createRegionMapVisualization({ return; } - this._updateChoroplethLayerForNewProperties( + await this._updateChoroplethLayerForNewProperties( selectedLayer.name, selectedLayer.attribution, this._params.showAllShapes @@ -151,24 +152,24 @@ export function createRegionMapVisualization({ ); } - _updateChoroplethLayerForNewMetrics(name, attribution, showAllData, newMetrics) { + async _updateChoroplethLayerForNewMetrics(name, attribution, showAllData, newMetrics) { if ( this._choroplethLayer && this._choroplethLayer.canReuseInstanceForNewMetrics(name, showAllData, newMetrics) ) { return; } - return this._recreateChoroplethLayer(name, attribution, showAllData); + await this._recreateChoroplethLayer(name, attribution, showAllData); } - _updateChoroplethLayerForNewProperties(name, attribution, showAllData) { + async _updateChoroplethLayerForNewProperties(name, attribution, showAllData) { if (this._choroplethLayer && this._choroplethLayer.canReuseInstance(name, showAllData)) { return; } - return this._recreateChoroplethLayer(name, attribution, showAllData); + await this._recreateChoroplethLayer(name, attribution, showAllData); } - _recreateChoroplethLayer(name, attribution, showAllData) { + async _recreateChoroplethLayer(name, attribution, showAllData) { this._kibanaMap.removeLayer(this._choroplethLayer); if (this._choroplethLayer) { @@ -179,9 +180,10 @@ export function createRegionMapVisualization({ showAllData, this._params.selectedLayer.meta, this._params.selectedLayer, - serviceSettings + await getServiceSettings() ); } else { + const { ChoroplethLayer } = await import('./choropleth_layer'); this._choroplethLayer = new ChoroplethLayer( name, attribution, @@ -189,7 +191,8 @@ export function createRegionMapVisualization({ showAllData, this._params.selectedLayer.meta, this._params.selectedLayer, - serviceSettings + await getServiceSettings(), + (await lazyLoadMapsLegacyModules()).L ); } diff --git a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx index ce08151d37c2c..dfc0c4049774d 100644 --- a/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx +++ b/src/plugins/saved_objects/public/save_modal/saved_object_save_modal_origin.tsx @@ -33,6 +33,8 @@ interface SaveModalDocumentInfo { interface OriginSaveModalProps { originatingApp?: string; getAppNameFromId?: (appId: string) => string | undefined; + originatingAppName?: string; + returnToOriginSwitchLabel?: string; documentInfo: SaveModalDocumentInfo; objectType: string; onClose: () => void; @@ -73,11 +75,13 @@ export function SavedObjectSaveModalOrigin(props: OriginSaveModalProps) { setReturnToOriginMode(event.target.checked); }} label={ - + props.returnToOriginSwitchLabel ?? ( + + ) } /> diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts index 53abe55ef0ea7..849e11dc3dd9f 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts @@ -26,10 +26,9 @@ import { SavedObjectSaveOpts, } from '../types'; -// @ts-ignore -import StubIndexPattern from 'test_utils/stub_index_pattern'; import { coreMock } from '../../../../core/public/mocks'; import { dataPluginMock, createSearchSourceMock } from '../../../../plugins/data/public/mocks'; +import { getStubIndexPattern, StubIndexPattern } from '../../../../plugins/data/public/test_utils'; import { SavedObjectAttributes, SimpleSavedObject } from 'kibana/public'; import { IIndexPattern } from '../../../data/common/index_patterns'; @@ -294,14 +293,14 @@ describe('Saved Object', () => { type: 'dashboard', } as SimpleSavedObject); - const indexPattern = new StubIndexPattern( + const indexPattern = getStubIndexPattern( 'my-index', getConfig, null, [], coreMock.createSetup() ); - indexPattern.title = indexPattern.id; + indexPattern.title = indexPattern.id!; savedObject.searchSource!.setField('index', indexPattern); return savedObject.save(saveOptionsMock).then(() => { const args = (savedObjectsClientStub.create as jest.Mock).mock.calls[0]; @@ -335,7 +334,7 @@ describe('Saved Object', () => { type: 'dashboard', } as SimpleSavedObject); - const indexPattern = new StubIndexPattern( + const indexPattern = getStubIndexPattern( 'non-existant-index', getConfig, null, @@ -662,14 +661,14 @@ describe('Saved Object', () => { const savedObject = new SavedObjectClass(config); savedObject.hydrateIndexPattern = jest.fn().mockImplementation(() => { - const indexPattern = new StubIndexPattern( + const indexPattern = getStubIndexPattern( indexPatternId, getConfig, null, [], coreMock.createSetup() ); - indexPattern.title = indexPattern.id; + indexPattern.title = indexPattern.id!; savedObject.searchSource!.setField('index', indexPattern); return Bluebird.resolve(indexPattern); }); diff --git a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts index 679ea5ffc23ee..eb95c213e680d 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_saved_objects.ts @@ -24,10 +24,11 @@ import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public'; import { DataPublicPluginStart, IndexPatternsContract, - IIndexPattern, injectSearchSourceReferences, + IndexPatternSpec, } from '../../../data/public'; import { FailedImport } from './process_import_response'; +import { DuplicateIndexPatternError, IndexPattern } from '../../../data/public'; type SavedObjectsRawDoc = Record; @@ -70,11 +71,10 @@ function addJsonFieldToIndexPattern( async function importIndexPattern( doc: SavedObjectsRawDoc, indexPatterns: IndexPatternsContract, - overwriteAll: boolean, + overwriteAll: boolean = false, openConfirm: OverlayStart['openConfirm'] ) { // TODO: consolidate this is the code in create_index_pattern_wizard.js - const emptyPattern = await indexPatterns.make(); const { title, timeFieldName, @@ -84,50 +84,53 @@ async function importIndexPattern( type, typeMeta, } = doc._source; - const importedIndexPattern = { + const indexPatternSpec: IndexPatternSpec = { id: doc._id, title, timeFieldName, - } as IIndexPattern; + }; + let emptyPattern: IndexPattern; if (type) { - importedIndexPattern.type = type; + indexPatternSpec.type = type; } - addJsonFieldToIndexPattern(importedIndexPattern, fields, 'fields', title); - addJsonFieldToIndexPattern(importedIndexPattern, fieldFormatMap, 'fieldFormatMap', title); - addJsonFieldToIndexPattern(importedIndexPattern, sourceFilters, 'sourceFilters', title); - addJsonFieldToIndexPattern(importedIndexPattern, typeMeta, 'typeMeta', title); - Object.assign(emptyPattern, importedIndexPattern); - - let newId = await emptyPattern.create(overwriteAll); - if (!newId) { - // We can override and we want to prompt for confirmation - const isConfirmed = await openConfirm( - i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteLabel', { - values: { title }, - defaultMessage: "Are you sure you want to overwrite '{title}'?", - }), - { - title: i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteTitle', { - defaultMessage: 'Overwrite {type}?', - values: { type }, + addJsonFieldToIndexPattern(indexPatternSpec, fields, 'fields', title); + addJsonFieldToIndexPattern(indexPatternSpec, fieldFormatMap, 'fieldFormatMap', title); + addJsonFieldToIndexPattern(indexPatternSpec, sourceFilters, 'sourceFilters', title); + addJsonFieldToIndexPattern(indexPatternSpec, typeMeta, 'typeMeta', title); + try { + emptyPattern = await indexPatterns.createAndSave(indexPatternSpec, overwriteAll, true); + } catch (err) { + if (err instanceof DuplicateIndexPatternError) { + // We can override and we want to prompt for confirmation + const isConfirmed = await openConfirm( + i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteLabel', { + values: { title }, + defaultMessage: "Are you sure you want to overwrite '{title}'?", }), - confirmButtonText: i18n.translate( - 'savedObjectsManagement.indexPattern.confirmOverwriteButton', - { - defaultMessage: 'Overwrite', - } - ), - } - ); + { + title: i18n.translate('savedObjectsManagement.indexPattern.confirmOverwriteTitle', { + defaultMessage: 'Overwrite {type}?', + values: { type }, + }), + confirmButtonText: i18n.translate( + 'savedObjectsManagement.indexPattern.confirmOverwriteButton', + { + defaultMessage: 'Overwrite', + } + ), + } + ); - if (isConfirmed) { - newId = (await emptyPattern.create(true)) as string; - } else { - return; + if (isConfirmed) { + emptyPattern = await indexPatterns.createAndSave(indexPatternSpec, true, true); + } else { + return; + } } } - indexPatterns.clearCache(newId); - return newId; + + indexPatterns.clearCache(emptyPattern!.id); + return emptyPattern!.id; } async function importDocument(obj: SavedObject, doc: SavedObjectsRawDoc, overwriteAll: boolean) { diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 5bce03a292760..6531262b6f1da 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -29,7 +29,10 @@ "sample-data": { "properties": { "installed": { - "type": "keyword" + "type": "array", + "items": { + "type": "keyword" + } }, "last_install_date": { "type": "date" @@ -44,7 +47,10 @@ "type": "keyword" }, "uninstalled": { - "type": "keyword" + "type": "array", + "items": { + "type": "keyword" + } } } }, @@ -1297,6 +1303,326 @@ } } }, + "kibana": { + "properties": { + "index": { + "type": "keyword" + }, + "dashboard": { + "properties": { + "total": { + "type": "long" + } + } + }, + "visualization": { + "properties": { + "total": { + "type": "long" + } + } + }, + "search": { + "properties": { + "total": { + "type": "long" + } + } + }, + "index_pattern": { + "properties": { + "total": { + "type": "long" + } + } + }, + "graph_workspace": { + "properties": { + "total": { + "type": "long" + } + } + }, + "timelion_sheet": { + "properties": { + "total": { + "type": "long" + } + } + } + } + }, + "stack_management": { + "properties": { + "visualize:enableLabs": { + "type": "boolean" + }, + "visualization:heatmap:maxBuckets": { + "type": "long" + }, + "visualization:colorMapping": { + "type": "text" + }, + "visualization:regionmap:showWarnings": { + "type": "boolean" + }, + "visualization:dimmingOpacity": { + "type": "float" + }, + "visualization:tileMap:maxPrecision": { + "type": "long" + }, + "securitySolution:ipReputationLinks": { + "type": "text" + }, + "csv:separator": { + "type": "keyword" + }, + "visualization:tileMap:WMSdefaults": { + "type": "text" + }, + "timelion:target_buckets": { + "type": "long" + }, + "timelion:max_buckets": { + "type": "long" + }, + "timelion:es.timefield": { + "type": "keyword" + }, + "timelion:min_interval": { + "type": "keyword" + }, + "timelion:default_rows": { + "type": "long" + }, + "timelion:default_columns": { + "type": "long" + }, + "timelion:quandl.key": { + "type": "keyword" + }, + "timelion:es.default_index": { + "type": "keyword" + }, + "timelion:showTutorial": { + "type": "boolean" + }, + "securitySolution:timeDefaults": { + "type": "keyword" + }, + "securitySolution:defaultAnomalyScore": { + "type": "long" + }, + "securitySolution:defaultIndex": { + "type": "keyword" + }, + "securitySolution:refreshIntervalDefaults": { + "type": "keyword" + }, + "securitySolution:newsFeedUrl": { + "type": "keyword" + }, + "securitySolution:enableNewsFeed": { + "type": "boolean" + }, + "search:includeFrozen": { + "type": "boolean" + }, + "courier:maxConcurrentShardRequests": { + "type": "long" + }, + "courier:batchSearches": { + "type": "boolean" + }, + "courier:setRequestPreference": { + "type": "keyword" + }, + "courier:customRequestPreference": { + "type": "keyword" + }, + "courier:ignoreFilterIfFieldNotInIndex": { + "type": "boolean" + }, + "rollups:enableIndexPatterns": { + "type": "boolean" + }, + "xpackReporting:customPdfLogo": { + "type": "text" + }, + "notifications:lifetime:warning": { + "type": "long" + }, + "notifications:lifetime:banner": { + "type": "long" + }, + "notifications:lifetime:info": { + "type": "long" + }, + "notifications:banner": { + "type": "text" + }, + "notifications:lifetime:error": { + "type": "long" + }, + "doc_table:highlight": { + "type": "boolean" + }, + "discover:searchOnPageLoad": { + "type": "boolean" + }, + "doc_table:hideTimeColumn": { + "type": "boolean" + }, + "discover:sampleSize": { + "type": "long" + }, + "defaultColumns": { + "type": "keyword" + }, + "context:defaultSize": { + "type": "long" + }, + "discover:aggs:terms:size": { + "type": "long" + }, + "context:tieBreakerFields": { + "type": "keyword" + }, + "discover:sort:defaultOrder": { + "type": "keyword" + }, + "context:step": { + "type": "long" + }, + "accessibility:disableAnimations": { + "type": "boolean" + }, + "ml:fileDataVisualizerMaxFileSize": { + "type": "keyword" + }, + "ml:anomalyDetection:results:enableTimeDefaults": { + "type": "boolean" + }, + "ml:anomalyDetection:results:timeDefaults": { + "type": "keyword" + }, + "truncate:maxHeight": { + "type": "long" + }, + "timepicker:timeDefaults": { + "type": "keyword" + }, + "timepicker:refreshIntervalDefaults": { + "type": "keyword" + }, + "timepicker:quickRanges": { + "type": "keyword" + }, + "theme:version": { + "type": "keyword" + }, + "theme:darkMode": { + "type": "boolean" + }, + "state:storeInSessionStorage": { + "type": "boolean" + }, + "savedObjects:perPage": { + "type": "long" + }, + "search:queryLanguage": { + "type": "keyword" + }, + "shortDots:enable": { + "type": "boolean" + }, + "sort:options": { + "type": "keyword" + }, + "savedObjects:listingLimit": { + "type": "long" + }, + "query:queryString:options": { + "type": "keyword" + }, + "pageNavigation": { + "type": "keyword" + }, + "metrics:max_buckets": { + "type": "long" + }, + "query:allowLeadingWildcards": { + "type": "boolean" + }, + "metaFields": { + "type": "keyword" + }, + "indexPattern:placeholder": { + "type": "keyword" + }, + "histogram:barTarget": { + "type": "long" + }, + "histogram:maxBars": { + "type": "long" + }, + "format:number:defaultLocale": { + "type": "keyword" + }, + "format:percent:defaultPattern": { + "type": "keyword" + }, + "format:number:defaultPattern": { + "type": "keyword" + }, + "history:limit": { + "type": "long" + }, + "format:defaultTypeMap": { + "type": "keyword" + }, + "format:currency:defaultPattern": { + "type": "keyword" + }, + "defaultIndex": { + "type": "keyword" + }, + "format:bytes:defaultPattern": { + "type": "keyword" + }, + "filters:pinnedByDefault": { + "type": "boolean" + }, + "filterEditor:suggestValues": { + "type": "boolean" + }, + "fields:popularLimit": { + "type": "long" + }, + "dateNanosFormat": { + "type": "keyword" + }, + "defaultRoute": { + "type": "keyword" + }, + "dateFormat:tz": { + "type": "keyword" + }, + "dateFormat:scaled": { + "type": "keyword" + }, + "csv:quoteValues": { + "type": "boolean" + }, + "dateFormat:dow": { + "type": "keyword" + }, + "dateFormat": { + "type": "keyword" + } + } + }, "telemetry": { "properties": { "opt_in_status": { @@ -1310,6 +1636,121 @@ } } }, + "static_telemetry": { + "properties": { + "ece": { + "properties": { + "kb_uuid": { + "type": "keyword" + }, + "es_uuid": { + "type": "keyword" + }, + "account_id": { + "type": "keyword" + }, + "license": { + "properties": { + "uuid": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "issued_to": { + "type": "text" + }, + "issuer": { + "type": "text" + }, + "issue_date_in_millis": { + "type": "long" + }, + "start_date_in_millis": { + "type": "long" + }, + "expiry_date_in_millis": { + "type": "long" + }, + "max_resource_units": { + "type": "long" + } + } + } + } + }, + "ess": { + "properties": { + "kb_uuid": { + "type": "keyword" + }, + "es_uuid": { + "type": "keyword" + }, + "account_id": { + "type": "keyword" + }, + "license": { + "properties": { + "uuid": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "issued_to": { + "type": "text" + }, + "issuer": { + "type": "text" + }, + "issue_date_in_millis": { + "type": "long" + }, + "start_date_in_millis": { + "type": "long" + }, + "expiry_date_in_millis": { + "type": "long" + }, + "max_resource_units": { + "type": "long" + } + } + } + } + }, + "eck": { + "properties": { + "operator_uuid": { + "type": "keyword" + }, + "operator_roles": { + "type": "keyword" + }, + "custom_operator_namespace": { + "type": "boolean" + }, + "distribution": { + "type": "text" + }, + "build": { + "properties": { + "hash": { + "type": "text" + }, + "date": { + "type": "date" + }, + "version": { + "type": "keyword" + } + } + } + } + } + } + }, "tsvb-validation": { "properties": { "failed_validations": { diff --git a/src/plugins/telemetry/server/collectors/usage/schema.ts b/src/plugins/telemetry/server/collectors/usage/schema.ts new file mode 100644 index 0000000000000..4bfb6f75c7c8f --- /dev/null +++ b/src/plugins/telemetry/server/collectors/usage/schema.ts @@ -0,0 +1,58 @@ +/* + * 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 { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { LicenseUsage, StaticTelemetryUsage } from './telemetry_usage_collector'; + +const licenseSchema: MakeSchemaFrom = { + uuid: { type: 'keyword' }, + type: { type: 'keyword' }, + issued_to: { type: 'text' }, + issuer: { type: 'text' }, + issue_date_in_millis: { type: 'long' }, + start_date_in_millis: { type: 'long' }, + expiry_date_in_millis: { type: 'long' }, + max_resource_units: { type: 'long' }, +}; + +export const staticTelemetrySchema: MakeSchemaFrom = { + ece: { + kb_uuid: { type: 'keyword' }, + es_uuid: { type: 'keyword' }, + account_id: { type: 'keyword' }, + license: licenseSchema, + }, + ess: { + kb_uuid: { type: 'keyword' }, + es_uuid: { type: 'keyword' }, + account_id: { type: 'keyword' }, + license: licenseSchema, + }, + eck: { + operator_uuid: { type: 'keyword' }, + operator_roles: { type: 'keyword' }, + custom_operator_namespace: { type: 'boolean' }, + distribution: { type: 'text' }, + build: { + hash: { type: 'text' }, + date: { type: 'date' }, + version: { type: 'keyword' }, + }, + }, +}; diff --git a/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts index bde7cfa5c4445..39f8ef0151a0b 100644 --- a/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts +++ b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts @@ -29,6 +29,7 @@ import { TelemetryConfigType } from '../../config'; // look for telemetry.yml in the same places we expect kibana.yml import { ensureDeepObject } from './ensure_deep_object'; +import { staticTelemetrySchema } from './schema'; /** * The maximum file size before we ignore it (note: this limit is arbitrary). @@ -60,10 +61,12 @@ export function isFileReadable(path: string): boolean { * @param configPath The config file path. * @returns The unmodified JSON object if the file exists and is a valid YAML file. */ -export async function readTelemetryFile(path: string): Promise { +export async function readTelemetryFile( + configPath: string +): Promise { try { - if (isFileReadable(path)) { - const yaml = readFileSync(path); + if (isFileReadable(configPath)) { + const yaml = readFileSync(configPath); const data = safeLoad(yaml.toString()); // don't bother returning empty objects @@ -79,11 +82,48 @@ export async function readTelemetryFile(path: string): Promise Promise ) { - return usageCollection.makeUsageCollector({ + return usageCollection.makeUsageCollector({ type: 'static_telemetry', isReady: () => true, fetch: async () => { @@ -91,6 +131,7 @@ export function createTelemetryUsageCollector( const telemetryPath = join(dirname(configPath), 'telemetry.yml'); return await readTelemetryFile(telemetryPath); }, + schema: staticTelemetrySchema, }); } diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts index 005c5f96d98d0..dfbbe3355e69c 100644 --- a/src/plugins/telemetry/server/plugin.ts +++ b/src/plugins/telemetry/server/plugin.ts @@ -34,6 +34,7 @@ import { SavedObjectsClient, Plugin, Logger, + IClusterClient, } from '../../../core/server'; import { registerRoutes } from './routes'; import { registerCollection } from './telemetry_collection'; @@ -83,6 +84,7 @@ export class TelemetryPlugin implements Plugin) { this.logger = initializerContext.logger.get(); @@ -102,8 +104,11 @@ export class TelemetryPlugin implements Plugin this.elasticsearchClient + ); const router = http.createRouter(); registerRoutes({ @@ -126,14 +131,12 @@ export class TelemetryPlugin implements Plugin { - const { savedObjects, uiSettings } = core; + public async start(core: CoreStart, { telemetryCollectionManager }: TelemetryPluginsDepsStart) { + const { savedObjects, uiSettings, elasticsearch } = core; this.savedObjectsClient = savedObjects.createInternalRepository(); const savedObjectsClient = new SavedObjectsClient(this.savedObjectsClient); this.uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + this.elasticsearchClient = elasticsearch.client; try { await handleOldSettings(savedObjectsClient, this.uiSettingsClient); diff --git a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_info.js b/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_info.js deleted file mode 100644 index fe83b76cd1158..0000000000000 --- a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_info.js +++ /dev/null @@ -1,39 +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 expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { getClusterInfo } from '../get_cluster_info'; - -export function mockGetClusterInfo(callCluster, clusterInfo, req) { - callCluster.withArgs(req, 'info').returns(clusterInfo); - callCluster.withArgs('info').returns(clusterInfo); -} - -describe('get_cluster_info', () => { - it('uses callCluster to get info API', () => { - const callCluster = sinon.stub(); - const response = Promise.resolve({}); - - mockGetClusterInfo(callCluster, response); - - expect(getClusterInfo(callCluster)).to.be(response); - }); -}); diff --git a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js b/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js deleted file mode 100644 index d1354608385f6..0000000000000 --- a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_cluster_stats.js +++ /dev/null @@ -1,49 +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 expect from '@kbn/expect'; -import sinon from 'sinon'; - -import { TIMEOUT } from '../constants'; -import { getClusterStats } from '../get_cluster_stats'; - -export function mockGetClusterStats(callCluster, clusterStats, req) { - callCluster - .withArgs(req, 'cluster.stats', { - timeout: TIMEOUT, - }) - .returns(clusterStats); - - callCluster - .withArgs('cluster.stats', { - timeout: TIMEOUT, - }) - .returns(clusterStats); -} - -describe.skip('get_cluster_stats', () => { - it('uses callCluster to get cluster.stats API', async () => { - const callCluster = sinon.stub(); - const response = Promise.resolve({}); - - mockGetClusterStats(callCluster, response); - - expect(getClusterStats(callCluster)).to.be(response); - }); -}); diff --git a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js b/src/plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js deleted file mode 100644 index 8541745faea3b..0000000000000 --- a/src/plugins/telemetry/server/telemetry_collection/__tests__/get_local_stats.js +++ /dev/null @@ -1,267 +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 expect from '@kbn/expect'; -import sinon from 'sinon'; -import { merge, omit } from 'lodash'; - -import { TIMEOUT } from '../constants'; -import { mockGetClusterInfo } from './get_cluster_info'; -import { mockGetClusterStats } from './get_cluster_stats'; - -import { getLocalStats, handleLocalStats } from '../get_local_stats'; - -const mockUsageCollection = (kibanaUsage = {}) => ({ - bulkFetch: () => kibanaUsage, - toObject: (data) => data, -}); - -const getMockServer = (getCluster = sinon.stub()) => ({ - log(tags, message) { - console.log({ tags, message }); - }, - config() { - return { - get(item) { - switch (item) { - case 'pkg.version': - return '8675309-snapshot'; - default: - throw Error(`unexpected config.get('${item}') received.`); - } - }, - }; - }, - plugins: { - elasticsearch: { getCluster }, - }, -}); -function mockGetNodesUsage(callCluster, nodesUsage, req) { - callCluster - .withArgs( - req, - { - method: 'GET', - path: '/_nodes/usage', - query: { - timeout: TIMEOUT, - }, - }, - 'transport.request' - ) - .returns(nodesUsage); -} - -function mockGetLocalStats(callCluster, clusterInfo, clusterStats, nodesUsage, req) { - mockGetClusterInfo(callCluster, clusterInfo, req); - mockGetClusterStats(callCluster, clusterStats, req); - mockGetNodesUsage(callCluster, nodesUsage, req); -} - -describe('get_local_stats', () => { - const clusterUuid = 'abc123'; - const clusterName = 'my-cool-cluster'; - const version = '2.3.4'; - const clusterInfo = { - cluster_uuid: clusterUuid, - cluster_name: clusterName, - version: { - number: version, - }, - }; - const nodesUsage = [ - { - node_id: 'some_node_id', - timestamp: 1588617023177, - since: 1588616945163, - rest_actions: { - nodes_usage_action: 1, - create_index_action: 1, - document_get_action: 1, - search_action: 19, - nodes_info_action: 36, - }, - aggregations: { - terms: { - bytes: 2, - }, - scripted_metric: { - other: 7, - }, - }, - }, - ]; - const clusterStats = { - _nodes: { failed: 123 }, - cluster_name: 'real-cool', - indices: { totally: 456 }, - nodes: { yup: 'abc' }, - random: 123, - }; - - const kibana = { - kibana: { - great: 'googlymoogly', - versions: [{ version: '8675309', count: 1 }], - }, - kibana_stats: { - os: { - platform: 'rocky', - platformRelease: 'iv', - }, - }, - localization: { - locale: 'en', - labelsCount: 0, - integrities: {}, - }, - sun: { chances: 5 }, - clouds: { chances: 95 }, - rain: { chances: 2 }, - snow: { chances: 0 }, - }; - - const clusterStatsWithNodesUsage = { - ...clusterStats, - nodes: merge(clusterStats.nodes, { usage: nodesUsage }), - }; - const combinedStatsResult = { - collection: 'local', - cluster_uuid: clusterUuid, - cluster_name: clusterName, - version, - cluster_stats: omit(clusterStatsWithNodesUsage, '_nodes', 'cluster_name'), - stack_stats: { - kibana: { - great: 'googlymoogly', - count: 1, - indices: 1, - os: { - platforms: [{ platform: 'rocky', count: 1 }], - platformReleases: [{ platformRelease: 'iv', count: 1 }], - }, - versions: [{ version: '8675309', count: 1 }], - plugins: { - localization: { - locale: 'en', - labelsCount: 0, - integrities: {}, - }, - sun: { chances: 5 }, - clouds: { chances: 95 }, - rain: { chances: 2 }, - snow: { chances: 0 }, - }, - }, - }, - }; - - const context = { - logger: console, - version: '8.0.0', - }; - - describe('handleLocalStats', () => { - it('returns expected object without xpack and kibana data', () => { - const result = handleLocalStats( - clusterInfo, - clusterStatsWithNodesUsage, - void 0, - void 0, - context - ); - expect(result.cluster_uuid).to.eql(combinedStatsResult.cluster_uuid); - expect(result.cluster_name).to.eql(combinedStatsResult.cluster_name); - expect(result.cluster_stats).to.eql(combinedStatsResult.cluster_stats); - expect(result.version).to.be('2.3.4'); - expect(result.collection).to.be('local'); - expect(result.license).to.be(undefined); - expect(result.stack_stats).to.eql({ kibana: undefined, data: undefined }); - }); - - it('returns expected object with xpack', () => { - const result = handleLocalStats( - clusterInfo, - clusterStatsWithNodesUsage, - void 0, - void 0, - context - ); - const { stack_stats: stack, ...cluster } = result; - expect(cluster.collection).to.be(combinedStatsResult.collection); - expect(cluster.cluster_uuid).to.be(combinedStatsResult.cluster_uuid); - expect(cluster.cluster_name).to.be(combinedStatsResult.cluster_name); - expect(stack.kibana).to.be(undefined); // not mocked for this test - expect(stack.data).to.be(undefined); // not mocked for this test - - expect(cluster.version).to.eql(combinedStatsResult.version); - expect(cluster.cluster_stats).to.eql(combinedStatsResult.cluster_stats); - expect(cluster.license).to.eql(combinedStatsResult.license); - expect(stack.xpack).to.eql(combinedStatsResult.stack_stats.xpack); - }); - }); - - describe.skip('getLocalStats', () => { - it('returns expected object without xpack data when X-Pack fails to respond', async () => { - const callClusterUsageFailed = sinon.stub(); - const usageCollection = mockUsageCollection(); - mockGetLocalStats( - callClusterUsageFailed, - Promise.resolve(clusterInfo), - Promise.resolve(clusterStats), - Promise.resolve(nodesUsage) - ); - const result = await getLocalStats([], { - server: getMockServer(), - callCluster: callClusterUsageFailed, - usageCollection, - }); - expect(result.cluster_uuid).to.eql(combinedStatsResult.cluster_uuid); - expect(result.cluster_name).to.eql(combinedStatsResult.cluster_name); - expect(result.cluster_stats).to.eql(combinedStatsResult.cluster_stats); - expect(result.cluster_stats.nodes).to.eql(combinedStatsResult.cluster_stats.nodes); - expect(result.version).to.be('2.3.4'); - expect(result.collection).to.be('local'); - - // license and xpack usage info come from the same cluster call - expect(result.license).to.be(undefined); - expect(result.stack_stats.xpack).to.be(undefined); - }); - - it('returns expected object with xpack and kibana data', async () => { - const callCluster = sinon.stub(); - const usageCollection = mockUsageCollection(kibana); - mockGetLocalStats( - callCluster, - Promise.resolve(clusterInfo), - Promise.resolve(clusterStats), - Promise.resolve(nodesUsage) - ); - - const result = await getLocalStats([], { - server: getMockServer(callCluster), - usageCollection, - callCluster, - }); - - expect(result.stack_stats.xpack).to.eql(combinedStatsResult.stack_stats.xpack); - expect(result.stack_stats.kibana).to.eql(combinedStatsResult.stack_stats.kibana); - }); - }); -}); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.test.ts new file mode 100644 index 0000000000000..459b18d252e17 --- /dev/null +++ b/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.test.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 { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; +import { getClusterInfo } from './get_cluster_info'; + +export function mockGetClusterInfo(clusterInfo: any) { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.info + // @ts-ignore we only care about the response body + .mockResolvedValue( + // @ts-ignore we only care about the response body + { + body: { ...clusterInfo }, + } + ); + return esClient; +} + +describe('get_cluster_info using the elasticsearch client', () => { + it('uses the esClient to get info API', async () => { + const clusterInfo = { + cluster_uuid: '1234', + cluster_name: 'testCluster', + version: { + number: '7.9.2', + build_flavor: 'default', + build_type: 'docker', + build_hash: 'b5ca9c58fb664ca8bf', + build_date: '2020-07-21T16:40:44.668009Z', + build_snapshot: false, + lucene_version: '8.5.1', + minimum_wire_compatibility_version: '6.8.0', + minimum_index_compatibility_version: '6.0.0-beta1', + }, + }; + const esClient = mockGetClusterInfo(clusterInfo); + + expect(await getClusterInfo(esClient)).toStrictEqual(clusterInfo); + }); +}); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.ts b/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.ts index 4a33356ee9761..407f3325c3a9f 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_cluster_info.ts @@ -17,7 +17,7 @@ * under the License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'src/core/server'; // This can be removed when the ES client improves the types export interface ESClusterInfo { @@ -25,24 +25,24 @@ export interface ESClusterInfo { cluster_name: string; version: { number: string; - build_flavor: string; - build_type: string; - build_hash: string; - build_date: string; + build_flavor?: string; + build_type?: string; + build_hash?: string; + build_date?: string; build_snapshot?: boolean; - lucene_version: string; - minimum_wire_compatibility_version: string; - minimum_index_compatibility_version: string; + lucene_version?: string; + minimum_wire_compatibility_version?: string; + minimum_index_compatibility_version?: string; }; } - /** * Get the cluster info from the connected cluster. * * This is the equivalent to GET / * - * @param {function} callCluster The callWithInternalUser handler (exposed for testing) + * @param {function} esClient The asInternalUser handler (exposed for testing) */ -export function getClusterInfo(callCluster: LegacyAPICaller) { - return callCluster('info'); +export async function getClusterInfo(esClient: ElasticsearchClient) { + const { body } = await esClient.info(); + return body; } diff --git a/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.test.ts new file mode 100644 index 0000000000000..81551c0c4d93d --- /dev/null +++ b/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.test.ts @@ -0,0 +1,44 @@ +/* + * 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 { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; +import { getClusterStats } from './get_cluster_stats'; +import { TIMEOUT } from './constants'; + +export function mockGetClusterStats(clusterStats: any) { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.cluster.stats.mockResolvedValue(clusterStats); + return esClient; +} + +describe('get_cluster_stats', () => { + it('uses the esClient to get the response from the `cluster.stats` API', async () => { + const response = Promise.resolve({ body: { cluster_uuid: '1234' } }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.cluster.stats.mockImplementationOnce( + // @ts-ignore the method only cares about the response body + async (_params = { timeout: TIMEOUT }) => { + return response; + } + ); + const result = getClusterStats(esClient); + expect(esClient.cluster.stats).toHaveBeenCalledWith({ timeout: TIMEOUT }); + expect(result).toStrictEqual(response); + }); +}); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts b/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts index d7c0110a99c6f..d2a64e4878679 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_cluster_stats.ts @@ -18,23 +18,23 @@ */ import { ClusterDetailsGetter } from 'src/plugins/telemetry_collection_manager/server'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'src/core/server'; import { TIMEOUT } from './constants'; /** * Get the cluster stats from the connected cluster. * * This is the equivalent to GET /_cluster/stats?timeout=30s. */ -export async function getClusterStats(callCluster: LegacyAPICaller) { - return await callCluster('cluster.stats', { - timeout: TIMEOUT, - }); +export async function getClusterStats(esClient: ElasticsearchClient) { + const { body } = await esClient.cluster.stats({ timeout: TIMEOUT }); + return body; } /** * Get the cluster uuids from the connected cluster. */ -export const getClusterUuids: ClusterDetailsGetter = async ({ callCluster }) => { - const result = await getClusterStats(callCluster); - return [{ clusterUuid: result.cluster_uuid }]; +export const getClusterUuids: ClusterDetailsGetter = async ({ esClient }) => { + const { body } = await esClient.cluster.stats({ timeout: TIMEOUT }); + + return [{ clusterUuid: body.cluster_uuid }]; }; diff --git a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.test.ts index dee718decdc1f..bb5eb7f6b726d 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.test.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.test.ts @@ -19,6 +19,7 @@ import { buildDataTelemetryPayload, getDataTelemetry } from './get_data_telemetry'; import { DATA_DATASETS_INDEX_PATTERNS, DATA_DATASETS_INDEX_PATTERNS_UNIQUE } from './constants'; +import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; describe('get_data_telemetry', () => { describe('DATA_DATASETS_INDEX_PATTERNS', () => { @@ -195,13 +196,15 @@ describe('get_data_telemetry', () => { describe('getDataTelemetry', () => { test('it returns the base payload (all 0s) because no indices are found', async () => { - const callCluster = mockCallCluster(); - await expect(getDataTelemetry(callCluster)).resolves.toStrictEqual([]); + const esClient = mockEsClient(); + await expect(getDataTelemetry(esClient)).resolves.toStrictEqual([]); + expect(esClient.indices.getMapping).toHaveBeenCalledTimes(1); + expect(esClient.indices.stats).toHaveBeenCalledTimes(1); }); test('can only see the index mappings, but not the stats', async () => { - const callCluster = mockCallCluster(['filebeat-12314']); - await expect(getDataTelemetry(callCluster)).resolves.toStrictEqual([ + const esClient = mockEsClient(['filebeat-12314']); + await expect(getDataTelemetry(esClient)).resolves.toStrictEqual([ { pattern_name: 'filebeat', shipper: 'filebeat', @@ -209,10 +212,12 @@ describe('get_data_telemetry', () => { ecs_index_count: 0, }, ]); + expect(esClient.indices.getMapping).toHaveBeenCalledTimes(1); + expect(esClient.indices.stats).toHaveBeenCalledTimes(1); }); test('can see the mappings and the stats', async () => { - const callCluster = mockCallCluster( + const esClient = mockEsClient( ['filebeat-12314'], { isECS: true }, { @@ -221,7 +226,7 @@ describe('get_data_telemetry', () => { }, } ); - await expect(getDataTelemetry(callCluster)).resolves.toStrictEqual([ + await expect(getDataTelemetry(esClient)).resolves.toStrictEqual([ { pattern_name: 'filebeat', shipper: 'filebeat', @@ -234,7 +239,7 @@ describe('get_data_telemetry', () => { }); test('find an index that does not match any index pattern but has mappings metadata', async () => { - const callCluster = mockCallCluster( + const esClient = mockEsClient( ['cannot_match_anything'], { isECS: true, dataStreamType: 'traces', shipper: 'my-beat' }, { @@ -245,7 +250,7 @@ describe('get_data_telemetry', () => { }, } ); - await expect(getDataTelemetry(callCluster)).resolves.toStrictEqual([ + await expect(getDataTelemetry(esClient)).resolves.toStrictEqual([ { data_stream: { dataset: undefined, type: 'traces' }, shipper: 'my-beat', @@ -258,45 +263,51 @@ describe('get_data_telemetry', () => { }); test('return empty array when there is an error', async () => { - const callCluster = jest.fn().mockRejectedValue(new Error('Something went terribly wrong')); - await expect(getDataTelemetry(callCluster)).resolves.toStrictEqual([]); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.indices.getMapping.mockRejectedValue(new Error('Something went terribly wrong')); + esClient.indices.stats.mockRejectedValue(new Error('Something went terribly wrong')); + await expect(getDataTelemetry(esClient)).resolves.toStrictEqual([]); }); }); }); - -function mockCallCluster( - indicesMappings: string[] = [], +function mockEsClient( + indicesMappings: string[] = [], // an array of `indices` to get mappings from. { isECS = false, dataStreamDataset = '', dataStreamType = '', shipper = '' } = {}, indexStats: any = {} ) { - return jest.fn().mockImplementation(async (method: string, opts: any) => { - if (method === 'indices.getMapping') { - return Object.fromEntries( - indicesMappings.map((index) => [ - index, - { - mappings: { - ...(shipper && { _meta: { beat: shipper } }), - properties: { - ...(isECS && { ecs: { properties: { version: { type: 'keyword' } } } }), - ...((dataStreamType || dataStreamDataset) && { - data_stream: { - properties: { - ...(dataStreamDataset && { - dataset: { type: 'constant_keyword', value: dataStreamDataset }, - }), - ...(dataStreamType && { - type: { type: 'constant_keyword', value: dataStreamType }, - }), - }, + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + // @ts-ignore + esClient.indices.getMapping.mockImplementationOnce(async () => { + const body = Object.fromEntries( + indicesMappings.map((index) => [ + index, + { + mappings: { + ...(shipper && { _meta: { beat: shipper } }), + properties: { + ...(isECS && { ecs: { properties: { version: { type: 'keyword' } } } }), + ...((dataStreamType || dataStreamDataset) && { + data_stream: { + properties: { + ...(dataStreamDataset && { + dataset: { type: 'constant_keyword', value: dataStreamDataset }, + }), + ...(dataStreamType && { + type: { type: 'constant_keyword', value: dataStreamType }, + }), }, - }), - }, + }, + }), }, }, - ]) - ); - } - return indexStats; + }, + ]) + ); + return { body }; + }); + // @ts-ignore + esClient.indices.stats.mockImplementationOnce(async () => { + return { body: indexStats }; }); + return esClient; } diff --git a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.ts b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.ts index f4734dde251cc..67769793cbfdf 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.ts @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import { ElasticsearchClient } from 'src/core/server'; -import { LegacyAPICaller } from 'kibana/server'; import { DATA_DATASETS_INDEX_PATTERNS_UNIQUE, DataPatternName, @@ -224,42 +224,50 @@ interface IndexMappings { }; } -export async function getDataTelemetry(callCluster: LegacyAPICaller) { +export async function getDataTelemetry(esClient: ElasticsearchClient) { try { const index = [ ...DATA_DATASETS_INDEX_PATTERNS_UNIQUE.map(({ pattern }) => pattern), '*-*-*', // Include data-streams aliases `{type}-{dataset}-{namespace}` ]; - const [indexMappings, indexStats]: [IndexMappings, IndexStats] = await Promise.all([ + const indexMappingsParams: { index: string; filter_path: string[] } = { // GET */_mapping?filter_path=*.mappings._meta.beat,*.mappings.properties.ecs.properties.version.type,*.mappings.properties.dataset.properties.type.value,*.mappings.properties.dataset.properties.name.value - callCluster('indices.getMapping', { - index: '*', // Request all indices because filter_path already filters out the indices without any of those fields - filterPath: [ - // _meta.beat tells the shipper - '*.mappings._meta.beat', - // _meta.package.name tells the Ingest Manager's package - '*.mappings._meta.package.name', - // _meta.managed_by is usually populated by Ingest Manager for the UI to identify it - '*.mappings._meta.managed_by', - // Does it have `ecs.version` in the mappings? => It follows the ECS conventions - '*.mappings.properties.ecs.properties.version.type', + index: '*', // Request all indices because filter_path already filters out the indices without any of those fields + filter_path: [ + // _meta.beat tells the shipper + '*.mappings._meta.beat', + // _meta.package.name tells the Ingest Manager's package + '*.mappings._meta.package.name', + // _meta.managed_by is usually populated by Ingest Manager for the UI to identify it + '*.mappings._meta.managed_by', + // Does it have `ecs.version` in the mappings? => It follows the ECS conventions + '*.mappings.properties.ecs.properties.version.type', - // If `data_stream.type` is a `constant_keyword`, it can be reported as a type - '*.mappings.properties.data_stream.properties.type.value', - // If `data_stream.dataset` is a `constant_keyword`, it can be reported as the dataset - '*.mappings.properties.data_stream.properties.dataset.value', - ], - }), + // If `data_stream.type` is a `constant_keyword`, it can be reported as a type + '*.mappings.properties.data_stream.properties.type.value', + // If `data_stream.dataset` is a `constant_keyword`, it can be reported as the dataset + '*.mappings.properties.data_stream.properties.dataset.value', + ], + }; + const indicesStatsParams: { + index: string | string[] | undefined; + level: 'cluster' | 'indices' | 'shards' | undefined; + metric: string[]; + filter_path: string[]; + } = { // GET /_stats/docs,store?level=indices&filter_path=indices.*.total - callCluster('indices.stats', { - index, - level: 'indices', - metric: ['docs', 'store'], - filterPath: ['indices.*.total'], - }), + index, + level: 'indices', + metric: ['docs', 'store'], + filter_path: ['indices.*.total'], + }; + const [{ body: indexMappings }, { body: indexStats }] = await Promise.all([ + esClient.indices.getMapping(indexMappingsParams), + esClient.indices.stats(indicesStatsParams), ]); const indexNames = Object.keys({ ...indexMappings, ...indexStats?.indices }); + const indices = indexNames.map((name) => { const baseIndexInfo = { name, diff --git a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/index.ts b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/index.ts index d056d1c9f299f..0e2ab98a24cba 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/index.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/index.ts @@ -20,8 +20,8 @@ export { DATA_TELEMETRY_ID } from './constants'; export { - DataTelemetryIndex, - DataTelemetryPayload, getDataTelemetry, buildDataTelemetryPayload, + DataTelemetryPayload, + DataTelemetryIndex, } from './get_data_telemetry'; diff --git a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts index 5d27774a630a5..0ef9815a4eadb 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts @@ -21,6 +21,7 @@ import { omit } from 'lodash'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { LegacyAPICaller } from 'kibana/server'; import { StatsCollectionContext } from 'src/plugins/telemetry_collection_manager/server'; +import { ElasticsearchClient } from 'src/core/server'; export interface KibanaUsageStats { kibana: { @@ -48,7 +49,6 @@ export function handleKibanaStats( logger.warn('No Kibana stats returned from usage collectors'); return; } - const { kibana, kibana_stats: kibanaStats, ...plugins } = response; const os = { @@ -83,8 +83,9 @@ export function handleKibanaStats( export async function getKibana( usageCollection: UsageCollectionSetup, - callWithInternalUser: LegacyAPICaller + callWithInternalUser: LegacyAPICaller, + asInternalUser: ElasticsearchClient ): Promise { - const usage = await usageCollection.bulkFetch(callWithInternalUser); + const usage = await usageCollection.bulkFetch(callWithInternalUser, asInternalUser); return usageCollection.toObject(usage); } diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_license.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_license.ts index d41904c6d8e0e..879416cda62fc 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_local_license.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_license.ts @@ -17,44 +17,41 @@ * under the License. */ -import { LegacyAPICaller } from 'kibana/server'; import { ESLicense, LicenseGetter } from 'src/plugins/telemetry_collection_manager/server'; +import { ElasticsearchClient } from 'src/core/server'; let cachedLicense: ESLicense | undefined; -function fetchLicense(callCluster: LegacyAPICaller, local: boolean) { - return callCluster<{ license: ESLicense }>('transport.request', { - method: 'GET', - path: '/_license', - query: { - local, - // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license. - accept_enterprise: 'true', - }, +async function fetchLicense(esClient: ElasticsearchClient, local: boolean) { + const { body } = await esClient.license.get({ + local, + // For versions >= 7.6 and < 8.0, this flag is needed otherwise 'platinum' is returned for 'enterprise' license. + accept_enterprise: true, }); + return body; } - /** * Get the cluster's license from the connected node. * - * This is the equivalent of GET /_license?local=true . + * This is the equivalent of GET /_license?local=true&accept_enterprise=true. * * Like any X-Pack related API, X-Pack must installed for this to work. + * + * In OSS we'll get a 400 response using the new elasticsearch client. */ -async function getLicenseFromLocalOrMaster(callCluster: LegacyAPICaller) { - // Fetching the local license is cheaper than getting it from the master and good enough - const { license } = await fetchLicense(callCluster, true).catch(async (err) => { +async function getLicenseFromLocalOrMaster(esClient: ElasticsearchClient) { + // Fetching the local license is cheaper than getting it from the master node and good enough + const { license } = await fetchLicense(esClient, true).catch(async (err) => { if (cachedLicense) { try { // Fallback to the master node's license info - const response = await fetchLicense(callCluster, false); + const response = await fetchLicense(esClient, false); return response; } catch (masterError) { - if (masterError.statusCode === 404) { + if ([400, 404].includes(masterError.statusCode)) { // If the master node does not have a license, we can assume there is no license cachedLicense = undefined; } else { - // Any other errors from the master node, throw and do not send any telemetry throw err; } } @@ -68,9 +65,8 @@ async function getLicenseFromLocalOrMaster(callCluster: LegacyAPICaller) { return license; } -export const getLocalLicense: LicenseGetter = async (clustersDetails, { callCluster }) => { - const license = await getLicenseFromLocalOrMaster(callCluster); - +export const getLocalLicense: LicenseGetter = async (clustersDetails, { esClient }) => { + const license = await getLicenseFromLocalOrMaster(esClient); // It should be called only with 1 cluster element in the clustersDetails array, but doing reduce just in case. return clustersDetails.reduce((acc, { clusterUuid }) => ({ ...acc, [clusterUuid]: license }), {}); }; diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts new file mode 100644 index 0000000000000..0c8b0b249f7d1 --- /dev/null +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts @@ -0,0 +1,259 @@ +/* + * 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 { merge, omit } from 'lodash'; + +import { getLocalStats, handleLocalStats } from './get_local_stats'; +import { usageCollectionPluginMock } from '../../../usage_collection/server/mocks'; +import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; + +function mockUsageCollection(kibanaUsage = {}) { + const usageCollection = usageCollectionPluginMock.createSetupContract(); + usageCollection.bulkFetch = jest.fn().mockResolvedValue(kibanaUsage); + usageCollection.toObject = jest.fn().mockImplementation((data: any) => data); + return usageCollection; +} +// set up successful call mocks for info, cluster stats, nodes usage and data telemetry +function mockGetLocalStats(clusterInfo: any, clusterStats: any) { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.info + // @ts-ignore we only care about the response body + .mockResolvedValue( + // @ts-ignore we only care about the response body + { + body: { ...clusterInfo }, + } + ); + esClient.cluster.stats + // @ts-ignore we only care about the response body + .mockResolvedValue({ body: { ...clusterStats } }); + esClient.nodes.usage.mockResolvedValue( + // @ts-ignore we only care about the response body + { + body: { + cluster_name: 'testCluster', + nodes: { + some_node_id: { + timestamp: 1588617023177, + since: 1588616945163, + rest_actions: { + nodes_usage_action: 1, + create_index_action: 1, + document_get_action: 1, + search_action: 19, + nodes_info_action: 36, + }, + aggregations: { + terms: { + bytes: 2, + }, + scripted_metric: { + other: 7, + }, + }, + }, + }, + }, + } + ); + // @ts-ignore we only care about the response body + esClient.indices.getMapping.mockResolvedValue({ body: { mappings: {} } }); + // @ts-ignore we only care about the response body + esClient.indices.stats.mockResolvedValue({ body: { indices: {} } }); + return esClient; +} + +describe('get_local_stats', () => { + const clusterUuid = 'abc123'; + const clusterName = 'my-cool-cluster'; + const version = '2.3.4'; + const clusterInfo = { + cluster_uuid: clusterUuid, + cluster_name: clusterName, + version: { number: version }, + }; + const nodesUsage = [ + { + node_id: 'some_node_id', + timestamp: 1588617023177, + since: 1588616945163, + rest_actions: { + nodes_usage_action: 1, + create_index_action: 1, + document_get_action: 1, + search_action: 19, + nodes_info_action: 36, + }, + aggregations: { + terms: { + bytes: 2, + }, + scripted_metric: { + other: 7, + }, + }, + }, + ]; + const clusterStats = { + _nodes: { failed: 123 }, + cluster_name: 'real-cool', + indices: { totally: 456 }, + nodes: { yup: 'abc' }, + random: 123, + }; + + const kibana = { + kibana: { + great: 'googlymoogly', + versions: [{ version: '8675309', count: 1 }], + }, + kibana_stats: { + os: { + platform: 'rocky', + platformRelease: 'iv', + }, + }, + localization: { + locale: 'en', + labelsCount: 0, + integrities: {}, + }, + sun: { chances: 5 }, + clouds: { chances: 95 }, + rain: { chances: 2 }, + snow: { chances: 0 }, + }; + + const clusterStatsWithNodesUsage = { + ...clusterStats, + nodes: merge(clusterStats.nodes, { usage: { nodes: nodesUsage } }), + }; + + const combinedStatsResult = { + collection: 'local', + cluster_uuid: clusterUuid, + cluster_name: clusterName, + version, + cluster_stats: omit(clusterStatsWithNodesUsage, '_nodes', 'cluster_name'), + stack_stats: { + kibana: { + great: 'googlymoogly', + count: 1, + indices: 1, + os: { + platforms: [{ platform: 'rocky', count: 1 }], + platformReleases: [{ platformRelease: 'iv', count: 1 }], + }, + versions: [{ version: '8675309', count: 1 }], + plugins: { + localization: { + locale: 'en', + labelsCount: 0, + integrities: {}, + }, + sun: { chances: 5 }, + clouds: { chances: 95 }, + rain: { chances: 2 }, + snow: { chances: 0 }, + }, + }, + }, + }; + + const context = { + logger: console, + version: '8.0.0', + }; + + describe('handleLocalStats', () => { + it('returns expected object without xpack or kibana data', () => { + const result = handleLocalStats( + clusterInfo, + clusterStatsWithNodesUsage, + void 0, + void 0, + context + ); + expect(result.cluster_uuid).toStrictEqual(combinedStatsResult.cluster_uuid); + expect(result.cluster_name).toStrictEqual(combinedStatsResult.cluster_name); + expect(result.cluster_stats).toStrictEqual(combinedStatsResult.cluster_stats); + expect(result.version).toEqual('2.3.4'); + expect(result.collection).toEqual('local'); + expect(Object.keys(result)).not.toContain('license'); + expect(result.stack_stats).toEqual({ kibana: undefined, data: undefined }); + }); + + it('returns expected object with xpack', () => { + const result = handleLocalStats( + clusterInfo, + clusterStatsWithNodesUsage, + void 0, + void 0, + context + ); + + const { stack_stats: stack, ...cluster } = result; + expect(cluster.collection).toBe(combinedStatsResult.collection); + expect(cluster.cluster_uuid).toBe(combinedStatsResult.cluster_uuid); + expect(cluster.cluster_name).toBe(combinedStatsResult.cluster_name); + expect(stack.kibana).toBe(undefined); // not mocked for this test + expect(stack.data).toBe(undefined); // not mocked for this test + + expect(cluster.version).toEqual(combinedStatsResult.version); + expect(cluster.cluster_stats).toEqual(combinedStatsResult.cluster_stats); + expect(Object.keys(cluster).indexOf('license')).toBeLessThan(0); + expect(Object.keys(stack).indexOf('xpack')).toBeLessThan(0); + }); + }); + + describe('getLocalStats', () => { + it('returns expected object with kibana data', async () => { + const callCluster = jest.fn(); + const usageCollection = mockUsageCollection(kibana); + const esClient = mockGetLocalStats(clusterInfo, clusterStats); + const response = await getLocalStats( + [{ clusterUuid: 'abc123' }], + { callCluster, usageCollection, esClient, start: '', end: '' }, + context + ); + const result = response[0]; + expect(result.cluster_uuid).toEqual(combinedStatsResult.cluster_uuid); + expect(result.cluster_name).toEqual(combinedStatsResult.cluster_name); + expect(result.cluster_stats).toEqual(combinedStatsResult.cluster_stats); + expect(result.cluster_stats.nodes).toEqual(combinedStatsResult.cluster_stats.nodes); + expect(result.version).toBe('2.3.4'); + expect(result.collection).toBe('local'); + expect(Object.keys(result).indexOf('license')).toBeLessThan(0); + expect(Object.keys(result.stack_stats).indexOf('xpack')).toBeLessThan(0); + }); + + it('returns an empty array when no cluster uuid is provided', async () => { + const callCluster = jest.fn(); + const usageCollection = mockUsageCollection(kibana); + const esClient = mockGetLocalStats(clusterInfo, clusterStats); + const response = await getLocalStats( + [], + { callCluster, usageCollection, esClient, start: '', end: '' }, + context + ); + expect(response).toBeDefined(); + expect(response.length).toEqual(0); + }); + }); +}); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts index 98c83a3394628..6244c6fac51d3 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts @@ -40,8 +40,8 @@ export function handleLocalStats( // eslint-disable-next-line @typescript-eslint/naming-convention { cluster_name, cluster_uuid, version }: ESClusterInfo, { _nodes, cluster_name: clusterName, ...clusterStats }: any, - kibana: KibanaUsageStats, - dataTelemetry: DataTelemetryPayload, + kibana: KibanaUsageStats | undefined, + dataTelemetry: DataTelemetryPayload | undefined, context: StatsCollectionContext ) { return { @@ -62,22 +62,25 @@ export type TelemetryLocalStats = ReturnType; /** * Get statistics for all products joined by Elasticsearch cluster. + * @param {Array} cluster uuids + * @param {Object} config contains the new esClient already scoped contains usageCollection, callCluster, esClient, start, end + * @param {Object} StatsCollectionContext contains logger and version (string) */ export const getLocalStats: StatsGetter<{}, TelemetryLocalStats> = async ( - clustersDetails, - config, - context + clustersDetails, // array of cluster uuid's + config, // contains the new esClient already scoped contains usageCollection, callCluster, esClient, start, end + context // StatsCollectionContext contains logger and version (string) ) => { - const { callCluster, usageCollection } = config; + const { callCluster, usageCollection, esClient } = config; return await Promise.all( clustersDetails.map(async (clustersDetail) => { const [clusterInfo, clusterStats, nodesUsage, kibana, dataTelemetry] = await Promise.all([ - getClusterInfo(callCluster), // cluster info - getClusterStats(callCluster), // cluster stats (not to be confused with cluster _state_) - getNodesUsage(callCluster), // nodes_usage info - getKibana(usageCollection, callCluster), - getDataTelemetry(callCluster), + getClusterInfo(esClient), // cluster info + getClusterStats(esClient), // cluster stats (not to be confused with cluster _state_) + getNodesUsage(esClient), // nodes_usage info + getKibana(usageCollection, callCluster, esClient), + getDataTelemetry(esClient), ]); return handleLocalStats( clusterInfo, diff --git a/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.test.ts index 4e4b0e11b7979..acf403ba25447 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.test.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.test.ts @@ -19,6 +19,7 @@ import { getNodesUsage } from './get_nodes_usage'; import { TIMEOUT } from './constants'; +import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; const mockedNodesFetchResponse = { cluster_name: 'test cluster', @@ -44,37 +45,35 @@ const mockedNodesFetchResponse = { }, }, }; + describe('get_nodes_usage', () => { - it('calls fetchNodesUsage', async () => { - const callCluster = jest.fn(); - callCluster.mockResolvedValueOnce(mockedNodesFetchResponse); - await getNodesUsage(callCluster); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_nodes/usage', - method: 'GET', - query: { - timeout: TIMEOUT, - }, - }); - }); - it('returns a modified array of node usage data', async () => { - const callCluster = jest.fn(); - callCluster.mockResolvedValueOnce(mockedNodesFetchResponse); - const result = await getNodesUsage(callCluster); - expect(result.nodes).toEqual([ - { - aggregations: { scripted_metric: { other: 7 }, terms: { bytes: 2 } }, - node_id: 'some_node_id', - rest_actions: { - create_index_action: 1, - document_get_action: 1, - nodes_info_action: 36, - nodes_usage_action: 1, - search_action: 19, + it('returns a modified array of nodes usage data', async () => { + const response = Promise.resolve({ body: mockedNodesFetchResponse }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.nodes.usage.mockImplementationOnce( + // @ts-ignore + async (_params = { timeout: TIMEOUT }) => { + return response; + } + ); + const item = await getNodesUsage(esClient); + expect(esClient.nodes.usage).toHaveBeenCalledWith({ timeout: TIMEOUT }); + expect(item).toStrictEqual({ + nodes: [ + { + aggregations: { scripted_metric: { other: 7 }, terms: { bytes: 2 } }, + node_id: 'some_node_id', + rest_actions: { + create_index_action: 1, + document_get_action: 1, + nodes_info_action: 36, + nodes_usage_action: 1, + search_action: 19, + }, + since: 1588616945163, + timestamp: 1588617023177, }, - since: 1588616945163, - timestamp: 1588617023177, - }, - ]); + ], + }); }); }); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.ts b/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.ts index c5c110fbb4149..959840d0020a2 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_nodes_usage.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'src/core/server'; import { TIMEOUT } from './constants'; export interface NodeAggregation { @@ -44,7 +44,7 @@ export interface NodesFeatureUsageResponse { } export type NodesUsageGetter = ( - callCluster: LegacyAPICaller + esClient: ElasticsearchClient ) => Promise<{ nodes: NodeObj[] | Array<{}> }>; /** * Get the nodes usage data from the connected cluster. @@ -54,16 +54,12 @@ export type NodesUsageGetter = ( * The Nodes usage API was introduced in v6.0.0 */ export async function fetchNodesUsage( - callCluster: LegacyAPICaller + esClient: ElasticsearchClient ): Promise { - const response = await callCluster('transport.request', { - method: 'GET', - path: '/_nodes/usage', - query: { - timeout: TIMEOUT, - }, + const { body } = await esClient.nodes.usage({ + timeout: TIMEOUT, }); - return response; + return body; } /** @@ -71,8 +67,8 @@ export async function fetchNodesUsage( * @param callCluster APICaller * @returns Object containing array of modified usage information with the node_id nested within the data for that node. */ -export const getNodesUsage: NodesUsageGetter = async (callCluster) => { - const result = await fetchNodesUsage(callCluster); +export const getNodesUsage: NodesUsageGetter = async (esClient) => { + const result = await fetchNodesUsage(esClient); const transformedNodes = Object.entries(result?.nodes || {}).map(([key, value]) => ({ ...(value as NodeObj), node_id: key, diff --git a/src/plugins/telemetry/server/telemetry_collection/register_collection.ts b/src/plugins/telemetry/server/telemetry_collection/register_collection.ts index 438fcadad9255..9dac4900f5f10 100644 --- a/src/plugins/telemetry/server/telemetry_collection/register_collection.ts +++ b/src/plugins/telemetry/server/telemetry_collection/register_collection.ts @@ -38,16 +38,19 @@ import { ILegacyClusterClient } from 'kibana/server'; import { TelemetryCollectionManagerPluginSetup } from 'src/plugins/telemetry_collection_manager/server'; +import { IClusterClient } from '../../../../../src/core/server'; import { getLocalStats } from './get_local_stats'; import { getClusterUuids } from './get_cluster_stats'; import { getLocalLicense } from './get_local_license'; export function registerCollection( telemetryCollectionManager: TelemetryCollectionManagerPluginSetup, - esCluster: ILegacyClusterClient + esCluster: ILegacyClusterClient, + esClientGetter: () => IClusterClient | undefined ) { telemetryCollectionManager.setCollection({ esCluster, + esClientGetter, title: 'local', priority: 0, statsGetter: getLocalStats, diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts index 051bb3a11cb16..e54e7451a670a 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.ts @@ -24,6 +24,7 @@ import { CoreStart, Plugin, Logger, + IClusterClient, } from '../../../core/server'; import { @@ -86,6 +87,7 @@ export class TelemetryCollectionManagerPlugin title, priority, esCluster, + esClientGetter, statsGetter, clusterDetailsGetter, licenseGetter, @@ -105,6 +107,9 @@ export class TelemetryCollectionManagerPlugin if (!esCluster) { throw Error('esCluster name must be set for the getCluster method.'); } + if (!esClientGetter) { + throw Error('esClientGetter method not set.'); + } if (!clusterDetailsGetter) { throw Error('Cluster UUIds method is not set.'); } @@ -118,6 +123,7 @@ export class TelemetryCollectionManagerPlugin clusterDetailsGetter, esCluster, title, + esClientGetter, }); this.usageGetterMethodPriority = priority; } @@ -126,6 +132,7 @@ export class TelemetryCollectionManagerPlugin private getStatsCollectionConfig( config: StatsGetterConfig, collection: Collection, + collectionEsClient: IClusterClient, usageCollection: UsageCollectionSetup ): StatsCollectionConfig { const { start, end, request } = config; @@ -133,8 +140,11 @@ export class TelemetryCollectionManagerPlugin const callCluster = config.unencrypted ? collection.esCluster.asScoped(request).callAsCurrentUser : collection.esCluster.callAsInternalUser; - - return { callCluster, start, end, usageCollection }; + // Scope the new elasticsearch Client appropriately and pass to the stats collection config + const esClient = config.unencrypted + ? collectionEsClient.asScoped(config.request).asCurrentUser + : collectionEsClient.asInternalUser; + return { callCluster, start, end, usageCollection, esClient }; } private async getOptInStats(optInStatus: boolean, config: StatsGetterConfig) { @@ -142,27 +152,33 @@ export class TelemetryCollectionManagerPlugin return []; } for (const collection of this.collections) { - const statsCollectionConfig = this.getStatsCollectionConfig( - config, - collection, - this.usageCollection - ); - try { - const optInStats = await this.getOptInStatsForCollection( + // first fetch the client and make sure it's not undefined. + const collectionEsClient = collection.esClientGetter(); + if (collectionEsClient !== undefined) { + const statsCollectionConfig = this.getStatsCollectionConfig( + config, collection, - optInStatus, - statsCollectionConfig + collectionEsClient, + this.usageCollection ); - if (optInStats && optInStats.length) { - this.logger.debug(`Got Opt In stats using ${collection.title} collection.`); - if (config.unencrypted) { - return optInStats; + + try { + const optInStats = await this.getOptInStatsForCollection( + collection, + optInStatus, + statsCollectionConfig + ); + if (optInStats && optInStats.length) { + this.logger.debug(`Got Opt In stats using ${collection.title} collection.`); + if (config.unencrypted) { + return optInStats; + } + return encryptTelemetry(optInStats, { useProdKey: this.isDistributable }); } - return encryptTelemetry(optInStats, { useProdKey: this.isDistributable }); + } catch (err) { + this.logger.debug(`Failed to collect any opt in stats with registered collections.`); + // swallow error to try next collection; } - } catch (err) { - this.logger.debug(`Failed to collect any opt in stats with registered collections.`); - // swallow error to try next collection; } } @@ -192,28 +208,32 @@ export class TelemetryCollectionManagerPlugin return []; } for (const collection of this.collections) { - const statsCollectionConfig = this.getStatsCollectionConfig( - config, - collection, - this.usageCollection - ); - try { - const usageData = await this.getUsageForCollection(collection, statsCollectionConfig); - if (usageData.length) { - this.logger.debug(`Got Usage using ${collection.title} collection.`); - if (config.unencrypted) { - return usageData; - } + const collectionEsClient = collection.esClientGetter(); + if (collectionEsClient !== undefined) { + const statsCollectionConfig = this.getStatsCollectionConfig( + config, + collection, + collectionEsClient, + this.usageCollection + ); + try { + const usageData = await this.getUsageForCollection(collection, statsCollectionConfig); + if (usageData.length) { + this.logger.debug(`Got Usage using ${collection.title} collection.`); + if (config.unencrypted) { + return usageData; + } - return encryptTelemetry(usageData.filter(isClusterOptedIn), { - useProdKey: this.isDistributable, - }); + return encryptTelemetry(usageData.filter(isClusterOptedIn), { + useProdKey: this.isDistributable, + }); + } + } catch (err) { + this.logger.debug( + `Failed to collect any usage with registered collection ${collection.title}.` + ); + // swallow error to try next collection; } - } catch (err) { - this.logger.debug( - `Failed to collect any usage with registered collection ${collection.title}.` - ); - // swallow error to try next collection; } } diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts index 16f96c07fd8ea..44970df30fd16 100644 --- a/src/plugins/telemetry_collection_manager/server/types.ts +++ b/src/plugins/telemetry_collection_manager/server/types.ts @@ -17,8 +17,15 @@ * under the License. */ -import { LegacyAPICaller, Logger, KibanaRequest, ILegacyClusterClient } from 'kibana/server'; +import { + LegacyAPICaller, + Logger, + KibanaRequest, + ILegacyClusterClient, + IClusterClient, +} from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { ElasticsearchClient } from '../../../../src/core/server'; import { TelemetryCollectionManagerPlugin } from './plugin'; export interface TelemetryCollectionManagerPluginSetup { @@ -67,6 +74,7 @@ export interface StatsCollectionConfig { callCluster: LegacyAPICaller; start: string | number; end: string | number; + esClient: ElasticsearchClient; } export interface BasicStatsPayload { @@ -100,7 +108,7 @@ export interface ESLicense { } export interface StatsCollectionContext { - logger: Logger; + logger: Logger | Console; version: string; } @@ -130,6 +138,7 @@ export interface CollectionConfig< title: string; priority: number; esCluster: ILegacyClusterClient; + esClientGetter: () => IClusterClient | undefined; // --> by now we know that the client getter will return the IClusterClient but we assure that through a code check statsGetter: StatsGetter; clusterDetailsGetter: ClusterDetailsGetter; licenseGetter: LicenseGetter; @@ -145,5 +154,6 @@ export interface Collection< licenseGetter: LicenseGetter; clusterDetailsGetter: ClusterDetailsGetter; esCluster: ILegacyClusterClient; + esClientGetter: () => IClusterClient | undefined; // the collection could still return undefined for the es client getter. title: string; } diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap index fa52815a9ce98..7357598c8495f 100644 --- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap +++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap @@ -49,7 +49,7 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = ` - `; -exports[`TelemetryManagementSectionComponent renders null because query does not match the SEARCH_TERMS 1`] = ` - -`; - exports[`TelemetryManagementSectionComponent test the wrapper (for coverage purposes) 1`] = `null`; diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx index 3a2055ef18ea6..993295746ea5b 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx @@ -21,6 +21,7 @@ import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import TelemetryManagementSection from './telemetry_management_section'; import { TelemetryService } from '../../../telemetry/public/services'; import { coreMock } from '../../../../core/public/mocks'; +import { render } from '@testing-library/react'; describe('TelemetryManagementSectionComponent', () => { const coreStart = coreMock.createStart(); @@ -73,19 +74,31 @@ describe('TelemetryManagementSectionComponent', () => { http: coreSetup.http, }); - const component = mountWithIntl( - + const component = render( + Fallback}> + + ); + try { - expect( - component.setProps({ ...component.props(), query: { text: 'asssdasdsad' } }) - ).toMatchSnapshot(); + component.rerender( + Fallback}> + + + ); expect(onQueryMatchChange).toHaveBeenCalledWith(false); expect(onQueryMatchChange).toHaveBeenCalledTimes(1); } finally { diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx index 92bf8a65b8bdc..822d8b49661c1 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx @@ -35,7 +35,7 @@ import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; import { PRIVACY_STATEMENT_URL } from '../../../telemetry/common/constants'; import { OptInExampleFlyout } from './opt_in_example_flyout'; import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout'; -import { Field } from '../../../advanced_settings/public'; +import { LazyField } from '../../../advanced_settings/public'; import { ToastsStart } from '../../../../core/public'; type TelemetryService = TelemetryPluginSetup['telemetryService']; @@ -123,7 +123,7 @@ export class TelemetryManagementSection extends Component { {this.maybeGetAppliesSettingMessage()} - | undefined; + try { + mapsTileMapUrlGenerator = getShareService().urlGenerators.getUrlGenerator( + 'MAPS_APP_TILE_MAP_URL_GENERATOR' + ); + } catch (error) { + // ignore error thrown when url generator is not available + } + + const title = i18n.translate('tileMap.vis.mapTitle', { + defaultMessage: 'Coordinate Map', + }); + + async function onClick(e: React.MouseEvent) { + e.preventDefault(); + + const query = getQueryService(); + const createUrlParams: { [key: string]: any } = { + label: vis.title ? vis.title : title, + mapType: vis.params.mapType, + colorSchema: vis.params.colorSchema, + indexPatternId: vis.data.indexPattern?.id, + metricAgg: 'count', + filters: query.filterManager.getFilters(), + query: query.queryString.getQuery(), + timeRange: query.timefilter.timefilter.getTime(), + }; + + const bucketAggs = vis.data?.aggs?.byType('buckets'); + if (bucketAggs?.length && bucketAggs[0].type.dslName === 'geohash_grid') { + createUrlParams.geoFieldName = bucketAggs[0].getField()?.name; + } else if (vis.data.indexPattern) { + // attempt to default to first geo point field when geohash is not configured yet + const geoField = vis.data.indexPattern.fields.find((field) => { + return ( + !indexPatterns.isNestedField(field) && field.aggregatable && field.type === 'geo_point' + ); + }); + if (geoField) { + createUrlParams.geoFieldName = geoField.name; + } + } + + const metricAggs = vis.data?.aggs?.byType('metrics'); + if (metricAggs?.length) { + createUrlParams.metricAgg = metricAggs[0].type.dslName; + createUrlParams.metricFieldName = metricAggs[0].getField()?.name; + } + + const url = await mapsTileMapUrlGenerator!.createUrl(createUrlParams); + getCoreService().application.navigateToUrl(url); + } + + return ( + + ); +} diff --git a/src/plugins/tile_map/public/markers/geohash_grid.js b/src/plugins/tile_map/public/markers/geohash_grid.js index 46e7987c601f5..d81e435a6dd5d 100644 --- a/src/plugins/tile_map/public/markers/geohash_grid.js +++ b/src/plugins/tile_map/public/markers/geohash_grid.js @@ -18,11 +18,10 @@ */ import { ScaledCirclesMarkers } from './scaled_circles'; -import { L } from '../../../maps_legacy/public'; export class GeohashGridMarkers extends ScaledCirclesMarkers { getMarkerFunction() { - return function (feature) { + return (feature) => { const geohashRect = feature.properties.geohash_meta.rectangle; // get bounds from northEast[3] and southWest[1] // corners in geohash rectangle @@ -30,7 +29,7 @@ export class GeohashGridMarkers extends ScaledCirclesMarkers { [geohashRect[3][0], geohashRect[3][1]], [geohashRect[1][0], geohashRect[1][1]], ]; - return L.rectangle(corners); + return this._leaflet.rectangle(corners); }; } } diff --git a/src/plugins/tile_map/public/markers/heatmap.js b/src/plugins/tile_map/public/markers/heatmap.js index f2d014797bce0..79bbee16548ac 100644 --- a/src/plugins/tile_map/public/markers/heatmap.js +++ b/src/plugins/tile_map/public/markers/heatmap.js @@ -20,7 +20,6 @@ import _ from 'lodash'; import d3 from 'd3'; import { EventEmitter } from 'events'; -import { L } from '../../../maps_legacy/public'; /** * Map overlay: canvas layer with leaflet.heat plugin @@ -30,17 +29,17 @@ import { L } from '../../../maps_legacy/public'; * @param params {Object} */ export class HeatmapMarkers extends EventEmitter { - constructor(featureCollection, options, zoom, max) { + constructor(featureCollection, options, zoom, max, leaflet) { super(); this._geojsonFeatureCollection = featureCollection; const points = dataToHeatArray(featureCollection, max); - this._leafletLayer = new L.HeatLayer(points, options); + this._leafletLayer = new leaflet.HeatLayer(points, options); this._tooltipFormatter = options.tooltipFormatter; this._zoom = zoom; this._disableTooltips = false; this._getLatLng = _.memoize( function (feature) { - return L.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]); + return leaflet.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]); }, function (feature) { // turn coords into a string for the memoize cache diff --git a/src/plugins/tile_map/public/markers/scaled_circles.js b/src/plugins/tile_map/public/markers/scaled_circles.js index cb111107f6fe3..4a5f1b452580b 100644 --- a/src/plugins/tile_map/public/markers/scaled_circles.js +++ b/src/plugins/tile_map/public/markers/scaled_circles.js @@ -21,7 +21,7 @@ import _ from 'lodash'; import d3 from 'd3'; import $ from 'jquery'; import { EventEmitter } from 'events'; -import { L, colorUtil } from '../../../maps_legacy/public'; +import { colorUtil } from '../../../maps_legacy/public'; import { truncatedColorMaps } from '../../../charts/public'; export class ScaledCirclesMarkers extends EventEmitter { @@ -31,14 +31,13 @@ export class ScaledCirclesMarkers extends EventEmitter { options, targetZoom, kibanaMap, - metricAgg + leaflet ) { super(); this._featureCollection = featureCollection; this._featureCollectionMetaData = featureCollectionMetaData; this._zoom = targetZoom; - this._metricAgg = metricAgg; this._valueFormatter = options.valueFormatter || @@ -55,6 +54,7 @@ export class ScaledCirclesMarkers extends EventEmitter { this._legendColors = null; this._legendQuantizer = null; + this._leaflet = leaflet; this._popups = []; @@ -72,7 +72,7 @@ export class ScaledCirclesMarkers extends EventEmitter { return kibanaMap.isInside(bucketRectBounds); }; } - this._leafletLayer = L.geoJson(null, layerOptions); + this._leafletLayer = this._leaflet.geoJson(null, layerOptions); this._leafletLayer.addData(this._featureCollection); } @@ -143,7 +143,7 @@ export class ScaledCirclesMarkers extends EventEmitter { mouseover: (e) => { const layer = e.target; // bring layer to front if not older browser - if (!L.Browser.ie && !L.Browser.opera) { + if (!this._leaflet.Browser.ie && !this._leaflet.Browser.opera) { layer.bringToFront(); } this._showTooltip(feature); @@ -170,7 +170,10 @@ export class ScaledCirclesMarkers extends EventEmitter { return; } - const latLng = L.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]); + const latLng = this._leaflet.latLng( + feature.geometry.coordinates[1], + feature.geometry.coordinates[0] + ); this.emit('showTooltip', { content: content, position: latLng, @@ -182,7 +185,7 @@ export class ScaledCirclesMarkers extends EventEmitter { return (feature, latlng) => { const value = feature.properties.value; const scaledRadius = this._radiusScale(value) * scaleFactor; - return L.circleMarker(latlng).setRadius(scaledRadius); + return this._leaflet.circleMarker(latlng).setRadius(scaledRadius); }; } diff --git a/src/plugins/tile_map/public/markers/shaded_circles.js b/src/plugins/tile_map/public/markers/shaded_circles.js index 745d0422856c6..3468cab7d8b75 100644 --- a/src/plugins/tile_map/public/markers/shaded_circles.js +++ b/src/plugins/tile_map/public/markers/shaded_circles.js @@ -19,7 +19,6 @@ import _ from 'lodash'; import { ScaledCirclesMarkers } from './scaled_circles'; -import { L } from '../../../maps_legacy/public'; export class ShadedCirclesMarkers extends ScaledCirclesMarkers { getMarkerFunction() { @@ -27,7 +26,7 @@ export class ShadedCirclesMarkers extends ScaledCirclesMarkers { const scaleFactor = 0.8; return (feature, latlng) => { const radius = this._geohashMinDistance(feature) * scaleFactor; - return L.circle(latlng, radius); + return this._leaflet.circle(latlng, radius); }; } @@ -49,12 +48,12 @@ export class ShadedCirclesMarkers extends ScaledCirclesMarkers { // clockwise, each value being an array of [lat, lng] // center lat and southeast lng - const east = L.latLng([centerPoint[0], geohashRect[2][1]]); + const east = this._leaflet.latLng([centerPoint[0], geohashRect[2][1]]); // southwest lat and center lng - const north = L.latLng([geohashRect[3][0], centerPoint[1]]); + const north = this._leaflet.latLng([geohashRect[3][0], centerPoint[1]]); // get latLng of geohash center point - const center = L.latLng([centerPoint[0], centerPoint[1]]); + const center = this._leaflet.latLng([centerPoint[0], centerPoint[1]]); // get smallest radius at center of geohash grid rectangle const eastRadius = Math.floor(center.distanceTo(east)); diff --git a/src/plugins/tile_map/public/plugin.ts b/src/plugins/tile_map/public/plugin.ts index 9a164f8a303f8..dfcafafbe47f7 100644 --- a/src/plugins/tile_map/public/plugin.ts +++ b/src/plugins/tile_map/public/plugin.ts @@ -34,8 +34,15 @@ import { createTileMapFn } from './tile_map_fn'; import { createTileMapTypeDefinition } from './tile_map_type'; import { IServiceSettings, MapsLegacyPluginSetup } from '../../maps_legacy/public'; import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService, setQueryService, setKibanaLegacy } from './services'; +import { + setCoreService, + setFormatService, + setQueryService, + setKibanaLegacy, + setShareService, +} from './services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { SharePluginStart } from '../../share/public'; export interface TileMapConfigType { tilemap: any; @@ -47,7 +54,7 @@ interface TileMapVisualizationDependencies { getZoomPrecision: any; getPrecision: any; BaseMapsVisualization: any; - serviceSettings: IServiceSettings; + getServiceSettings: () => Promise; } /** @internal */ @@ -61,6 +68,7 @@ export interface TileMapPluginSetupDependencies { export interface TileMapPluginStartDependencies { data: DataPublicPluginStart; kibanaLegacy: KibanaLegacyStart; + share: SharePluginStart; } export interface TileMapPluginSetup { @@ -81,13 +89,13 @@ export class TileMapPlugin implements Plugin = { getZoomPrecision, getPrecision, BaseMapsVisualization: mapsLegacy.getBaseMapsVis(), uiSettings: core.uiSettings, - serviceSettings, + getServiceSettings, }; expressions.registerFunction(() => createTileMapFn(visualizationDependencies)); @@ -100,10 +108,12 @@ export class TileMapPlugin implements Plugin('Core'); export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] @@ -29,6 +33,8 @@ export const [getQueryService, setQueryService] = createGetterSetter< DataPublicPluginStart['query'] >('Query'); +export const [getShareService, setShareService] = createGetterSetter('Share'); + export const [getKibanaLegacy, setKibanaLegacy] = createGetterSetter( 'KibanaLegacy' ); diff --git a/src/plugins/tile_map/public/tile_map_type.js b/src/plugins/tile_map/public/tile_map_type.js index f76da26022a77..cc19a8bbcef91 100644 --- a/src/plugins/tile_map/public/tile_map_type.js +++ b/src/plugins/tile_map/public/tile_map_type.js @@ -25,13 +25,15 @@ import { createTileMapVisualization } from './tile_map_visualization'; import { TileMapOptions } from './components/tile_map_options'; import { supportsCssFilters } from './css_filters'; import { truncatedColorSchemas } from '../../charts/public'; +import { getDeprecationMessage } from './get_deprecation_message'; export function createTileMapTypeDefinition(dependencies) { const CoordinateMapsVisualization = createTileMapVisualization(dependencies); - const { uiSettings, serviceSettings } = dependencies; + const { uiSettings, getServiceSettings } = dependencies; return { name: 'tile_map', + getDeprecationMessage, title: i18n.translate('tileMap.vis.mapTitle', { defaultMessage: 'Coordinate Map', }), @@ -142,6 +144,7 @@ export function createTileMapTypeDefinition(dependencies) { let tmsLayers; try { + const serviceSettings = await getServiceSettings(); tmsLayers = await serviceSettings.getTMSServices(); } catch (e) { return vis; diff --git a/src/plugins/tile_map/public/tile_map_visualization.js b/src/plugins/tile_map/public/tile_map_visualization.js index 2ebb76d05c219..b09a2f3bac48f 100644 --- a/src/plugins/tile_map/public/tile_map_visualization.js +++ b/src/plugins/tile_map/public/tile_map_visualization.js @@ -17,12 +17,42 @@ * under the License. */ -import { get } from 'lodash'; -import { GeohashLayer } from './geohash_layer'; +import { get, round } from 'lodash'; import { getFormatService, getQueryService, getKibanaLegacy } from './services'; -import { scaleBounds, geoContains, mapTooltipProvider } from '../../maps_legacy/public'; +import { + geoContains, + mapTooltipProvider, + lazyLoadMapsLegacyModules, +} from '../../maps_legacy/public'; import { tooltipFormatter } from './tooltip_formatter'; +function scaleBounds(bounds) { + const scale = 0.5; // scale bounds by 50% + + const topLeft = bounds.top_left; + const bottomRight = bounds.bottom_right; + let latDiff = round(Math.abs(topLeft.lat - bottomRight.lat), 5); + const lonDiff = round(Math.abs(bottomRight.lon - topLeft.lon), 5); + // map height can be zero when vis is first created + if (latDiff === 0) latDiff = lonDiff; + + const latDelta = latDiff * scale; + let topLeftLat = round(topLeft.lat, 5) + latDelta; + if (topLeftLat > 90) topLeftLat = 90; + let bottomRightLat = round(bottomRight.lat, 5) - latDelta; + if (bottomRightLat < -90) bottomRightLat = -90; + const lonDelta = lonDiff * scale; + let topLeftLon = round(topLeft.lon, 5) - lonDelta; + if (topLeftLon < -180) topLeftLon = -180; + let bottomRightLon = round(bottomRight.lon, 5) + lonDelta; + if (bottomRightLon > 180) bottomRightLon = 180; + + return { + top_left: { lat: topLeftLat, lon: topLeftLon }, + bottom_right: { lat: bottomRightLat, lon: bottomRightLon }, + }; +} + export const createTileMapVisualization = (dependencies) => { const { getZoomPrecision, getPrecision, BaseMapsVisualization } = dependencies; @@ -147,7 +177,9 @@ export const createTileMapVisualization = (dependencies) => { this._recreateGeohashLayer(); } - _recreateGeohashLayer() { + async _recreateGeohashLayer() { + const { GeohashLayer } = await import('./geohash_layer'); + if (this._geohashLayer) { this._kibanaMap.removeLayer(this._geohashLayer); this._geohashLayer = null; @@ -158,7 +190,8 @@ export const createTileMapVisualization = (dependencies) => { this._geoJsonFeatureCollectionAndMeta.meta, geohashOptions, this._kibanaMap.getZoomLevel(), - this._kibanaMap + this._kibanaMap, + (await lazyLoadMapsLegacyModules()).L ); this._kibanaMap.addLayer(this._geohashLayer); } diff --git a/src/plugins/ui_actions/README.asciidoc b/src/plugins/ui_actions/README.asciidoc new file mode 100644 index 0000000000000..577aa2eae354b --- /dev/null +++ b/src/plugins/ui_actions/README.asciidoc @@ -0,0 +1,26 @@ +[[uiactions-plugin]] +== UI Actions + +An API for: + +- creating custom functionality (`actions`) +- creating custom user interaction events (`triggers`) +- attaching and detaching `actions` to `triggers`. +- emitting `trigger` events +- executing `actions` attached to a given `trigger`. +- exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. + +=== Examples + +https://github.com/elastic/kibana/blob/master/examples/ui_action_examples/README.md[ui_action examples] + +=== API Docs + +==== Server API +https://github.com/elastic/kibana/blob/master/docs/development/plugins/ui_actions/server/kibana-plugin-plugins-ui_actions-server.uiactionssetup.md[Browser Setup contract] +https://github.com/elastic/kibana/blob/master/docs/development/plugins/ui_actions/server/kibana-plugin-plugins-ui_actions-server.uiactionsstart.md[Browser Start contract] + +==== Browser API +https://github.com/elastic/kibana/blob/master/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionssetup.md[Browser Setup contract] +https://github.com/elastic/kibana/blob/master/docs/development/plugins/ui_actions/public/kibana-plugin-plugins-ui_actions-public.uiactionsstart.md[Browser Start contract] + diff --git a/src/plugins/ui_actions/README.md b/src/plugins/ui_actions/README.md deleted file mode 100644 index c4e02b551c884..0000000000000 --- a/src/plugins/ui_actions/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# UI Actions - -An API for: - -- creating custom functionality (`actions`) -- creating custom user interaction events (`triggers`) -- attaching and detaching `actions` to `triggers`. -- emitting `trigger` events -- executing `actions` attached to a given `trigger`. -- exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. diff --git a/src/plugins/ui_actions/public/mocks.ts b/src/plugins/ui_actions/public/mocks.ts index 3522ac4941ba0..759430169b613 100644 --- a/src/plugins/ui_actions/public/mocks.ts +++ b/src/plugins/ui_actions/public/mocks.ts @@ -48,6 +48,7 @@ const createStartContract = (): Start => { executeTriggerActions: jest.fn(), fork: jest.fn(), getAction: jest.fn(), + hasAction: jest.fn(), getTrigger: jest.fn(), getTriggerActions: jest.fn((id: TriggerId) => []), getTriggerCompatibleActions: jest.fn(), diff --git a/src/plugins/ui_actions/public/public.api.md b/src/plugins/ui_actions/public/public.api.md new file mode 100644 index 0000000000000..8b3d81a589365 --- /dev/null +++ b/src/plugins/ui_actions/public/public.api.md @@ -0,0 +1,337 @@ +## API Report File for "kibana" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { CoreSetup } from 'src/core/public'; +import { CoreStart } from 'src/core/public'; +import { EnvironmentMode } from '@kbn/config'; +import { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import { Observable } from 'rxjs'; +import { PackageInfo } from '@kbn/config'; +import { Plugin } from 'src/core/public'; +import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; +import React from 'react'; +import * as Rx from 'rxjs'; +import { UiComponent } from 'src/plugins/kibana_utils/public'; + +// Warning: (ae-forgotten-export) The symbol "BaseContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "Action" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface Action extends Partial>> { + execute(context: ActionExecutionContext): Promise; + getDisplayName(context: ActionExecutionContext): string; + getHref?(context: ActionExecutionContext): Promise; + getIconType(context: ActionExecutionContext): string | undefined; + id: string; + isCompatible(context: ActionExecutionContext): Promise; + MenuItem?: UiComponent<{ + context: ActionExecutionContext; + }>; + order?: number; + shouldAutoExecute?(context: ActionExecutionContext): Promise; + readonly type: T; +} + +// Warning: (ae-missing-release-tag) "ACTION_VISUALIZE_FIELD" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ACTION_VISUALIZE_FIELD = "ACTION_VISUALIZE_FIELD"; + +// Warning: (ae-missing-release-tag) "ACTION_VISUALIZE_GEO_FIELD" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const ACTION_VISUALIZE_GEO_FIELD = "ACTION_VISUALIZE_GEO_FIELD"; + +// Warning: (ae-missing-release-tag) "ActionByType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ActionByType = Action; + +// Warning: (ae-missing-release-tag) "ActionContextMapping" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ActionContextMapping { + // Warning: (ae-forgotten-export) The symbol "DEFAULT_ACTION" needs to be exported by the entry point index.d.ts + // + // (undocumented) + [DEFAULT_ACTION]: BaseContext; + // (undocumented) + [ACTION_VISUALIZE_FIELD]: VisualizeFieldContext; + // (undocumented) + [ACTION_VISUALIZE_GEO_FIELD]: VisualizeFieldContext; +} + +// Warning: (ae-missing-release-tag) "ActionDefinitionByType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ActionDefinitionByType = UiActionsActionDefinition; + +// Warning: (ae-missing-release-tag) "ActionExecutionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type ActionExecutionContext = Context & ActionExecutionMeta; + +// Warning: (ae-missing-release-tag) "ActionExecutionMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ActionExecutionMeta { + trigger: Trigger; +} + +// Warning: (ae-missing-release-tag) "ActionType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ActionType = keyof ActionContextMapping; + +// Warning: (ae-missing-release-tag) "APPLY_FILTER_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const APPLY_FILTER_TRIGGER = "FILTER_TRIGGER"; + +// Warning: (ae-missing-release-tag) "applyFilterTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'>; + +// Warning: (ae-forgotten-export) The symbol "BuildContextMenuParams" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "buildContextMenuForActions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export function buildContextMenuForActions({ actions, title, closeMenu, }: BuildContextMenuParams): Promise; + +// Warning: (ae-forgotten-export) The symbol "ActionDefinitionByType" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "createAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function createAction(action: ActionDefinitionByType_2): ActionByType; + +// Warning: (ae-missing-release-tag) "IncompatibleActionError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class IncompatibleActionError extends Error { + constructor(); + // (undocumented) + code: string; +} + +// Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "UiActionsPlugin" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export function plugin(initializerContext: PluginInitializerContext): UiActionsPlugin; + +// Warning: (ae-missing-release-tag) "SELECT_RANGE_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SELECT_RANGE_TRIGGER = "SELECT_RANGE_TRIGGER"; + +// Warning: (ae-missing-release-tag) "selectRangeTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "Trigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface Trigger { + description?: string; + id: ID; + title?: string; +} + +// Warning: (ae-missing-release-tag) "TriggerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type TriggerContext = T extends TriggerId ? TriggerContextMapping[T] : never; + +// Warning: (ae-missing-release-tag) "TriggerContextMapping" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface TriggerContextMapping { + // Warning: (ae-forgotten-export) The symbol "DEFAULT_TRIGGER" needs to be exported by the entry point index.d.ts + // Warning: (ae-forgotten-export) The symbol "TriggerContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + [DEFAULT_TRIGGER]: TriggerContext_2; + // Warning: (ae-forgotten-export) The symbol "ApplyGlobalFilterActionContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + [APPLY_FILTER_TRIGGER]: ApplyGlobalFilterActionContext; + // Warning: (ae-forgotten-export) The symbol "RangeSelectContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + [SELECT_RANGE_TRIGGER]: RangeSelectContext; + // Warning: (ae-forgotten-export) The symbol "ValueClickContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + [VALUE_CLICK_TRIGGER]: ValueClickContext; + // (undocumented) + [VISUALIZE_FIELD_TRIGGER]: VisualizeFieldContext; + // (undocumented) + [VISUALIZE_GEO_FIELD_TRIGGER]: VisualizeFieldContext; +} + +// Warning: (ae-missing-release-tag) "TriggerId" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type TriggerId = keyof TriggerContextMapping; + +// Warning: (ae-forgotten-export) The symbol "ActionDefinitionContext" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ActionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface UiActionsActionDefinition extends Partial>> { + execute(context: ActionDefinitionContext): Promise; + getHref?(context: ActionDefinitionContext): Promise; + readonly id: string; + isCompatible?(context: ActionDefinitionContext): Promise; + shouldAutoExecute?(context: ActionDefinitionContext): Promise; + readonly type?: ActionType; +} + +// Warning: (ae-missing-release-tag) "Presentable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface UiActionsPresentable { + getDisplayName(context: Context): string; + getDisplayNameTooltip(context: Context): string; + getHref?(context: Context): Promise; + getIconType(context: Context): string | undefined; + readonly grouping?: UiActionsPresentableGrouping; + readonly id: string; + isCompatible(context: Context): Promise; + readonly MenuItem?: UiComponent<{ + context: Context; + }>; + readonly order: number; +} + +// Warning: (ae-forgotten-export) The symbol "PresentableGroup" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "PresentableGrouping" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type UiActionsPresentableGrouping = Array>; + +// Warning: (ae-missing-release-tag) "UiActionsService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class UiActionsService { + constructor({ triggers, actions, triggerToActions, }?: UiActionsServiceParams); + // Warning: (ae-forgotten-export) The symbol "ActionRegistry" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly actions: ActionRegistry; + readonly addTriggerAction: (triggerId: T, action: UiActionsActionDefinition | Action) => void; + // (undocumented) + readonly attachAction: (triggerId: T, actionId: string) => void; + readonly clear: () => void; + // (undocumented) + readonly detachAction: (triggerId: TriggerId, actionId: string) => void; + // @deprecated (undocumented) + readonly executeTriggerActions: (triggerId: T, context: TriggerContext) => Promise; + // Warning: (ae-forgotten-export) The symbol "UiActionsExecutionService" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly executionService: UiActionsExecutionService; + readonly fork: () => UiActionsService; + // (undocumented) + readonly getAction: >(id: string) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">; + // Warning: (ae-forgotten-export) The symbol "TriggerContract" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly getTrigger: (triggerId: T) => TriggerContract; + // (undocumented) + readonly getTriggerActions: (triggerId: T) => Action[]; + // (undocumented) + readonly getTriggerCompatibleActions: (triggerId: T, context: TriggerContextMapping[T]) => Promise[]>; + // (undocumented) + readonly hasAction: (actionId: string) => boolean; + // Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts + // + // (undocumented) + readonly registerAction: >(definition: A) => Action, "" | "ACTION_VISUALIZE_FIELD" | "ACTION_VISUALIZE_GEO_FIELD" | "ACTION_CUSTOMIZE_PANEL" | "ACTION_ADD_PANEL" | "openInspector" | "deletePanel" | "editPanel" | "togglePanel" | "replacePanel" | "clonePanel" | "addToFromLibrary" | "unlinkFromLibrary" | "ACTION_LIBRARY_NOTIFICATION" | "ACTION_GLOBAL_APPLY_FILTER" | "ACTION_SELECT_RANGE" | "ACTION_VALUE_CLICK">; + // (undocumented) + readonly registerTrigger: (trigger: Trigger) => void; + // Warning: (ae-forgotten-export) The symbol "TriggerRegistry" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly triggers: TriggerRegistry; + // Warning: (ae-forgotten-export) The symbol "TriggerToActionsRegistry" needs to be exported by the entry point index.d.ts + // + // (undocumented) + protected readonly triggerToActions: TriggerToActionsRegistry; + // (undocumented) + readonly unregisterAction: (actionId: string) => void; +} + +// Warning: (ae-missing-release-tag) "UiActionsServiceParams" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface UiActionsServiceParams { + // (undocumented) + readonly actions?: ActionRegistry; + // (undocumented) + readonly triggers?: TriggerRegistry; + readonly triggerToActions?: TriggerToActionsRegistry; +} + +// Warning: (ae-missing-release-tag) "UiActionsSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type UiActionsSetup = Pick; + +// Warning: (ae-missing-release-tag) "UiActionsStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type UiActionsStart = PublicMethodsOf; + +// Warning: (ae-missing-release-tag) "VALUE_CLICK_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const VALUE_CLICK_TRIGGER = "VALUE_CLICK_TRIGGER"; + +// Warning: (ae-missing-release-tag) "valueClickTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "VISUALIZE_FIELD_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const VISUALIZE_FIELD_TRIGGER = "VISUALIZE_FIELD_TRIGGER"; + +// Warning: (ae-missing-release-tag) "VISUALIZE_GEO_FIELD_TRIGGER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const VISUALIZE_GEO_FIELD_TRIGGER = "VISUALIZE_GEO_FIELD_TRIGGER"; + +// Warning: (ae-missing-release-tag) "VisualizeFieldContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface VisualizeFieldContext { + // (undocumented) + contextualFields?: string[]; + // (undocumented) + fieldName: string; + // (undocumented) + indexPatternId: string; +} + +// Warning: (ae-missing-release-tag) "visualizeFieldTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const visualizeFieldTrigger: Trigger<'VISUALIZE_FIELD_TRIGGER'>; + +// Warning: (ae-missing-release-tag) "visualizeGeoFieldTrigger" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const visualizeGeoFieldTrigger: Trigger<'VISUALIZE_GEO_FIELD_TRIGGER'>; + + +// (No @packageDocumentation comment for this package) + +``` diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts index 6028177964fb7..ec5f3afa19c94 100644 --- a/src/plugins/ui_actions/public/service/ui_actions_service.ts +++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts @@ -99,6 +99,10 @@ export class UiActionsService { this.actions.delete(actionId); }; + public readonly hasAction = (actionId: string): boolean => { + return this.actions.has(actionId); + }; + public readonly attachAction = (triggerId: T, actionId: string): void => { const trigger = this.triggers.get(triggerId); diff --git a/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts index 81120990001e3..af2510467ba87 100644 --- a/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts +++ b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts @@ -22,7 +22,7 @@ import { openContextMenu } from '../context_menu'; import { uiActionsPluginMock } from '../mocks'; import { Trigger } from '../triggers'; import { TriggerId, ActionType } from '../types'; -import { wait } from '@testing-library/dom'; +import { waitFor } from '@testing-library/dom'; jest.mock('../context_menu'); @@ -85,7 +85,7 @@ test('executes a single action mapped to a trigger', async () => { expect(executeFn).toBeCalledWith(expect.objectContaining(context)); }); -test('throws an error if there are no compatible actions to execute', async () => { +test("doesn't throw an error if there are no compatible actions to execute", async () => { const { setup, doStart } = uiActions; const trigger: Trigger = { id: 'MY-TRIGGER' as TriggerId, @@ -98,9 +98,7 @@ test('throws an error if there are no compatible actions to execute', async () = const start = doStart(); await expect( start.executeTriggerActions('MY-TRIGGER' as TriggerId, context) - ).rejects.toMatchObject( - new Error('No compatible actions found to execute for trigger [triggerId = MY-TRIGGER].') - ); + ).resolves.toBeUndefined(); }); test('does not execute an incompatible action', async () => { @@ -149,7 +147,7 @@ test('shows a context menu when more than one action is mapped to a trigger', as jest.runAllTimers(); - await wait(() => { + await waitFor(() => { expect(executeFn).toBeCalledTimes(0); expect(openContextMenu).toHaveBeenCalledTimes(1); }); @@ -197,7 +195,7 @@ test("doesn't show a context menu for auto executable actions", async () => { jest.runAllTimers(); - await wait(() => { + await waitFor(() => { expect(executeFn).toBeCalledTimes(2); expect(openContextMenu).toHaveBeenCalledTimes(0); }); diff --git a/src/plugins/ui_actions/public/triggers/trigger_internal.ts b/src/plugins/ui_actions/public/triggers/trigger_internal.ts index c91468d31add5..c766b5c798ecb 100644 --- a/src/plugins/ui_actions/public/triggers/trigger_internal.ts +++ b/src/plugins/ui_actions/public/triggers/trigger_internal.ts @@ -35,12 +35,6 @@ export class TriggerInternal { const triggerId = this.trigger.id; const actions = await this.service.getTriggerCompatibleActions!(triggerId, context); - if (!actions.length) { - throw new Error( - `No compatible actions found to execute for trigger [triggerId = ${triggerId}].` - ); - } - await Promise.all([ actions.map((action) => this.service.executionService.execute({ diff --git a/src/plugins/usage_collection/README.md b/src/plugins/usage_collection/README.md index 0b1cca07de007..9955f9fac81ca 100644 --- a/src/plugins/usage_collection/README.md +++ b/src/plugins/usage_collection/README.md @@ -63,7 +63,7 @@ All you need to provide is a `type` for organizing your fields, `schema` field t total: 'long', }, }, - fetch: async (callCluster: APICluster) => { + fetch: async (callCluster: APICluster, esClient: IClusterClient) => { // query ES and get some data // summarize the data into a model @@ -86,9 +86,9 @@ Some background: - `MY_USAGE_TYPE` can be any string. It usually matches the plugin name. As a safety mechanism, we double check there are no duplicates at the moment of registering the collector. - The `fetch` method needs to support multiple contexts in which it is called. For example, when stats are pulled from a Kibana Metricbeat module, the Beat calls Kibana's stats API to invoke usage collection. -In this case, the `fetch` method is called as a result of an HTTP API request and `callCluster` wraps `callWithRequest`, where the request headers are expected to have read privilege on the entire `.kibana' index. +In this case, the `fetch` method is called as a result of an HTTP API request and `callCluster` wraps `callWithRequest` or `esClient` wraps `asCurrentUser`, where the request headers are expected to have read privilege on the entire `.kibana' index. -Note: there will be many cases where you won't need to use the `callCluster` function that gets passed in to your `fetch` method at all. Your feature might have an accumulating value in server memory, or read something from the OS, or use other clients like a custom SavedObjects client. In that case it's up to the plugin to initialize those clients like the example below: +Note: there will be many cases where you won't need to use the `callCluster` (or `esClient`) function that gets passed in to your `fetch` method at all. Your feature might have an accumulating value in server memory, or read something from the OS, or use other clients like a custom SavedObjects client. In that case it's up to the plugin to initialize those clients like the example below: ```ts // server/plugin.ts @@ -140,6 +140,14 @@ The `AllowedSchemaTypes` is the list of allowed schema types for the usage field 'keyword', 'text', 'number', 'boolean', 'long', 'date', 'float' ``` +### Arrays + +If any of your properties is an array, the schema definition must follow the convention below: + +``` +{ type: 'array', items: {...mySchemaDefinitionOfTheEntriesInTheArray} } +``` + ### Example ```ts @@ -152,6 +160,8 @@ export const myCollector = makeUsageCollector({ some_obj: { total: 123, }, + some_array: ['value1', 'value2'], + some_array_of_obj: [{total: 123}], }; }, schema: { @@ -163,6 +173,18 @@ export const myCollector = makeUsageCollector({ type: 'number', }, }, + some_array: { + type: 'array', + items: { type: 'keyword' } + }, + some_array_of_obj: { + type: 'array', + items: { + total: { + type: 'number', + }, + }, + }, }, }); ``` @@ -302,4 +324,4 @@ These saved objects are automatically consumed by the stats API and surfaced und By storing these metrics and their counts as key-value pairs, we can add more metrics without having to worry about exceeding the 1000-field soft limit in Elasticsearch. -The only caveat is that it makes it harder to consume in Kibana when analysing each entry in the array separately. In the telemetry team we are working to find a solution to this. We are building a new way of reporting telemetry called [Pulse](../../../rfcs/text/0008_pulse.md) that will help on making these UI-Metrics easier to consume. +The only caveat is that it makes it harder to consume in Kibana when analysing each entry in the array separately. In the telemetry team we are working to find a solution to this. diff --git a/src/plugins/usage_collection/server/collector/collector.test.ts b/src/plugins/usage_collection/server/collector/collector.test.ts index a3e2425c1f122..375fe4f7686c0 100644 --- a/src/plugins/usage_collection/server/collector/collector.test.ts +++ b/src/plugins/usage_collection/server/collector/collector.test.ts @@ -153,7 +153,10 @@ describe('collector', () => { isReady: () => false, fetch: () => ({ testPass: [{ name: 'a', value: 100 }] }), schema: { - testPass: { name: { type: 'keyword' }, value: { type: 'long' } }, + testPass: { + type: 'array', + items: { name: { type: 'keyword' }, value: { type: 'long' } }, + }, }, }); expect(collector).toBeDefined(); @@ -166,7 +169,10 @@ describe('collector', () => { fetch: () => ({ testPass: [{ name: 'a', value: 100 }], otherProp: 1 }), // @ts-expect-error schema: { - testPass: { name: { type: 'keyword' }, value: { type: 'long' } }, + testPass: { + type: 'array', + items: { name: { type: 'keyword' }, value: { type: 'long' } }, + }, }, }); expect(collector).toBeDefined(); @@ -185,7 +191,10 @@ describe('collector', () => { }, // @ts-expect-error schema: { - testPass: { name: { type: 'keyword' }, value: { type: 'long' } }, + testPass: { + type: 'array', + items: { name: { type: 'keyword' }, value: { type: 'long' } }, + }, }, }); expect(collector).toBeDefined(); @@ -203,7 +212,10 @@ describe('collector', () => { return { otherProp: 1 }; }, schema: { - testPass: { name: { type: 'keyword' }, value: { type: 'long' } }, + testPass: { + type: 'array', + items: { name: { type: 'keyword' }, value: { type: 'long' } }, + }, otherProp: { type: 'long' }, }, }); diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index d57700024c088..8491bdb0c957c 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Logger, LegacyAPICaller } from 'kibana/server'; +import { Logger, LegacyAPICaller, ElasticsearchClient } from 'kibana/server'; export type CollectorFormatForBulkUpload = (result: T) => { type: string; payload: U }; @@ -38,17 +38,18 @@ export type RecursiveMakeSchemaFrom = U extends object ? MakeSchemaFrom : { type: AllowedSchemaTypes }; +// Using Required to enforce all optional keys in the object export type MakeSchemaFrom = { - [Key in keyof Base]: Base[Key] extends Array - ? RecursiveMakeSchemaFrom - : RecursiveMakeSchemaFrom; + [Key in keyof Required]: Required[Key] extends Array + ? { type: 'array'; items: RecursiveMakeSchemaFrom } + : RecursiveMakeSchemaFrom[Key]>; }; export interface CollectorOptions { type: string; init?: Function; - schema?: MakeSchemaFrom>; // Using Required to enforce all optional keys in the object - fetch: (callCluster: LegacyAPICaller) => Promise | T; + schema?: MakeSchemaFrom; + fetch: (callCluster: LegacyAPICaller, esClient?: ElasticsearchClient) => Promise | T; /* * A hook for allowing the fetched data payload to be organized into a typed * data model for internal bulk upload. See defaultFormatterForBulkUpload for diff --git a/src/plugins/usage_collection/server/collector/collector_set.test.ts b/src/plugins/usage_collection/server/collector/collector_set.test.ts index 545642c5dcfa3..3f943ad8bf2ff 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.test.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.test.ts @@ -21,7 +21,7 @@ import { noop } from 'lodash'; import { Collector } from './collector'; import { CollectorSet } from './collector_set'; import { UsageCollector } from './usage_collector'; -import { loggingSystemMock } from '../../../../core/server/mocks'; +import { elasticsearchServiceMock, loggingSystemMock } from '../../../../core/server/mocks'; const logger = loggingSystemMock.createLogger(); @@ -42,6 +42,7 @@ describe('CollectorSet', () => { }); const mockCallCluster = jest.fn().mockResolvedValue({ passTest: 1000 }); + const mockEsClient = elasticsearchServiceMock.createClusterClient().asInternalUser; it('should throw an error if non-Collector type of object is registered', () => { const collectors = new CollectorSet({ logger }); @@ -85,7 +86,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockCallCluster); + const result = await collectors.bulkFetch(mockCallCluster, mockEsClient); expect(loggerSpies.debug).toHaveBeenCalledTimes(1); expect(loggerSpies.debug).toHaveBeenCalledWith( 'Fetching data from MY_TEST_COLLECTOR collector' @@ -110,7 +111,7 @@ describe('CollectorSet', () => { let result; try { - result = await collectors.bulkFetch(mockCallCluster); + result = await collectors.bulkFetch(mockCallCluster, mockEsClient); } catch (err) { // Do nothing } @@ -128,7 +129,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockCallCluster); + const result = await collectors.bulkFetch(mockCallCluster, mockEsClient); expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', @@ -146,7 +147,7 @@ describe('CollectorSet', () => { } as any) ); - const result = await collectors.bulkFetch(mockCallCluster); + const result = await collectors.bulkFetch(mockCallCluster, mockEsClient); expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', @@ -169,7 +170,7 @@ describe('CollectorSet', () => { }) ); - const result = await collectors.bulkFetch(mockCallCluster); + const result = await collectors.bulkFetch(mockCallCluster, mockEsClient); expect(result).toStrictEqual([ { type: 'MY_TEST_COLLECTOR', diff --git a/src/plugins/usage_collection/server/collector/collector_set.ts b/src/plugins/usage_collection/server/collector/collector_set.ts index fce17a46b7168..6861be7f4f76b 100644 --- a/src/plugins/usage_collection/server/collector/collector_set.ts +++ b/src/plugins/usage_collection/server/collector/collector_set.ts @@ -18,7 +18,7 @@ */ import { snakeCase } from 'lodash'; -import { Logger, LegacyAPICaller } from 'kibana/server'; +import { Logger, LegacyAPICaller, ElasticsearchClient } from 'kibana/server'; import { Collector, CollectorOptions } from './collector'; import { UsageCollector } from './usage_collector'; @@ -117,8 +117,12 @@ export class CollectorSet { return allReady; }; + // all collections eventually pass through bulkFetch. + // the shape of the response is different when using the new ES client as is the error handling. + // We'll handle the refactor for using the new client in a follow up PR. public bulkFetch = async ( callCluster: LegacyAPICaller, + esClient: ElasticsearchClient, collectors: Map> = this.collectors ) => { const responses = await Promise.all( @@ -127,7 +131,7 @@ export class CollectorSet { try { return { type: collector.type, - result: await collector.fetch(callCluster), + result: await collector.fetch(callCluster, esClient), // each collector must ensure they handle the response appropriately. }; } catch (err) { this.logger.warn(err); @@ -149,9 +153,9 @@ export class CollectorSet { return this.makeCollectorSetFromArray(filtered); }; - public bulkFetchUsage = async (callCluster: LegacyAPICaller) => { + public bulkFetchUsage = async (callCluster: LegacyAPICaller, esClient: ElasticsearchClient) => { const usageCollectors = this.getFilteredCollectorSet((c) => c instanceof UsageCollector); - return await this.bulkFetch(callCluster, usageCollectors.collectors); + return await this.bulkFetch(callCluster, esClient, usageCollectors.collectors); }; // convert an array of fetched stats results into key/object diff --git a/src/plugins/usage_collection/server/routes/stats.ts b/src/plugins/usage_collection/server/routes/stats.ts index 7c64c9f180319..ef5da2eb11ba6 100644 --- a/src/plugins/usage_collection/server/routes/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats.ts @@ -24,6 +24,7 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { + ElasticsearchClient, IRouter, LegacyAPICaller, MetricsServiceSetup, @@ -61,8 +62,11 @@ export function registerStatsRoute({ metrics: MetricsServiceSetup; overallStatus$: Observable; }) { - const getUsage = async (callCluster: LegacyAPICaller): Promise => { - const usage = await collectorSet.bulkFetchUsage(callCluster); + const getUsage = async ( + callCluster: LegacyAPICaller, + esClient: ElasticsearchClient + ): Promise => { + const usage = await collectorSet.bulkFetchUsage(callCluster, esClient); return collectorSet.toObject(usage); }; @@ -96,13 +100,14 @@ export function registerStatsRoute({ let extended; if (isExtended) { const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; + const esClient = context.core.elasticsearch.client.asCurrentUser; const collectorsReady = await collectorSet.areAllCollectorsReady(); if (shouldGetUsage && !collectorsReady) { return res.customError({ statusCode: 503, body: { message: STATS_NOT_READY_MESSAGE } }); } - const usagePromise = shouldGetUsage ? getUsage(callCluster) : Promise.resolve({}); + const usagePromise = shouldGetUsage ? getUsage(callCluster, esClient) : Promise.resolve({}); const [usage, clusterUuid] = await Promise.all([usagePromise, getClusterUuid(callCluster)]); let modifiedUsage = usage; diff --git a/src/plugins/vis_type_markdown/kibana.json b/src/plugins/vis_type_markdown/kibana.json index 5723fdefe1e4c..c0afcb0e99d13 100644 --- a/src/plugins/vis_type_markdown/kibana.json +++ b/src/plugins/vis_type_markdown/kibana.json @@ -4,5 +4,5 @@ "ui": true, "server": true, "requiredPlugins": ["expressions", "visualizations"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "charts", "visualizations", "expressions", "visDefaultEditor"] + "requiredBundles": ["kibanaReact", "charts", "visualizations", "expressions", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap b/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap index 473e2cba742b7..9983f67d4be4d 100644 --- a/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap +++ b/src/plugins/vis_type_markdown/public/__snapshots__/markdown_fn.test.ts.snap @@ -5,7 +5,7 @@ Object { "as": "markdown_vis", "type": "render", "value": Object { - "visConfig": Object { + "visParams": Object { "fontSize": 12, "markdown": "## hello _markdown_", "openLinksInNewTab": true, diff --git a/src/plugins/vis_type_markdown/public/_markdown_vis.scss b/src/plugins/vis_type_markdown/public/_markdown_vis.scss deleted file mode 100644 index fb0a3d05e5e85..0000000000000 --- a/src/plugins/vis_type_markdown/public/_markdown_vis.scss +++ /dev/null @@ -1,15 +0,0 @@ -.mkdVis { - padding: $euiSizeS; - width: 100%; -} - -.visEditor--markdown { - .visEditorSidebar__config > *, - .visEditor--markdown__textarea { - flex-grow: 1; - } - - .mkdEditor { - height: 100%; - } -} diff --git a/src/plugins/vis_type_markdown/public/index.scss b/src/plugins/vis_type_markdown/public/index.scss deleted file mode 100644 index ddb7fe3a6b0d9..0000000000000 --- a/src/plugins/vis_type_markdown/public/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Prefix all styles with "mkd" to avoid conflicts. -// Examples -// mkdChart -// mkdChart__legend -// mkdChart__legend--small -// mkdChart__legend-isLoading - -@import './markdown_vis'; diff --git a/src/plugins/vis_type_markdown/public/markdown_fn.ts b/src/plugins/vis_type_markdown/public/markdown_fn.ts index 4b3c9989431f9..eaa2c840f8046 100644 --- a/src/plugins/vis_type_markdown/public/markdown_fn.ts +++ b/src/plugins/vis_type_markdown/public/markdown_fn.ts @@ -21,16 +21,16 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, Render } from '../../expressions/public'; import { Arguments, MarkdownVisParams } from './types'; -interface RenderValue { +export interface MarkdownVisRenderValue { visType: 'markdown'; - visConfig: MarkdownVisParams; + visParams: MarkdownVisParams; } export type MarkdownVisExpressionFunctionDefinition = ExpressionFunctionDefinition< 'markdownVis', unknown, Arguments, - Render + Render >; export const createMarkdownVisFn = (): MarkdownVisExpressionFunctionDefinition => ({ @@ -70,7 +70,7 @@ export const createMarkdownVisFn = (): MarkdownVisExpressionFunctionDefinition = as: 'markdown_vis', value: { visType: 'markdown', - visConfig: { + visParams: { markdown: args.markdown, openLinksInNewTab: args.openLinksInNewTab, fontSize: parseInt(args.font.spec.fontSize || '12', 10), diff --git a/src/plugins/vis_type_markdown/public/markdown_renderer.tsx b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx index 5950a762635b2..8071196c6a213 100644 --- a/src/plugins/vis_type_markdown/public/markdown_renderer.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx @@ -17,41 +17,29 @@ * under the License. */ -import React from 'react'; +import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { VisualizationContainer } from '../../visualizations/public'; import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers'; -import { MarkdownVisWrapper } from './markdown_vis_controller'; -import { StartServicesGetter } from '../../kibana_utils/public'; +import { MarkdownVisRenderValue } from './markdown_fn'; -export const getMarkdownRenderer = (start: StartServicesGetter) => { - const markdownVisRenderer: () => ExpressionRenderDefinition = () => ({ - name: 'markdown_vis', - displayName: 'markdown visualization', - reuseDomNode: true, - render: async (domNode: HTMLElement, config: any, handlers: any) => { - const { visConfig } = config; +// @ts-ignore +const MarkdownVisComponent = lazy(() => import('./markdown_vis_controller')); - const I18nContext = await start().core.i18n.Context; +export const markdownVisRenderer: ExpressionRenderDefinition = { + name: 'markdown_vis', + displayName: 'markdown visualization', + reuseDomNode: true, + render: async (domNode, { visParams }, handlers) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); - - render( - - - - - , - domNode - ); - }, - }); - - return markdownVisRenderer; + render( + + + , + domNode + ); + }, }; diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.scss b/src/plugins/vis_type_markdown/public/markdown_vis.scss new file mode 100644 index 0000000000000..2356562a86ed0 --- /dev/null +++ b/src/plugins/vis_type_markdown/public/markdown_vis.scss @@ -0,0 +1,22 @@ +// Prefix all styles with "mkd" to avoid conflicts. +// Examples +// mkdChart +// mkdChart__legend +// mkdChart__legend--small +// mkdChart__legend-isLoading + +.mkdVis { + padding: $euiSizeS; + width: 100%; +} + +.visEditor--markdown { + .visEditorSidebar__config > *, + .visEditor--markdown__textarea { + flex-grow: 1; + } + + .mkdEditor { + height: 100%; + } +} diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx index ff0cc89a5d9c9..36850fc820ded 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx @@ -18,11 +18,14 @@ */ import React from 'react'; -import { render, mount } from 'enzyme'; -import { MarkdownVisWrapper } from './markdown_vis_controller'; +import { wait } from '@testing-library/dom'; +import { render, cleanup } from '@testing-library/react/pure'; +import MarkdownVisComponent from './markdown_vis_controller'; + +afterEach(cleanup); describe('markdown vis controller', () => { - it('should set html from markdown params', () => { + it('should set html from markdown params', async () => { const vis = { params: { openLinksInNewTab: false, @@ -32,13 +35,22 @@ describe('markdown vis controller', () => { }, }; - const wrapper = render( - + const { getByTestId, getByText } = render( + ); - expect(wrapper.find('a').text()).toBe('markdown'); + + await wait(() => getByTestId('markdownBody')); + + expect(getByText('markdown')).toMatchInlineSnapshot(` + + markdown + + `); }); - it('should not render the html', () => { + it('should not render the html', async () => { const vis = { params: { openLinksInNewTab: false, @@ -47,13 +59,20 @@ describe('markdown vis controller', () => { }, }; - const wrapper = render( - + const { getByTestId, getByText } = render( + ); - expect(wrapper.text()).toBe('Testing html\n'); + + await wait(() => getByTestId('markdownBody')); + + expect(getByText(/testing/i)).toMatchInlineSnapshot(` +

+ Testing <a>html</a> +

+ `); }); - it('should update the HTML when render again with changed params', () => { + it('should update the HTML when render again with changed params', async () => { const vis = { params: { openLinksInNewTab: false, @@ -62,13 +81,18 @@ describe('markdown vis controller', () => { }, }; - const wrapper = mount( - + const { getByTestId, getByText, rerender } = render( + ); - expect(wrapper.text().trim()).toBe('Initial'); + + await wait(() => getByTestId('markdownBody')); + + expect(getByText(/initial/i)).toBeInTheDocument(); + vis.params.markdown = 'Updated'; - wrapper.setProps({ vis }); - expect(wrapper.text().trim()).toBe('Updated'); + rerender(); + + expect(getByText(/Updated/i)).toBeInTheDocument(); }); describe('renderComplete', () => { @@ -86,56 +110,47 @@ describe('markdown vis controller', () => { renderComplete.mockClear(); }); - it('should be called on initial rendering', () => { - mount( - + it('should be called on initial rendering', async () => { + const { getByTestId } = render( + ); - expect(renderComplete.mock.calls.length).toBe(1); + + await wait(() => getByTestId('markdownBody')); + + expect(renderComplete).toHaveBeenCalledTimes(1); }); - it('should be called on successive render when params change', () => { - mount( - + it('should be called on successive render when params change', async () => { + const { getByTestId, rerender } = render( + ); - expect(renderComplete.mock.calls.length).toBe(1); + + await wait(() => getByTestId('markdownBody')); + + expect(renderComplete).toHaveBeenCalledTimes(1); + renderComplete.mockClear(); vis.params.markdown = 'changed'; - mount( - - ); - expect(renderComplete.mock.calls.length).toBe(1); + + rerender(); + + expect(renderComplete).toHaveBeenCalledTimes(1); }); - it('should be called on successive render even without data change', () => { - mount( - + it('should be called on successive render even without data change', async () => { + const { getByTestId, rerender } = render( + ); - expect(renderComplete.mock.calls.length).toBe(1); + + await wait(() => getByTestId('markdownBody')); + + expect(renderComplete).toHaveBeenCalledTimes(1); + renderComplete.mockClear(); - mount( - - ); - expect(renderComplete.mock.calls.length).toBe(1); + + rerender(); + + expect(renderComplete).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx index e1155ca42df72..a2387b96eab6d 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.tsx @@ -17,83 +17,35 @@ * under the License. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { Markdown } from '../../kibana_react/public'; import { MarkdownVisParams } from './types'; +import './markdown_vis.scss'; + interface MarkdownVisComponentProps extends MarkdownVisParams { renderComplete: () => void; } -/** - * The MarkdownVisComponent renders markdown to HTML and presents it. - */ -class MarkdownVisComponent extends React.Component { - /** - * Will be called after the first render when the component is present in the DOM. - * - * We call renderComplete here, to signal, that we are done with rendering. - */ - componentDidMount() { - this.props.renderComplete(); - } - - /** - * Will be called after the component has been updated and the changes has been - * flushed into the DOM. - * - * We will use this to signal that we are done rendering by calling the - * renderComplete property. - */ - componentDidUpdate() { - this.props.renderComplete(); - } +const MarkdownVisComponent = ({ + fontSize, + markdown, + openLinksInNewTab, + renderComplete, +}: MarkdownVisComponentProps) => { + useEffect(renderComplete); // renderComplete will be called after each render to signal, that we are done with rendering. - /** - * Render the actual HTML. - * Note: if only fontSize parameter has changed, this method will be called - * and return the appropriate JSX, but React will detect, that only the - * style argument has been updated, and thus only set this attribute to the DOM. - */ - render() { - return ( -
- -
- ); - } -} - -/** - * This is a wrapper component, that is actually used as the visualization. - * The sole purpose of this component is to extract all required parameters from - * the properties and pass them down as separate properties to the actual component. - * That way the actual (MarkdownVisComponent) will properly trigger it's prop update - * callback (componentWillReceiveProps) if one of these params change. It wouldn't - * trigger otherwise (e.g. it doesn't for this wrapper), since it only triggers - * if the reference to the prop changes (in this case the reference to vis). - * - * The way React works, this wrapper nearly brings no overhead, but allows us - * to use proper lifecycle methods in the actual component. - */ - -export interface MarkdownVisWrapperProps { - visParams: MarkdownVisParams; - fireEvent: (event: any) => void; - renderComplete: () => void; -} - -export function MarkdownVisWrapper(props: MarkdownVisWrapperProps) { return ( - +
+ +
); -} +}; + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { MarkdownVisComponent as default }; diff --git a/src/plugins/vis_type_markdown/public/plugin.ts b/src/plugins/vis_type_markdown/public/plugin.ts index c117df7e0fa33..790b19876d366 100644 --- a/src/plugins/vis_type_markdown/public/plugin.ts +++ b/src/plugins/vis_type_markdown/public/plugin.ts @@ -24,10 +24,7 @@ import { VisualizationsSetup } from '../../visualizations/public'; import { markdownVisDefinition } from './markdown_vis'; import { createMarkdownVisFn } from './markdown_fn'; import { ConfigSchema } from '../config'; - -import './index.scss'; -import { getMarkdownRenderer } from './markdown_renderer'; -import { createStartServicesGetter } from '../../kibana_utils/public'; +import { markdownVisRenderer } from './markdown_renderer'; /** @internal */ export interface MarkdownPluginSetupDependencies { @@ -44,9 +41,8 @@ export class MarkdownPlugin implements Plugin { } public setup(core: CoreSetup, { expressions, visualizations }: MarkdownPluginSetupDependencies) { - const start = createStartServicesGetter(core.getStartServices); visualizations.createBaseVisualization(markdownVisDefinition); - expressions.registerRenderer(getMarkdownRenderer(start)); + expressions.registerRenderer(markdownVisRenderer); expressions.registerFunction(createMarkdownVisFn); } diff --git a/src/plugins/vis_type_metric/public/_metric_vis.scss b/src/plugins/vis_type_metric/public/_metric_vis.scss deleted file mode 100644 index b1f04cc93c4b7..0000000000000 --- a/src/plugins/vis_type_metric/public/_metric_vis.scss +++ /dev/null @@ -1,33 +0,0 @@ -.mtrVis { - width: 100%; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - flex-wrap: wrap; -} - -.mtrVis__value { - @include euiTextTruncate; - font-weight: $euiFontWeightBold; -} - -.mtrVis__container { - text-align: center; - padding: $euiSize; -} - -.mtrVis__container--light { - color: $euiColorEmptyShade; -} - -.mtrVis__container-isFilterable { - cursor: pointer; - transition: transform $euiAnimSpeedNormal $euiAnimSlightResistance; - transform: translate(0, 0); - - &:hover, &:focus { - box-shadow: none; - transform: translate(0, -2px); - } -} diff --git a/src/plugins/vis_type_metric/public/components/metric_vis.scss b/src/plugins/vis_type_metric/public/components/metric_vis.scss new file mode 100644 index 0000000000000..5665ba8e8d099 --- /dev/null +++ b/src/plugins/vis_type_metric/public/components/metric_vis.scss @@ -0,0 +1,40 @@ +// Prefix all styles with "mtr" to avoid conflicts. +// Examples +// mtrChart +// mtrChart__legend +// mtrChart__legend--small +// mtrChart__legend-isLoading + +.mtrVis { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + flex-wrap: wrap; +} + +.mtrVis__value { + @include euiTextTruncate; + font-weight: $euiFontWeightBold; +} + +.mtrVis__container { + text-align: center; + padding: $euiSize; +} + +.mtrVis__container--light { + color: $euiColorEmptyShade; +} + +.mtrVis__container-isFilterable { + cursor: pointer; + transition: transform $euiAnimSpeedNormal $euiAnimSlightResistance; + transform: translate(0, 0); + + &:hover, &:focus { + box-shadow: none; + transform: translate(0, -2px); + } +} diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx index b56d4e4f62e41..7f82c6adb5694 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { MetricVisComponent, MetricVisComponentProps } from './metric_vis_component'; +import MetricVisComponent, { MetricVisComponentProps } from './metric_vis_component'; jest.mock('../services', () => ({ getFormatService: () => ({ diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx index 9ce3820ee4e23..e5c7db65c09a8 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx @@ -30,14 +30,16 @@ import { getFormatService } from '../services'; import { SchemaConfig } from '../../../visualizations/public'; import { Range } from '../../../expressions/public'; +import './metric_vis.scss'; + export interface MetricVisComponentProps { - visParams: VisParams; + visParams: Pick; visData: Input; fireEvent: (event: any) => void; renderComplete: () => void; } -export class MetricVisComponent extends Component { +class MetricVisComponent extends Component { private getLabels() { const config = this.props.visParams.metric; const isPercentageMode = config.percentageMode; @@ -209,3 +211,7 @@ export class MetricVisComponent extends Component { return metricsHtml; } } + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { MetricVisComponent as default }; diff --git a/src/plugins/vis_type_metric/public/index.scss b/src/plugins/vis_type_metric/public/index.scss deleted file mode 100644 index 638f9ac1ef93a..0000000000000 --- a/src/plugins/vis_type_metric/public/index.scss +++ /dev/null @@ -1,8 +0,0 @@ -// Prefix all styles with "mtr" to avoid conflicts. -// Examples -// mtrChart -// mtrChart__legend -// mtrChart__legend--small -// mtrChart__legend-isLoading - -@import 'metric_vis'; diff --git a/src/plugins/vis_type_metric/public/index.ts b/src/plugins/vis_type_metric/public/index.ts index 3d3e1879a51d9..ac541a9577cfc 100644 --- a/src/plugins/vis_type_metric/public/index.ts +++ b/src/plugins/vis_type_metric/public/index.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import './index.scss'; import { PluginInitializerContext } from 'kibana/public'; import { MetricVisPlugin as Plugin } from './plugin'; diff --git a/src/plugins/vis_type_metric/public/metric_vis_fn.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.ts index b58be63581724..97b1e6822333e 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_fn.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_fn.ts @@ -46,7 +46,7 @@ interface Arguments { bucket: any; // these aren't typed yet } -interface RenderValue { +export interface MetricVisRenderValue { visType: typeof visType; visData: Input; visConfig: Pick; @@ -57,7 +57,7 @@ export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition 'metricVis', Input, Arguments, - Render + Render >; export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ diff --git a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx index 2bae668b080ea..bf0d6da9fba05 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx +++ b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx @@ -17,37 +17,33 @@ * under the License. */ -import React from 'react'; +import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { MetricVisComponent } from './components/metric_vis_component'; -import { getI18n } from './services'; + import { VisualizationContainer } from '../../visualizations/public'; import { ExpressionRenderDefinition } from '../../expressions/common/expression_renderers'; +import { MetricVisRenderValue } from './metric_vis_fn'; +// @ts-ignore +const MetricVisComponent = lazy(() => import('./components/metric_vis_component')); -export const metricVisRenderer: () => ExpressionRenderDefinition = () => ({ +export const metricVisRenderer: () => ExpressionRenderDefinition = () => ({ name: 'metric_vis', displayName: 'metric visualization', reuseDomNode: true, - render: async (domNode: HTMLElement, config: any, handlers: any) => { - const { visData, visConfig } = config; - - const I18nContext = getI18n().Context; - + render: async (domNode, { visData, visConfig }, handlers) => { handlers.onDestroy(() => { unmountComponentAtNode(domNode); }); render( - - - - - , + + + , domNode ); }, diff --git a/src/plugins/vis_type_metric/public/metric_vis_type.ts b/src/plugins/vis_type_metric/public/metric_vis_type.ts index 6b4d6e151693f..1c5afd396c2c3 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_type.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_type.ts @@ -18,13 +18,14 @@ */ import { i18n } from '@kbn/i18n'; +import { BaseVisTypeOptions } from 'src/plugins/visualizations/public'; import { MetricVisOptions } from './components/metric_vis_options'; import { ColorSchemas, colorSchemas, ColorModes } from '../../charts/public'; import { AggGroupNames } from '../../data/public'; import { Schemas } from '../../vis_default_editor/public'; import { toExpressionAst } from './to_ast'; -export const createMetricVisTypeDefinition = () => ({ +export const createMetricVisTypeDefinition = (): BaseVisTypeOptions => ({ name: 'metric', title: i18n.translate('visTypeMetric.metricTitle', { defaultMessage: 'Metric' }), icon: 'visMetric', diff --git a/src/plugins/vis_type_metric/public/plugin.ts b/src/plugins/vis_type_metric/public/plugin.ts index b9e094aa76889..c653d1bdaf965 100644 --- a/src/plugins/vis_type_metric/public/plugin.ts +++ b/src/plugins/vis_type_metric/public/plugin.ts @@ -25,7 +25,7 @@ import { createMetricVisFn } from './metric_vis_fn'; import { createMetricVisTypeDefinition } from './metric_vis_type'; import { ChartsPluginSetup } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; -import { setFormatService, setI18n } from './services'; +import { setFormatService } from './services'; import { ConfigSchema } from '../config'; import { metricVisRenderer } from './metric_vis_renderer'; @@ -59,7 +59,6 @@ export class MetricVisPlugin implements Plugin { } public start(core: CoreStart, { data }: MetricVisPluginStartDependencies) { - setI18n(core.i18n); setFormatService(data.fieldFormats); } } diff --git a/src/plugins/vis_type_metric/public/services.ts b/src/plugins/vis_type_metric/public/services.ts index 0e19cfdce228d..681afbaf0b268 100644 --- a/src/plugins/vis_type_metric/public/services.ts +++ b/src/plugins/vis_type_metric/public/services.ts @@ -17,12 +17,9 @@ * under the License. */ -import { I18nStart } from 'kibana/public'; import { createGetterSetter } from '../../kibana_utils/common'; import { DataPublicPluginStart } from '../../data/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] >('metric data.fieldFormats'); - -export const [getI18n, setI18n] = createGetterSetter('I18n'); diff --git a/src/plugins/vis_type_table/public/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/table_vis_controller.test.ts index 56d17c187bd3f..2b4017ae0ee81 100644 --- a/src/plugins/vis_type_table/public/table_vis_controller.test.ts +++ b/src/plugins/vis_type_table/public/table_vis_controller.test.ts @@ -22,8 +22,6 @@ import 'angular-mocks'; import 'angular-sanitize'; import $ from 'jquery'; -// @ts-ignore -import StubIndexPattern from 'test_utils/stub_index_pattern'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; import { getTableVisTypeDefinition } from './table_vis_type'; @@ -32,6 +30,7 @@ import { stubFields } from '../../data/public/stubs'; import { tableVisResponseHandler } from './table_vis_response_handler'; import { coreMock } from '../../../core/public/mocks'; import { IAggConfig, search } from '../../data/public'; +import { getStubIndexPattern } from '../../data/public/test_utils'; // TODO: remove linting disable import { searchServiceMock } from '../../data/public/search/mocks'; @@ -105,7 +104,7 @@ describe('Table Vis - Controller', () => { ); beforeEach(() => { - stubIndexPattern = new StubIndexPattern( + stubIndexPattern = getStubIndexPattern( 'logstash-*', (cfg: any) => cfg, 'time', @@ -121,7 +120,7 @@ describe('Table Vis - Controller', () => { function getRangeVis(params?: object) { return ({ type: tableVisTypeDefinition, - params: Object.assign({}, tableVisTypeDefinition.visConfig.defaults, params), + params: Object.assign({}, tableVisTypeDefinition.visConfig?.defaults, params), data: { aggs: createAggConfigs(stubIndexPattern, [ { type: 'count', schema: 'metric' }, diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts index 80d53021b7866..c1419a4847458 100644 --- a/src/plugins/vis_type_table/public/table_vis_type.ts +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -20,7 +20,7 @@ import { CoreSetup, PluginInitializerContext } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../data/public'; import { Schemas } from '../../vis_default_editor/public'; -import { Vis } from '../../visualizations/public'; +import { BaseVisTypeOptions, Vis } from '../../visualizations/public'; import { tableVisResponseHandler } from './table_vis_response_handler'; // @ts-ignore import tableVisTemplate from './table_vis.html'; @@ -28,9 +28,11 @@ import { TableOptions } from './components/table_vis_options_lazy'; import { getTableVisualizationControllerClass } from './vis_controller'; import { VIS_EVENT_TO_TRIGGER } from '../../../plugins/visualizations/public'; -export function getTableVisTypeDefinition(core: CoreSetup, context: PluginInitializerContext) { +export function getTableVisTypeDefinition( + core: CoreSetup, + context: PluginInitializerContext +): BaseVisTypeOptions { return { - type: 'table', name: 'table', title: i18n.translate('visTypeTable.tableVisTitle', { defaultMessage: 'Data Table', diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/vis_controller.ts index d87812b9f5d69..5e82796e66339 100644 --- a/src/plugins/vis_type_table/public/vis_controller.ts +++ b/src/plugins/vis_type_table/public/vis_controller.ts @@ -64,7 +64,7 @@ export function getTableVisualizationControllerClass( } } - async render(esResponse: object, visParams: VisParams) { + async render(esResponse: object, visParams: VisParams): Promise { getKibanaLegacy().loadFontAwesome(); await this.initLocalAngular(); diff --git a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap b/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap index 8e28be33515f7..debc7ab27c632 100644 --- a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap +++ b/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap @@ -2,25 +2,9 @@ exports[`interpreter/functions#tagcloud returns an object with the correct structure 1`] = ` Object { - "as": "visualization", + "as": "tagloud_vis", "type": "render", "value": Object { - "params": Object { - "listenOnChange": true, - }, - "visConfig": Object { - "maxFontSize": 72, - "metric": Object { - "accessor": 0, - "format": Object { - "id": "number", - }, - }, - "minFontSize": 18, - "orientation": "single", - "scale": "linear", - "showLabel": true, - }, "visData": Object { "columns": Array [ Object { @@ -35,6 +19,19 @@ Object { ], "type": "kibana_datatable", }, + "visParams": Object { + "maxFontSize": 72, + "metric": Object { + "accessor": 0, + "format": Object { + "id": "number", + }, + }, + "minFontSize": 18, + "orientation": "single", + "scale": "linear", + "showLabel": true, + }, "visType": "tagcloud", }, } diff --git a/src/plugins/vis_type_tagcloud/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_tagcloud/public/__snapshots__/to_ast.test.ts.snap new file mode 100644 index 0000000000000..d64bdfb1f46f9 --- /dev/null +++ b/src/plugins/vis_type_tagcloud/public/__snapshots__/to_ast.test.ts.snap @@ -0,0 +1,171 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`tagcloud vis toExpressionAst function should match snapshot params fulfilled 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + false, + ], + }, + "function": "esaggs", + "type": "function", + }, + Object { + "arguments": Object { + "bucket": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + 0, + ], + "format": Array [ + "terms", + ], + "formatParams": Array [ + "{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\"}", + ], + }, + "function": "visdimension", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "maxFontSize": Array [ + 15, + ], + "metric": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + 1, + ], + "format": Array [ + "number", + ], + }, + "function": "visdimension", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "minFontSize": Array [ + 5, + ], + "orientation": Array [ + "single", + ], + "scale": Array [ + "linear", + ], + "showLabel": Array [ + true, + ], + }, + "function": "tagcloud", + "type": "function", + }, + ], + "type": "expression", +} +`; + +exports[`tagcloud vis toExpressionAst function should match snapshot without params 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "aggConfigs": Array [ + "[]", + ], + "includeFormatHints": Array [ + false, + ], + "index": Array [ + "123", + ], + "metricsAtAllLevels": Array [ + false, + ], + "partialRows": Array [ + false, + ], + }, + "function": "esaggs", + "type": "function", + }, + Object { + "arguments": Object { + "bucket": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + 0, + ], + "format": Array [ + "terms", + ], + "formatParams": Array [ + "{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\"}", + ], + }, + "function": "visdimension", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "metric": Array [ + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + 1, + ], + "format": Array [ + "number", + ], + }, + "function": "visdimension", + "type": "function", + }, + ], + "type": "expression", + }, + ], + "showLabel": Array [ + false, + ], + }, + "function": "tagcloud", + "type": "function", + }, + ], + "type": "expression", +} +`; diff --git a/src/plugins/vis_type_tagcloud/public/_tag_cloud.scss b/src/plugins/vis_type_tagcloud/public/_tag_cloud.scss deleted file mode 100644 index 08901bebc0349..0000000000000 --- a/src/plugins/vis_type_tagcloud/public/_tag_cloud.scss +++ /dev/null @@ -1,14 +0,0 @@ -.tgcVis { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; -} - -.tgcVisLabel { - width: 100%; - text-align: center; - font-weight: $euiFontWeightBold; -} diff --git a/src/plugins/vis_type_tagcloud/public/components/label.js b/src/plugins/vis_type_tagcloud/public/components/label.js index 168ec4b270fde..88b3c2f851138 100644 --- a/src/plugins/vis_type_tagcloud/public/components/label.js +++ b/src/plugins/vis_type_tagcloud/public/components/label.js @@ -28,7 +28,7 @@ export class Label extends Component { render() { return (
{this.state.label} diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud.scss b/src/plugins/vis_type_tagcloud/public/components/tag_cloud.scss new file mode 100644 index 0000000000000..37867f1ed1c17 --- /dev/null +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud.scss @@ -0,0 +1,26 @@ +// Prefix all styles with "tgc" to avoid conflicts. +// Examples +// tgcChart +// tgcChart__legend +// tgcChart__legend--small +// tgcChart__legend-isLoading + +.tgcChart__container, .tgcChart__wrapper { + flex: 1 1 0; + display: flex; +} + +.tgcChart { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; +} + +.tgcChart__label { + width: 100%; + text-align: center; + font-weight: $euiFontWeightBold; +} diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx new file mode 100644 index 0000000000000..18a09ec9f4969 --- /dev/null +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_chart.tsx @@ -0,0 +1,84 @@ +/* + * 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, { useEffect, useMemo, useRef } from 'react'; +import { EuiResizeObserver } from '@elastic/eui'; +import { throttle } from 'lodash'; + +import { TagCloudVisDependencies } from '../plugin'; +import { TagCloudVisRenderValue } from '../tag_cloud_fn'; +// @ts-ignore +import { TagCloudVisualization } from './tag_cloud_visualization'; + +import './tag_cloud.scss'; + +type TagCloudChartProps = TagCloudVisDependencies & + TagCloudVisRenderValue & { + fireEvent: (event: any) => void; + renderComplete: () => void; + }; + +export const TagCloudChart = ({ + colors, + visData, + visParams, + fireEvent, + renderComplete, +}: TagCloudChartProps) => { + const chartDiv = useRef(null); + const visController = useRef(null); + + useEffect(() => { + visController.current = new TagCloudVisualization(chartDiv.current, colors, fireEvent); + return () => { + visController.current.destroy(); + visController.current = null; + }; + }, [colors, fireEvent]); + + useEffect(() => { + if (visController.current) { + visController.current.render(visData, visParams).then(renderComplete); + } + }, [visData, visParams, renderComplete]); + + const updateChartSize = useMemo( + () => + throttle(() => { + if (visController.current) { + visController.current.render().then(renderComplete); + } + }, 300), + [renderComplete] + ); + + return ( + + {(resizeRef) => ( +
+
+
+ )} + + ); +}; + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { TagCloudChart as default }; diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js index e43b3bdc747ab..5ec22d2c6a4d9 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js @@ -32,126 +32,138 @@ import d3 from 'd3'; const MAX_TAG_COUNT = 200; -export function createTagCloudVisualization({ colors }) { - const colorScale = d3.scale.ordinal().range(colors.seedColors); - return class TagCloudVisualization { - constructor(node, vis) { - this._containerNode = node; - - const cloudRelativeContainer = document.createElement('div'); - cloudRelativeContainer.classList.add('tgcVis'); - cloudRelativeContainer.setAttribute('style', 'position: relative'); - const cloudContainer = document.createElement('div'); - cloudContainer.classList.add('tgcVis'); - cloudContainer.setAttribute('data-test-subj', 'tagCloudVisualization'); - this._containerNode.classList.add('visChart--vertical'); - cloudRelativeContainer.appendChild(cloudContainer); - this._containerNode.appendChild(cloudRelativeContainer); - - this._vis = vis; - this._truncated = false; - this._tagCloud = new TagCloud(cloudContainer, colorScale); - this._tagCloud.on('select', (event) => { - if (!this._visParams.bucket) { - return; - } - this._vis.API.events.filter({ - table: event.meta.data, - column: 0, - row: event.meta.rowIndex, - }); - }); - this._renderComplete$ = Rx.fromEvent(this._tagCloud, 'renderComplete'); - - this._feedbackNode = document.createElement('div'); - this._containerNode.appendChild(this._feedbackNode); - this._feedbackMessage = React.createRef(); - render( - - - , - this._feedbackNode - ); - - this._labelNode = document.createElement('div'); - this._containerNode.appendChild(this._labelNode); - this._label = React.createRef(); - render(
`; @@ -48,28 +58,38 @@ exports[`Storyshots arguments/AxisConfig/components simple template 1`] = ` } >
- + className="euiSwitch__body" + > + + + + +
`; diff --git a/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts b/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts index e8cbddc5c1102..257e251fe2bc2 100644 --- a/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts +++ b/x-pack/plugins/canvas/i18n/functions/dict/saved_visualization.ts @@ -29,5 +29,8 @@ export const help: FunctionHelp> = { defaultMessage: `Specifies the option to hide the legend`, } ), + title: i18n.translate('xpack.canvas.functions.savedVisualization.args.titleHelpText', { + defaultMessage: `The title for the visualization object`, + }), }, }; diff --git a/x-pack/plugins/canvas/i18n/templates/template_strings.test.ts b/x-pack/plugins/canvas/i18n/templates/template_strings.test.ts index 4185ad00e7ed7..f27b3554cacd6 100644 --- a/x-pack/plugins/canvas/i18n/templates/template_strings.test.ts +++ b/x-pack/plugins/canvas/i18n/templates/template_strings.test.ts @@ -5,13 +5,13 @@ */ import { getTemplateStrings } from './template_strings'; -import { templates } from '../../server/templates'; // eslint-disable-line +import { loadTemplates } from '../../server/templates'; // eslint-disable-line import { TagStrings } from '../tags'; describe('TemplateStrings', () => { const templateStrings = getTemplateStrings(); - const templateNames = templates.map((template) => template.name); + const templateNames = loadTemplates().map((template) => template.name); const stringKeys = Object.keys(templateStrings); test('All template names should exist in the strings definition', () => { @@ -39,7 +39,7 @@ describe('TemplateStrings', () => { test('All templates should have tags that are defined', () => { const tagNames = Object.keys(TagStrings); - templates.forEach((template) => { + loadTemplates().forEach((template) => { template.tags.forEach((tagName: string) => expect(tagNames).toContain(tagName)); }); }); diff --git a/x-pack/plugins/canvas/public/components/datatable/datatable.scss b/x-pack/plugins/canvas/public/components/datatable/datatable.scss index bd11bff18e091..8e36de3b84423 100644 --- a/x-pack/plugins/canvas/public/components/datatable/datatable.scss +++ b/x-pack/plugins/canvas/public/components/datatable/datatable.scss @@ -4,7 +4,6 @@ display: flex; flex-direction: column; justify-content: space-between; - font-size: $euiFontSizeS; .canvasDataTable__tableWrapper { @include euiScrollBar; @@ -33,7 +32,6 @@ .canvasDataTable__th, .canvasDataTable__td { - text-align: left; padding: $euiSizeS $euiSizeXS; border-bottom: $euiBorderThin; } diff --git a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot index 99d5dc3c115be..5c17eb2b68137 100644 --- a/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/expression_input/__stories__/__snapshots__/expression_input.stories.storyshot @@ -16,18 +16,7 @@ exports[`Storyshots components/ExpressionInput default 1`] = ` id="generated-id" onBlur={[Function]} onFocus={[Function]} - > -
-
-
+ />
diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/simple_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/simple_template.stories.storyshot index 401e7bf1e937d..495bf5262476c 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/simple_template.stories.storyshot +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/container_style/__stories__/__snapshots__/simple_template.stories.storyshot @@ -13,50 +13,60 @@ exports[`Storyshots arguments/ContainerStyle simple 1`] = `
- + /> +
+ +
@@ -76,50 +86,60 @@ exports[`Storyshots arguments/ContainerStyle/components simple template 1`] = `
- + /> +
+ +
diff --git a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/simple_template.stories.storyshot b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/simple_template.stories.storyshot index f8583d7cd0dc0..b242d90cd7361 100644 --- a/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/simple_template.stories.storyshot +++ b/x-pack/plugins/canvas/public/expression_types/arg_types/series_style/__stories__/__snapshots__/simple_template.stories.storyshot @@ -11,31 +11,41 @@ exports[`Storyshots arguments/SeriesStyle simple 1`] = ` } >
- Color +
+ Color +
-
-
- + +
@@ -53,31 +63,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: defaults 1`] = ` } >
- Color +
+ Color +
-
-
- + +
@@ -95,31 +115,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: no labels 1`] = ` } >
- Color +
+ Color +
-
-
- + +
@@ -137,51 +167,61 @@ exports[`Storyshots arguments/SeriesStyle/components simple: no series 1`] = ` } >
- Color +
+ Color +
-
-
- + +
-
-
- -
- + +
+ +
@@ -198,31 +238,41 @@ exports[`Storyshots arguments/SeriesStyle/components simple: with series 1`] = ` } >
- Color +
+ Color +
-
-
- + +
diff --git a/x-pack/plugins/canvas/scripts/storybook_new.js b/x-pack/plugins/canvas/scripts/storybook_new.js deleted file mode 100644 index 4871898b73a45..0000000000000 --- a/x-pack/plugins/canvas/scripts/storybook_new.js +++ /dev/null @@ -1,13 +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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'canvas', - storyGlobs: [join(__dirname, '..', '**', '*.stories.tsx')], -}); diff --git a/x-pack/plugins/canvas/server/collectors/collector.ts b/x-pack/plugins/canvas/server/collectors/collector.ts index eb650ca5ad152..39a8262a5deec 100644 --- a/x-pack/plugins/canvas/server/collectors/collector.ts +++ b/x-pack/plugins/canvas/server/collectors/collector.ts @@ -5,11 +5,16 @@ */ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { LegacyAPICaller } from 'kibana/server'; import { TelemetryCollector } from '../../types'; -import { workpadCollector } from './workpad_collector'; -import { customElementCollector } from './custom_element_collector'; +import { workpadCollector, workpadSchema, WorkpadTelemetry } from './workpad_collector'; +import { + customElementCollector, + CustomElementTelemetry, + customElementSchema, +} from './custom_element_collector'; + +type CanvasUsage = WorkpadTelemetry & CustomElementTelemetry; const collectors: TelemetryCollector[] = [workpadCollector, customElementCollector]; @@ -29,18 +34,19 @@ export function registerCanvasUsageCollector( return; } - const canvasCollector = usageCollection.makeUsageCollector({ + const canvasCollector = usageCollection.makeUsageCollector({ type: 'canvas', isReady: () => true, - fetch: async (callCluster: LegacyAPICaller) => { + fetch: async (callCluster) => { const collectorResults = await Promise.all( collectors.map((collector) => collector(kibanaIndex, callCluster)) ); return collectorResults.reduce((reduction, usage) => { return { ...reduction, ...usage }; - }, {}); + }, {}) as CanvasUsage; // We need the casting because `TelemetryCollector` claims it returns `Record` }, + schema: { ...workpadSchema, ...customElementSchema }, }); usageCollection.registerCollector(canvasCollector); diff --git a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts index 7b39e8b83b045..d3ed1e17785ee 100644 --- a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts @@ -6,6 +6,7 @@ import { SearchParams } from 'elasticsearch'; import { get } from 'lodash'; +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { collectFns } from './collector_helpers'; import { TelemetryCollector, @@ -19,7 +20,7 @@ interface CustomElementSearch { [CUSTOM_ELEMENT_TYPE]: TelemetryCustomElementDocument; } -interface CustomElementTelemetry { +export interface CustomElementTelemetry { custom_elements?: { count: number; elements: { @@ -31,6 +32,18 @@ interface CustomElementTelemetry { }; } +export const customElementSchema: MakeSchemaFrom = { + custom_elements: { + count: { type: 'long' }, + elements: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + functions_in_use: { type: 'array', items: { type: 'keyword' } }, + }, +}; + function isCustomElement(maybeCustomElement: any): maybeCustomElement is TelemetryCustomElement { return ( maybeCustomElement !== null && diff --git a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts index 9fa39c580962d..0479411528802 100644 --- a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts @@ -6,6 +6,7 @@ import { SearchParams } from 'elasticsearch'; import { sum as arraySum, min as arrayMin, max as arrayMax, get } from 'lodash'; +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { CANVAS_TYPE } from '../../common/lib/constants'; import { collectFns } from './collector_helpers'; import { TelemetryCollector, CanvasWorkpad } from '../../types'; @@ -15,7 +16,7 @@ interface WorkpadSearch { [CANVAS_TYPE]: CanvasWorkpad; } -interface WorkpadTelemetry { +export interface WorkpadTelemetry { workpads?: { total: number; }; @@ -54,6 +55,43 @@ interface WorkpadTelemetry { }; } +export const workpadSchema: MakeSchemaFrom = { + workpads: { total: { type: 'long' } }, + pages: { + total: { type: 'long' }, + per_workpad: { + avg: { type: 'float' }, + min: { type: 'long' }, + max: { type: 'long' }, + }, + }, + elements: { + total: { type: 'long' }, + per_page: { + avg: { type: 'float' }, + min: { type: 'long' }, + max: { type: 'long' }, + }, + }, + functions: { + total: { type: 'long' }, + in_use: { type: 'array', items: { type: 'keyword' } }, + per_element: { + avg: { type: 'float' }, + min: { type: 'long' }, + max: { type: 'long' }, + }, + }, + variables: { + total: { type: 'long' }, + per_workpad: { + avg: { type: 'float' }, + min: { type: 'long' }, + max: { type: 'long' }, + }, + }, +}; + /** Gather statistic about the given workpads @param workpadDocs a collection of workpad documents diff --git a/x-pack/plugins/canvas/server/templates/index.ts b/x-pack/plugins/canvas/server/templates/index.ts index c2723fbc87e17..800ac10fb9cd4 100644 --- a/x-pack/plugins/canvas/server/templates/index.ts +++ b/x-pack/plugins/canvas/server/templates/index.ts @@ -5,15 +5,24 @@ */ import { SavedObjectsRepository } from 'src/core/server'; -import { pitch } from './pitch_presentation'; -import { status } from './status_report'; -import { summary } from './summary_report'; -import { dark } from './theme_dark'; -import { light } from './theme_light'; import { TEMPLATE_TYPE } from '../../common/lib/constants'; -export const templates = [status, summary, dark, light, pitch]; +// only load templates when requested to reduce require() cost on startup +export function loadTemplates() { + return [ + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./pitch_presentation').pitch, + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./status_report').status, + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./summary_report').summary, + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./theme_dark').dark, + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('./theme_light').light, + ]; +} export async function initializeTemplates( client: Pick @@ -26,7 +35,7 @@ export async function initializeTemplates( // So, rather than doing a bulk create of templates, we're going to fire off individual // creates and catch and throw-away any errors that happen. // Once packages are ready, we should probably move that pitch that is so large to a package - for (const template of templates) { + for (const template of loadTemplates()) { client.create(TEMPLATE_TYPE, template, { id: template.id }).catch((err) => undefined); } } diff --git a/x-pack/plugins/canvas/storybook/addon/src/register.tsx b/x-pack/plugins/canvas/storybook/addon/src/register.tsx index 4934438789b94..7fcb8832b1ed8 100644 --- a/x-pack/plugins/canvas/storybook/addon/src/register.tsx +++ b/x-pack/plugins/canvas/storybook/addon/src/register.tsx @@ -24,7 +24,7 @@ addons.register(ADDON_ID, (api) => { type: types.PANEL, render: ({ active, key }) => { return ( - + ); diff --git a/x-pack/plugins/canvas/storybook/decorators/index.ts b/x-pack/plugins/canvas/storybook/decorators/index.ts index 8cd716cf7e3f1..c518e7e0daaa3 100644 --- a/x-pack/plugins/canvas/storybook/decorators/index.ts +++ b/x-pack/plugins/canvas/storybook/decorators/index.ts @@ -5,14 +5,6 @@ */ import { addDecorator } from '@storybook/react'; -// @ts-expect-error -import { withInfo } from '@storybook/addon-info'; -import { Provider as ReduxProvider } from 'react-redux'; - -import { ServicesProvider } from '../../public/services'; -import { RouterContext } from '../../public/components/router'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; - import { routerContextDecorator } from './router_decorator'; import { kibanaContextDecorator } from './kibana_decorator'; import { servicesContextDecorator } from './services_decorator'; @@ -23,22 +15,6 @@ export const addDecorators = () => { if (process.env.NODE_ENV === 'test') { // eslint-disable-next-line @typescript-eslint/no-var-requires require('babel-plugin-require-context-hook/register')(); - } else { - // Customize the info for each story. - addDecorator( - withInfo({ - inline: true, - styles: { - infoBody: { - margin: 20, - }, - infoStory: { - margin: '40px 60px', - }, - }, - propTablesExclude: [ReduxProvider, ServicesProvider, RouterContext, KibanaContextProvider], - }) - ); } addDecorator(kibanaContextDecorator); diff --git a/x-pack/plugins/canvas/storybook/main.ts b/x-pack/plugins/canvas/storybook/main.ts index ad6d10f9bc75f..29952d22e44df 100644 --- a/x-pack/plugins/canvas/storybook/main.ts +++ b/x-pack/plugins/canvas/storybook/main.ts @@ -4,11 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { existsSync } = require('fs'); +const { join } = require('path'); + +// Check for DLL +if (!existsSync(join(__dirname, '../../../../built_assets/canvas_storybook_dll/manifest.json'))) { + // eslint-disable-next-line no-console + console.error( + 'No DLL found. Run `node scripts/storybook --dll` from the Canvas plugin directory.' + ); + process.exit(1); +} + module.exports = { stories: ['../**/*.stories.tsx'], - addons: [ - '@storybook/addon-actions', - '@storybook/addon-knobs', - './storybook/addon/target/register', - ], + addons: ['@storybook/addon-actions', '@storybook/addon-knobs', './addon/target/register'], }; diff --git a/x-pack/plugins/canvas/storybook/storyshots.test.tsx b/x-pack/plugins/canvas/storybook/storyshots.test.tsx index 85ec7baf18c62..44420159c328a 100644 --- a/x-pack/plugins/canvas/storybook/storyshots.test.tsx +++ b/x-pack/plugins/canvas/storybook/storyshots.test.tsx @@ -11,7 +11,7 @@ import moment from 'moment'; import 'moment-timezone'; import ReactDOM from 'react-dom'; -import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots'; +// import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots'; // @ts-expect-error untyped library import styleSheetSerializer from 'jest-styled-components/src/styleSheetSerializer'; import { addSerializer } from 'jest-specific-snapshot'; @@ -104,9 +104,12 @@ if (!fs.existsSync(cssDir)) { addSerializer(styleSheetSerializer); // Initialize Storyshots and build the Jest Snapshots -initStoryshots({ - configPath: path.resolve(__dirname, './../storybook'), - test: multiSnapshotWithOptions({}), - // Don't snapshot tests that start with 'redux' - storyNameRegex: /^((?!.*?redux).)*$/, -}); +// Commenting this out until after #75357 is merged and Jest gets updated. +// initStoryshots({ +// configPath: path.resolve(__dirname, './../storybook'), +// test: multiSnapshotWithOptions({}), +// // Don't snapshot tests that start with 'redux' +// storyNameRegex: /^((?!.*?redux).)*$/, +// }); + +test.todo('Storyshots'); diff --git a/x-pack/plugins/dashboard_enhanced/.storybook/main.js b/x-pack/plugins/dashboard_enhanced/.storybook/main.js new file mode 100644 index 0000000000000..1818aa44a9399 --- /dev/null +++ b/x-pack/plugins/dashboard_enhanced/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.story.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.stories.tsx similarity index 100% rename from x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.story.tsx rename to x-pack/plugins/dashboard_enhanced/public/services/drilldowns/dashboard_to_dashboard_drilldown/components/dashboard_drilldown_config/dashboard_drilldown_config.stories.tsx diff --git a/x-pack/plugins/dashboard_enhanced/scripts/storybook.js b/x-pack/plugins/dashboard_enhanced/scripts/storybook.js deleted file mode 100644 index 5d95c56c31e3b..0000000000000 --- a/x-pack/plugins/dashboard_enhanced/scripts/storybook.js +++ /dev/null @@ -1,13 +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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'dashboard_enhanced', - storyGlobs: [join(__dirname, '..', 'public', '**', '*.story.tsx')], -}); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts index af2fc85602541..6e34e4c1964c5 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts @@ -8,6 +8,7 @@ import { coreMock } from '../../../../../src/core/public/mocks'; import { EnhancedSearchInterceptor } from './search_interceptor'; import { CoreSetup, CoreStart } from 'kibana/public'; import { AbortError, UI_SETTINGS } from '../../../../../src/plugins/data/common'; +import { SearchTimeoutError } from 'src/plugins/data/public'; const timeTravel = (msToRun = 0) => { jest.advanceTimersByTime(msToRun); @@ -265,7 +266,7 @@ describe('EnhancedSearchInterceptor', () => { await timeTravel(1000); expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); + expect(error.mock.calls[0][0]).toBeInstanceOf(SearchTimeoutError); expect(mockCoreSetup.http.fetch).toHaveBeenCalled(); expect(mockCoreSetup.http.delete).not.toHaveBeenCalled(); }); @@ -305,7 +306,7 @@ describe('EnhancedSearchInterceptor', () => { await timeTravel(1000); expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); + expect(error.mock.calls[0][0]).toBeInstanceOf(SearchTimeoutError); expect(mockCoreSetup.http.fetch).toHaveBeenCalledTimes(2); expect(mockCoreSetup.http.delete).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index f7ae9fc6d0f91..cca87c85e326c 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -5,15 +5,15 @@ */ import { throwError, EMPTY, timer, from, Subscription } from 'rxjs'; -import { mergeMap, expand, takeUntil, finalize, tap } from 'rxjs/operators'; -import { debounce } from 'lodash'; -import { i18n } from '@kbn/i18n'; +import { mergeMap, expand, takeUntil, finalize, catchError } from 'rxjs/operators'; import { SearchInterceptor, SearchInterceptorDeps, UI_SETTINGS, } from '../../../../../src/plugins/data/public'; +import { isErrorResponse, isCompleteResponse } from '../../../../../src/plugins/data/public'; import { AbortError, toPromise } from '../../../../../src/plugins/data/common'; +import { TimeoutErrorMode } from '../../../../../src/plugins/data/public'; import { IAsyncSearchOptions } from '.'; import { IAsyncSearchRequest, ENHANCED_ES_SEARCH_STRATEGY } from '../../common'; @@ -39,6 +39,12 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { this.uiSettingsSub.unsubscribe(); } + protected getTimeoutMode() { + return this.application.capabilities.advancedSettings?.save + ? TimeoutErrorMode.CHANGE + : TimeoutErrorMode.CONTACT; + } + /** * Abort our `AbortController`, which in turn aborts any intercepted searches. */ @@ -54,7 +60,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { ) { let { id } = request; - const { combinedSignal, cleanup } = this.setupAbortSignal({ + const { combinedSignal, timeoutSignal, cleanup } = this.setupAbortSignal({ abortSignal: options.abortSignal, timeout: this.searchTimeout, }); @@ -66,12 +72,12 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { return this.runSearch(request, combinedSignal, strategy).pipe( expand((response) => { // If the response indicates of an error, stop polling and complete the observable - if (!response || (!response.isRunning && response.isPartial)) { + if (isErrorResponse(response)) { return throwError(new AbortError()); } // If the response indicates it is complete, stop polling and complete the observable - if (!response.isRunning) { + if (isCompleteResponse(response)) { return EMPTY; } @@ -85,15 +91,14 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { ); }), takeUntil(aborted$), - tap({ - error: () => { - // If we haven't received the response to the initial request, including the ID, then - // we don't need to send a follow-up request to delete this search. Otherwise, we - // send the follow-up request to delete this search, then throw an abort error. - if (id !== undefined) { - this.deps.http.delete(`/internal/search/${strategy}/${id}`); - } - }, + catchError((e: any) => { + // If we haven't received the response to the initial request, including the ID, then + // we don't need to send a follow-up request to delete this search. Otherwise, we + // send the follow-up request to delete this search, then throw an abort error. + if (id !== undefined) { + this.deps.http.delete(`/internal/search/${strategy}/${id}`); + } + return throwError(this.handleSearchError(e, request, timeoutSignal, options?.abortSignal)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); @@ -101,28 +106,4 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { }) ); } - - // Right now we are debouncing but we will hook this up with background sessions to show only one - // error notification per session. - protected showTimeoutError = debounce( - (e: Error) => { - const message = this.application.capabilities.advancedSettings?.save - ? i18n.translate('xpack.data.search.timeoutIncreaseSetting', { - defaultMessage: - 'One or more queries timed out. Increase run time with the search.timeout advanced setting.', - }) - : i18n.translate('xpack.data.search.timeoutContactAdmin', { - defaultMessage: - 'One or more queries timed out. Contact your system administrator to increase the run time.', - }); - this.deps.toasts.addError(e, { - title: 'Timed out', - toastMessage: message, - }); - }, - 60000, - { - leading: true, - } - ); } diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 72ea1f096e8fb..f3cf67a487a68 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -19,7 +19,11 @@ import { shimHitsTotal, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; -import { ISearchOptions, IEsSearchResponse } from '../../../../../src/plugins/data/common/search'; +import { + ISearchOptions, + IEsSearchResponse, + isCompleteResponse, +} from '../../../../../src/plugins/data/common/search'; function isEnhancedEsSearchResponse(response: any): response is IEsSearchResponse { return response.hasOwnProperty('isPartial') && response.hasOwnProperty('isRunning'); @@ -48,8 +52,7 @@ export const enhancedEsSearchStrategyProvider = ( usage && isAsync && isEnhancedEsSearchResponse(response) && - !response.isRunning && - !response.isPartial + isCompleteResponse(response) ) { usage.trackSuccess(response.rawResponse.took); } diff --git a/x-pack/plugins/drilldowns/url_drilldown/README.md b/x-pack/plugins/drilldowns/url_drilldown/README.md new file mode 100644 index 0000000000000..8eedc44ca35ae --- /dev/null +++ b/x-pack/plugins/drilldowns/url_drilldown/README.md @@ -0,0 +1,26 @@ +## URL drilldown + +> NOTE: This plugin contains implementation of URL drilldown. For drilldowns infrastructure code refer to `ui_actions_enhanced` plugin. + +Url drilldown allows navigating to external URL or to internal kibana URL. +By using variables in url template result url can be dynamic and depend on user's interaction. + +URL drilldown has 3 sources for variables: + +1. Global static variables like, for example, `kibanaUrl`. Such variables won’t change depending on a place where url drilldown is used. +2. Context variables are dynamic and different depending on where drilldown is created and used. +3. Event variables depend on a trigger context. These variables are dynamically extracted from the action context when drilldown is executed. + +Difference between `event` and `context` variables, is that real `context` variables are available during drilldown creation (e.g. embeddable panel), +but `event` variables mapped from trigger context. Since there is no trigger context during drilldown creation, we have to provide some _mock_ variables for validating and previewing the URL. + +In current implementation url drilldown has to be used inside the embeddable and with `ValueClickTrigger` or `RangeSelectTrigger`. + +* `context` variables extracted from `embeddable` +* `event` variables extracted from `trigger` context + +In future this basic url drilldown implementation would allow injecting more variables into `context` (e.g. `dashboard` app specific variables) and would allow providing support for new trigger types from outside. +This extensibility improvements are tracked here: https://github.com/elastic/kibana/issues/55324 + +In case a solution app has a use case for url drilldown that has to be different from current basic implementation and +just extending variables list is not enough, then recommendation is to create own custom url drilldown and reuse building blocks from `ui_actions_enhanced`. \ No newline at end of file diff --git a/x-pack/plugins/drilldowns/url_drilldown/kibana.json b/x-pack/plugins/drilldowns/url_drilldown/kibana.json new file mode 100644 index 0000000000000..9bdd13fbfea26 --- /dev/null +++ b/x-pack/plugins/drilldowns/url_drilldown/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "urlDrilldown", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["embeddable", "uiActions", "uiActionsEnhanced"], + "requiredBundles": ["kibanaUtils", "kibanaReact"] +} diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/index.ts b/x-pack/plugins/drilldowns/url_drilldown/public/index.ts new file mode 100644 index 0000000000000..b040ef625bc1f --- /dev/null +++ b/x-pack/plugins/drilldowns/url_drilldown/public/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 { UrlDrilldownPlugin } from './plugin'; + +export function plugin(context: PluginInitializerContext) { + return new UrlDrilldownPlugin(context); +} diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/i18n.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/i18n.ts new file mode 100644 index 0000000000000..7e91c6b849035 --- /dev/null +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/i18n.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 { i18n } from '@kbn/i18n'; + +export const txtUrlDrilldownDisplayName = i18n.translate('xpack.urlDrilldown.DisplayName', { + defaultMessage: 'Go to URL', +}); diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/index.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/index.ts similarity index 100% rename from x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/index.ts rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/index.ts diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts similarity index 100% rename from x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx similarity index 100% rename from x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.tsx diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown_scope.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts similarity index 100% rename from x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown_scope.test.ts rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown_scope.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts similarity index 100% rename from x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown_scope.ts rename to x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/plugin.ts b/x-pack/plugins/drilldowns/url_drilldown/public/plugin.ts new file mode 100644 index 0000000000000..82ce7a129f497 --- /dev/null +++ b/x-pack/plugins/drilldowns/url_drilldown/public/plugin.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. + */ + +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; +import { EmbeddableSetup, EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { + AdvancedUiActionsSetup, + AdvancedUiActionsStart, + urlDrilldownGlobalScopeProvider, +} from '../../../ui_actions_enhanced/public'; +import { UrlDrilldown } from './lib'; +import { createStartServicesGetter } from '../../../../../src/plugins/kibana_utils/public'; + +export interface SetupDependencies { + embeddable: EmbeddableSetup; + uiActionsEnhanced: AdvancedUiActionsSetup; +} + +export interface StartDependencies { + embeddable: EmbeddableStart; + uiActionsEnhanced: AdvancedUiActionsStart; +} + +// eslint-disable-next-line +export interface SetupContract {} + +// eslint-disable-next-line +export interface StartContract {} + +export class UrlDrilldownPlugin + implements Plugin { + constructor(protected readonly context: PluginInitializerContext) {} + + public setup(core: CoreSetup, plugins: SetupDependencies): SetupContract { + const startServices = createStartServicesGetter(core.getStartServices); + plugins.uiActionsEnhanced.registerDrilldown( + new UrlDrilldown({ + getGlobalScope: urlDrilldownGlobalScopeProvider({ core }), + navigateToUrl: (url: string) => + core.getStartServices().then(([{ application }]) => application.navigateToUrl(url)), + getSyntaxHelpDocsLink: () => + startServices().core.docLinks.links.dashboard.urlDrilldownTemplateSyntax, + getVariablesHelpDocsLink: () => + startServices().core.docLinks.links.dashboard.urlDrilldownVariables, + }) + ); + + return {}; + } + + public start(core: CoreStart, plugins: StartDependencies): StartContract { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/embeddable_enhanced/kibana.json b/x-pack/plugins/embeddable_enhanced/kibana.json index acada946fe0d1..8d49e3e26eb7b 100644 --- a/x-pack/plugins/embeddable_enhanced/kibana.json +++ b/x-pack/plugins/embeddable_enhanced/kibana.json @@ -3,6 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": ["embeddable", "kibanaReact", "uiActions", "uiActionsEnhanced"], - "requiredBundles": ["kibanaUtils"] + "requiredPlugins": ["embeddable", "kibanaReact", "uiActions", "uiActionsEnhanced"] } diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/index.ts b/x-pack/plugins/embeddable_enhanced/public/drilldowns/index.ts deleted file mode 100644 index a8d5a179dbac1..0000000000000 --- a/x-pack/plugins/embeddable_enhanced/public/drilldowns/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export * from './url_drilldown'; diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/README.md b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/README.md deleted file mode 100644 index 996723ccb914d..0000000000000 --- a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Basic url drilldown implementation - -Url drilldown allows navigating to external URL or to internal kibana URL. -By using variables in url template result url can be dynamic and depend on user's interaction. - -URL drilldown has 3 sources for variables: - -- Global static variables like, for example, `kibanaUrl`. Such variables won’t change depending on a place where url drilldown is used. -- Context variables are dynamic and different depending on where drilldown is created and used. -- Event variables depend on a trigger context. These variables are dynamically extracted from the action context when drilldown is executed. - -Difference between `event` and `context` variables, is that real `context` variables are available during drilldown creation (e.g. embeddable panel), -but `event` variables mapped from trigger context. Since there is no trigger context during drilldown creation, we have to provide some _mock_ variables for validating and previewing the URL. - -In current implementation url drilldown has to be used inside the embeddable and with `ValueClickTrigger` or `RangeSelectTrigger`. - -- `context` variables extracted from `embeddable` -- `event` variables extracted from `trigger` context - -In future this basic url drilldown implementation would allow injecting more variables into `context` (e.g. `dashboard` app specific variables) and would allow providing support for new trigger types from outside. -This extensibility improvements are tracked here: https://github.com/elastic/kibana/issues/55324 - -In case a solution app has a use case for url drilldown that has to be different from current basic implementation and -just extending variables list is not enough, then recommendation is to create own custom url drilldown and reuse building blocks from `ui_actions_enhanced`. diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/i18n.ts b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/i18n.ts deleted file mode 100644 index 748f6f4cecedd..0000000000000 --- a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/i18n.ts +++ /dev/null @@ -1,14 +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 { i18n } from '@kbn/i18n'; - -export const txtUrlDrilldownDisplayName = i18n.translate( - 'xpack.embeddableEnhanced.drilldowns.urlDrilldownDisplayName', - { - defaultMessage: 'Go to URL', - } -); diff --git a/x-pack/plugins/embeddable_enhanced/public/plugin.ts b/x-pack/plugins/embeddable_enhanced/public/plugin.ts index 2138a372523b7..5d5ad852839d4 100644 --- a/x-pack/plugins/embeddable_enhanced/public/plugin.ts +++ b/x-pack/plugins/embeddable_enhanced/public/plugin.ts @@ -28,11 +28,8 @@ import { UiActionsEnhancedDynamicActionManager as DynamicActionManager, AdvancedUiActionsSetup, AdvancedUiActionsStart, - urlDrilldownGlobalScopeProvider, } from '../../ui_actions_enhanced/public'; import { PanelNotificationsAction, ACTION_PANEL_NOTIFICATIONS } from './actions'; -import { UrlDrilldown } from './drilldowns'; -import { createStartServicesGetter } from '../../../../src/plugins/kibana_utils/public'; declare module '../../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { @@ -64,23 +61,10 @@ export class EmbeddableEnhancedPlugin public setup(core: CoreSetup, plugins: SetupDependencies): SetupContract { this.setCustomEmbeddableFactoryProvider(plugins); - const startServices = createStartServicesGetter(core.getStartServices); const panelNotificationAction = new PanelNotificationsAction(); plugins.uiActionsEnhanced.registerAction(panelNotificationAction); plugins.uiActionsEnhanced.attachAction(PANEL_NOTIFICATION_TRIGGER, panelNotificationAction.id); - plugins.uiActionsEnhanced.registerDrilldown( - new UrlDrilldown({ - getGlobalScope: urlDrilldownGlobalScopeProvider({ core }), - navigateToUrl: (url: string) => - core.getStartServices().then(([{ application }]) => application.navigateToUrl(url)), - getSyntaxHelpDocsLink: () => - startServices().core.docLinks.links.dashboard.urlDrilldownTemplateSyntax, - getVariablesHelpDocsLink: () => - startServices().core.docLinks.links.dashboard.urlDrilldownVariables, - }) - ); - return {}; } diff --git a/x-pack/plugins/enterprise_search/README.md b/x-pack/plugins/enterprise_search/README.md index ba14be5564be1..711c7c7b065d2 100644 --- a/x-pack/plugins/enterprise_search/README.md +++ b/x-pack/plugins/enterprise_search/README.md @@ -17,6 +17,10 @@ This plugin's goal is to provide a Kibana user interface to the Enterprise Searc Enterprise Search uses [Kea.js](https://github.com/keajs/kea) to manage our React/Redux state for us. Kea state is handled in our `*Logic` files and exposes [values](https://kea.js.org/docs/guide/concepts#values) and [actions](https://kea.js.org/docs/guide/concepts#actions) for our components to get and set state with. +#### Advanced Kea usage + +For the most part, we stick to the functionality described in Kea's [core concepts](https://kea.js.org/docs/guide/concepts). However, in some files, we also take advantage of [props](https://kea.js.org/docs/guide/additional#props) and [events](https://kea.js.org/docs/guide/additional#events), as well as [manually mounting](https://kea.js.org/docs/guide/advanced#mounting-and-unmounting) some shared logic files on plugin init outside of React. + #### Debugging Kea To debug Kea state in-browser, Kea recommends [Redux Devtools](https://kea.js.org/docs/guide/debugging). To facilitate debugging, we use the [path](https://kea.js.org/docs/guide/debugging/#setting-the-path-manually) key with `snake_case`d paths. The path key should always end with the logic filename (e.g. `['enterprise_search', 'some_logic']`) to make it easy for devs to quickly find/jump to files via IDE tooling. diff --git a/x-pack/plugins/enterprise_search/common/types/index.ts b/x-pack/plugins/enterprise_search/common/types/index.ts index d5774adc0d516..1006d39138759 100644 --- a/x-pack/plugins/enterprise_search/common/types/index.ts +++ b/x-pack/plugins/enterprise_search/common/types/index.ts @@ -30,3 +30,14 @@ export interface IConfiguredLimits { appSearch: IAppSearchConfiguredLimits; workplaceSearch: IWorkplaceSearchConfiguredLimits; } + +export interface IMetaPage { + current: number; + size: number; + total_pages: number; + total_results: number; +} + +export interface IMeta { + page: IMetaPage; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/enterprise_search_url.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/enterprise_search_url.mock.ts new file mode 100644 index 0000000000000..47660d0a31720 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/enterprise_search_url.mock.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 { externalUrl } from '../shared/enterprise_search_url'; + +externalUrl.enterpriseSearchUrl = 'http://localhost:3002'; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts new file mode 100644 index 0000000000000..a610ea0238ac0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/flash_messages_logic.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 mockFlashMessagesValues = { + messages: [], + queuedMessages: [], +}; + +export const mockFlashMessagesActions = { + setFlashMessages: jest.fn(), + clearFlashMessages: jest.fn(), + setQueuedMessages: jest.fn(), + clearQueuedMessages: jest.fn(), +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.ts new file mode 100644 index 0000000000000..e77863c70c23a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/http_logic.mock.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 { httpServiceMock } from 'src/core/public/mocks'; + +export const mockHttpValues = { + http: httpServiceMock.createSetupContract(), + errorConnecting: false, + readOnlyMode: false, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts index e999d40a3f8e6..22bb556e6d602 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/index.ts @@ -5,13 +5,13 @@ */ export { mockHistory, mockLocation } from './react_router_history.mock'; -export { mockKibanaContext } from './kibana_context.mock'; -export { mockLicenseContext } from './license_context.mock'; -export { - mountWithContext, - mountWithKibanaContext, - mountWithAsyncContext, -} from './mount_with_context.mock'; -export { shallowWithIntl } from './shallow_with_i18n.mock'; +export { mockKibanaValues } from './kibana_logic.mock'; +export { mockLicensingValues } from './licensing_logic.mock'; +export { mockHttpValues } from './http_logic.mock'; +export { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock'; +export { mockAllValues, mockAllActions, setMockValues, setMockActions } from './kea.mock'; -// Note: shallow_usecontext must be imported directly as a file +export { mountAsync } from './mount_async.mock'; +export { mountWithIntl } from './mount_with_i18n.mock'; +export { shallowWithIntl } from './shallow_with_i18n.mock'; +// Note: shallow_useeffect must be imported directly as a file diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts index 5049e9da21ce9..ffbbaaf794bcc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea.mock.ts @@ -4,21 +4,55 @@ * you may not use this file except in compliance with the Elastic License. */ +/** + * Combine all shared mock values/actions into a single obj + * + * NOTE: These variable names MUST start with 'mock*' in order for + * Jest to accept its use within a jest.mock() + */ +import { mockKibanaValues } from './kibana_logic.mock'; +import { mockLicensingValues } from './licensing_logic.mock'; +import { mockHttpValues } from './http_logic.mock'; +import { mockFlashMessagesValues, mockFlashMessagesActions } from './flash_messages_logic.mock'; + +export const mockAllValues = { + ...mockKibanaValues, + ...mockLicensingValues, + ...mockHttpValues, + ...mockFlashMessagesValues, +}; +export const mockAllActions = { + ...mockFlashMessagesActions, +}; + +/** + * Import this file directly to mock useValues with a set of default values for all shared logic files. + * Example usage: + * + * import '../../../__mocks__/kea'; // Must come before kea's import, adjust relative path as needed + */ jest.mock('kea', () => ({ ...(jest.requireActual('kea') as object), - useValues: jest.fn(() => ({})), - useActions: jest.fn(() => ({})), + useValues: jest.fn(() => ({ ...mockAllValues })), + useActions: jest.fn(() => ({ ...mockAllActions })), })); /** + * Call this function to override a specific set of Kea values while retaining all other defaults * Example usage within a component test: * - * import '../../../__mocks__/kea'; // Must come before kea's import, adjust relative path as needed - * - * import { useActions, useValues } from 'kea'; + * import '../../../__mocks__/kea'; + * import { setMockValues } from ''../../../__mocks__'; * * it('some test', () => { - * (useValues as jest.Mock).mockImplementationOnce(() => ({ someValue: 'hello' })); - * (useActions as jest.Mock).mockImplementationOnce(() => ({ someAction: () => 'world' })); + * setMockValues({ someValue: 'hello' }); * }); */ +import { useValues, useActions } from 'kea'; + +export const setMockValues = (values: object) => { + (useValues as jest.Mock).mockImplementation(() => ({ ...mockAllValues, ...values })); +}; +export const setMockActions = (actions: object) => { + (useActions as jest.Mock).mockImplementation(() => ({ ...mockAllActions, ...actions })); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.mock.ts deleted file mode 100644 index 890072ab42eb9..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_context.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 { httpServiceMock } from 'src/core/public/mocks'; -import { ExternalUrl } from '../shared/enterprise_search_url'; - -/** - * A set of default Kibana context values to use across component tests. - * @see enterprise_search/public/index.tsx for the KibanaContext definition/import - */ -export const mockKibanaContext = { - http: httpServiceMock.createSetupContract(), - navigateToUrl: jest.fn(), - setBreadcrumbs: jest.fn(), - setDocTitle: jest.fn(), - config: { host: 'http://localhost:3002' }, - externalUrl: new ExternalUrl('http://localhost:3002'), -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.ts new file mode 100644 index 0000000000000..ab91666d4acb6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kibana_logic.mock.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 { mockHistory } from './'; + +export const mockKibanaValues = { + config: { host: 'http://localhost:3002' }, + history: mockHistory, + navigateToUrl: jest.fn(), + setBreadcrumbs: jest.fn(), + setDocTitle: jest.fn(), + renderHeaderActions: jest.fn(), +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts deleted file mode 100644 index 7c37ecc7cde1b..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/license_context.mock.ts +++ /dev/null @@ -1,11 +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 { licensingMock } from '../../../../licensing/public/mocks'; - -export const mockLicenseContext = { - license: licensingMock.createLicense(), -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.ts new file mode 100644 index 0000000000000..51b32e7a877b2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/licensing_logic.mock.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 { licensingMock } from '../../../../licensing/public/mocks'; + +export const mockLicensingValues = { + license: licensingMock.createLicense(), + hasPlatinumLicense: false, + hasGoldLicense: false, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_async.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_async.mock.tsx new file mode 100644 index 0000000000000..a33e116c7ca72 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_async.mock.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 { act } from 'react-dom/test-utils'; +import { mount, ReactWrapper } from 'enzyme'; + +import { mountWithIntl } from './'; + +/** + * This helper is intended for components that have async effects + * (e.g. http fetches) on mount. It mostly adds act/update boilerplate + * that's needed for the wrapper to play nice with Enzyme/Jest + * + * Example usage: + * + * const wrapper = mountAsync(); + */ + +interface IOptions { + i18n?: boolean; +} + +export const mountAsync = async ( + children: React.ReactElement, + options: IOptions +): Promise => { + let wrapper: ReactWrapper | undefined; + + // We get a lot of act() warning/errors in the terminal without this. + // TBH, I don't fully understand why since Enzyme's mount is supposed to + // have act() baked in - could be because of the wrapping context provider? + await act(async () => { + wrapper = options.i18n ? mountWithIntl(children) : mount(children); + }); + if (wrapper) { + wrapper.update(); // This seems to be required for the DOM to actually update + + return wrapper; + } else { + throw new Error('Could not mount wrapper'); + } +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx deleted file mode 100644 index 826e0482acef7..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mount, ReactWrapper } from 'enzyme'; - -import { Provider } from 'react-redux'; -import { Store } from 'redux'; -import { getContext, resetContext } from 'kea'; - -import { I18nProvider } from '@kbn/i18n/react'; -import { KibanaContext } from '../'; -import { mockKibanaContext } from './kibana_context.mock'; -import { LicenseContext } from '../shared/licensing'; -import { mockLicenseContext } from './license_context.mock'; - -/** - * This helper mounts a component with all the contexts/providers used - * by the production app, while allowing custom context to be - * passed in via a second arg - * - * Example usage: - * - * const wrapper = mountWithContext(, { config: { host: 'someOverride' } }); - */ -export const mountWithContext = (children: React.ReactNode, context?: object) => { - resetContext({ createStore: true }); - const store = getContext().store as Store; - - return mount( - - - - {children} - - - - ); -}; - -/** - * This helper mounts a component with just the default KibanaContext - - * useful for isolated / helper components that only need this context - * - * Same usage/override functionality as mountWithContext - */ -export const mountWithKibanaContext = (children: React.ReactNode, context?: object) => { - return mount( - - {children} - - ); -}; - -/** - * This helper is intended for components that have async effects - * (e.g. http fetches) on mount. It mostly adds act/update boilerplate - * that's needed for the wrapper to play nice with Enzyme/Jest - * - * Example usage: - * - * const wrapper = mountWithAsyncContext(, { http: { get: () => someData } }); - */ -export const mountWithAsyncContext = async ( - children: React.ReactNode, - context: object -): Promise => { - let wrapper: ReactWrapper | undefined; - - // We get a lot of act() warning/errors in the terminal without this. - // TBH, I don't fully understand why since Enzyme's mount is supposed to - // have act() baked in - could be because of the wrapping context provider? - await act(async () => { - wrapper = mountWithContext(children, context); - }); - if (wrapper) { - wrapper.update(); // This seems to be required for the DOM to actually update - - return wrapper; - } else { - throw new Error('Could not mount wrapper'); - } -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_i18n.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_i18n.mock.tsx new file mode 100644 index 0000000000000..55abe1030544f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_i18n.mock.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 React from 'react'; +import { mount } from 'enzyme'; +import { I18nProvider } from '@kbn/i18n/react'; + +/** + * This helper wraps a component with react-intl's which + * fixes "Could not find required `intl` object" console errors when running tests + * + * Example usage (should be the same as mount()): + * + * const wrapper = mountWithI18n(); + */ +export const mountWithIntl = (children: React.ReactElement) => { + return mount({children}); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts index 842dcefd3aef8..2c833bcfeaf4c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/react_router_history.mock.ts @@ -14,7 +14,8 @@ export const mockHistory = { location: { pathname: '/current-path', }, -}; + listen: jest.fn(() => jest.fn()), +} as any; export const mockLocation = { key: 'someKey', pathname: '/current-path', @@ -24,6 +25,7 @@ export const mockLocation = { }; jest.mock('react-router-dom', () => ({ + ...(jest.requireActual('react-router-dom') as object), useHistory: jest.fn(() => mockHistory), useLocation: jest.fn(() => mockLocation), })); diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts deleted file mode 100644 index 3a2193db646de..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts +++ /dev/null @@ -1,41 +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. - */ - -/** - * NOTE: These variable names MUST start with 'mock*' in order for - * Jest to accept its use within a jest.mock() - */ -import { mockKibanaContext } from './kibana_context.mock'; -import { mockLicenseContext } from './license_context.mock'; - -jest.mock('react', () => ({ - ...(jest.requireActual('react') as object), - useContext: jest.fn(() => ({ ...mockKibanaContext, ...mockLicenseContext })), - useEffect: jest.fn((fn) => fn()), // Calls on mount/every update - use mount for more complex behavior -})); - -/** - * Example usage within a component test using shallow(): - * - * import '../../../__mocks__/shallow_usecontext'; // Must come before React's import, adjust relative path as needed - * - * import React from 'react'; - * import { shallow } from 'enzyme'; - * - * // ... etc. - */ - -/** - * If you need to override the default mock context values, you can do so via jest.mockImplementation: - * - * import React, { useContext } from 'react'; - * - * // ... etc. - * - * it('some test', () => { - * useContext.mockImplementationOnce(() => ({ config: { host: 'someOverride' } })); - * }); - */ diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts new file mode 100644 index 0000000000000..732786b5f9249 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_useeffect.mock.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('react', () => ({ + ...(jest.requireActual('react') as object), + useEffect: jest.fn((fn) => fn()), // Calls on mount/every update - use mount for more complex behavior +})); + +/** + * Example usage within a component test using shallow(): + * + * import '../../../__mocks__/shallow_useeffect.mock'; // Must come before React's import, adjust relative path as needed + * + * import React from 'react'; + * import { shallow } from 'enzyme'; + * + * // ... etc. + */ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.test.ts index 0f7bfe09edf7e..9410b9ef7cb03 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.test.ts @@ -56,6 +56,15 @@ describe('AppLogic', () => { }), }); }); + + it('gracefully handles missing initial data', () => { + AppLogic.actions.initializeAppData({}); + + expect(AppLogic.values).toEqual({ + ...DEFAULT_VALUES, + hasInitialized: true, + }); + }); }); describe('setOnboardingComplete()', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.ts index 8e5a8d75f407f..932e84af45c2b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/app_logic.ts @@ -39,7 +39,7 @@ export const AppLogic = kea>({ account: [ {}, { - initializeAppData: (_, { appSearch: account }) => account, + initializeAppData: (_, { appSearch: account }) => account || {}, setOnboardingComplete: (account) => ({ ...account, onboardingComplete: true, @@ -49,7 +49,7 @@ export const AppLogic = kea>({ configuredLimits: [ {}, { - initializeAppData: (_, { configuredLimits }) => configuredLimits.appSearch, + initializeAppData: (_, { configuredLimits }) => configuredLimits?.appSearch || {}, }, ], ilmEnabled: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts new file mode 100644 index 0000000000000..92d14f7275185 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; + +export const ADMIN = 'admin'; +export const PRIVATE = 'private'; +export const SEARCH = 'search'; + +export const TOKEN_TYPE_DESCRIPTION = { + [SEARCH]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.search.description', { + defaultMessage: 'Public Search Keys are used for search endpoints only.', + }), + [PRIVATE]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.private.description', { + defaultMessage: + 'Private API Keys are used for read and/or write access on one or more Engines.', + }), + [ADMIN]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.admin.description', { + defaultMessage: 'Private Admin Keys are used to interact with the Credentials API.', + }), +}; + +export const TOKEN_TYPE_DISPLAY_NAMES = { + [SEARCH]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.search.name', { + defaultMessage: 'Public Search Key', + }), + [PRIVATE]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.private.name', { + defaultMessage: 'Private API Key', + }), + [ADMIN]: i18n.translate('xpack.enterpriseSearch.appSearch.tokens.admin.name', { + defaultMessage: 'Private Admin Key', + }), +}; + +export const TOKEN_TYPE_INFO = [ + { value: SEARCH, text: TOKEN_TYPE_DISPLAY_NAMES[SEARCH] }, + { value: PRIVATE, text: TOKEN_TYPE_DISPLAY_NAMES[PRIVATE] }, + { value: ADMIN, text: TOKEN_TYPE_DISPLAY_NAMES[ADMIN] }, +]; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts new file mode 100644 index 0000000000000..c5cb8a2c61759 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.test.ts @@ -0,0 +1,1196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { resetContext } from 'kea'; + +import { CredentialsLogic } from './credentials_logic'; +import { ADMIN, PRIVATE } from './constants'; + +jest.mock('../../../shared/http', () => ({ + HttpLogic: { values: { http: { get: jest.fn(), delete: jest.fn() } } }, +})); +import { HttpLogic } from '../../../shared/http'; +jest.mock('../../../shared/flash_messages', () => ({ + flashAPIErrors: jest.fn(), +})); +import { flashAPIErrors } from '../../../shared/flash_messages'; + +describe('CredentialsLogic', () => { + const DEFAULT_VALUES = { + activeApiToken: { + name: '', + type: PRIVATE, + read: true, + write: true, + access_all_engines: true, + }, + activeApiTokenIsExisting: false, + activeApiTokenRawName: '', + apiTokens: [], + dataLoading: true, + engines: [], + formErrors: [], + isCredentialsDataComplete: false, + isCredentialsDetailsComplete: false, + meta: {}, + nameInputBlurred: false, + showCredentialsForm: false, + }; + + const mount = (defaults?: object) => { + if (!defaults) { + resetContext({}); + } else { + resetContext({ + defaults: { + enterprise_search: { + app_search: { + credentials_logic: { + ...defaults, + }, + }, + }, + }, + }); + } + CredentialsLogic.mount(); + }; + + const newToken = { + id: 1, + name: 'myToken', + type: PRIVATE, + read: true, + write: true, + access_all_engines: true, + engines: [], + }; + + const credentialsDetails = { + engines: [ + { name: 'engine1', type: 'indexed', language: 'english', result_fields: [] }, + { name: 'engine1', type: 'indexed', language: 'english', result_fields: [] }, + ], + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('has expected default values', () => { + mount(); + expect(CredentialsLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('addEngineName', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: expect.any(Object), + }; + + describe('activeApiToken', () => { + it("should add an engine to the active api token's engine list", () => { + mount({ + activeApiToken: { + ...newToken, + engines: ['someEngine'], + }, + }); + + CredentialsLogic.actions.addEngineName('newEngine'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, engines: ['someEngine', 'newEngine'] }, + }); + }); + + it("should create a new engines list if one doesn't exist", () => { + mount({ + activeApiToken: { + ...newToken, + engines: undefined, + }, + }); + + CredentialsLogic.actions.addEngineName('newEngine'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, engines: ['newEngine'] }, + }); + }); + }); + }); + + describe('removeEngineName', () => { + describe('activeApiToken', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: expect.any(Object), + }; + + it("should remove an engine from the active api token's engine list", () => { + mount({ + activeApiToken: { + ...newToken, + engines: ['someEngine', 'anotherEngine'], + }, + }); + + CredentialsLogic.actions.removeEngineName('someEngine'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, engines: ['anotherEngine'] }, + }); + }); + + it('will not remove the engine if it is not found', () => { + mount({ + activeApiToken: { + ...newToken, + engines: ['someEngine', 'anotherEngine'], + }, + }); + + CredentialsLogic.actions.removeEngineName('notfound'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, engines: ['someEngine', 'anotherEngine'] }, + }); + }); + + it('does not throw a type error if no engines are stored in state', () => { + mount({ + activeApiToken: {}, + }); + CredentialsLogic.actions.removeEngineName(''); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { engines: [] }, + }); + }); + }); + }); + + describe('setAccessAllEngines', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: expect.any(Object), + }; + + describe('activeApiToken', () => { + it('should set the value of access_all_engines and clear out engines list if true', () => { + mount({ + activeApiToken: { + ...newToken, + access_all_engines: false, + engines: ['someEngine', 'anotherEngine'], + }, + }); + + CredentialsLogic.actions.setAccessAllEngines(true); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, engines: [], access_all_engines: true }, + }); + }); + + it('should set the value of access_all_engines and but maintain engines list if false', () => { + mount({ + activeApiToken: { + ...newToken, + access_all_engines: true, + engines: ['someEngine', 'anotherEngine'], + }, + }); + + CredentialsLogic.actions.setAccessAllEngines(false); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...newToken, + access_all_engines: false, + engines: ['someEngine', 'anotherEngine'], + }, + }); + }); + }); + }); + + describe('onApiKeyDelete', () => { + const values = { + ...DEFAULT_VALUES, + apiTokens: expect.any(Array), + }; + + describe('apiTokens', () => { + it('should remove specified token from apiTokens if name matches', () => { + mount({ + apiTokens: [newToken], + }); + + CredentialsLogic.actions.onApiKeyDelete(newToken.name); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [], + }); + }); + + it('should not remove specified token from apiTokens if name does not match', () => { + mount({ + apiTokens: [newToken], + }); + + CredentialsLogic.actions.onApiKeyDelete('foo'); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [newToken], + }); + }); + }); + }); + + describe('onApiTokenCreateSuccess', () => { + const values = { + ...DEFAULT_VALUES, + apiTokens: expect.any(Array), + activeApiToken: expect.any(Object), + activeApiTokenRawName: expect.any(String), + showCredentialsForm: expect.any(Boolean), + formErrors: expect.any(Array), + }; + + describe('apiTokens', () => { + const existingToken = { + name: 'some_token', + type: PRIVATE, + }; + + it('should add the provided token to the apiTokens list', () => { + mount({ + apiTokens: [existingToken], + }); + + CredentialsLogic.actions.onApiTokenCreateSuccess(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [existingToken, newToken], + }); + }); + }); + + describe('activeApiToken', () => { + // TODO It is weird that methods like this update activeApiToken but not activeApiTokenIsExisting... + it('should reset to the default value, which effectively clears out the current form', () => { + mount({ + activeApiToken: newToken, + }); + + CredentialsLogic.actions.onApiTokenCreateSuccess(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: DEFAULT_VALUES.activeApiToken, + }); + }); + }); + + describe('activeApiTokenRawName', () => { + it('should reset to the default value, which effectively clears out the current form', () => { + mount({ + activeApiTokenRawName: 'foo', + }); + + CredentialsLogic.actions.onApiTokenCreateSuccess(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: DEFAULT_VALUES.activeApiTokenRawName, + }); + }); + }); + + describe('showCredentialsForm', () => { + it('should reset to the default value, which closes the credentials form', () => { + mount({ + showCredentialsForm: true, + }); + + CredentialsLogic.actions.onApiTokenCreateSuccess(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + showCredentialsForm: false, + }); + }); + }); + + describe('formErrors', () => { + it('should reset `formErrors`', () => { + mount({ + formErrors: ['I am an error'], + }); + + CredentialsLogic.actions.onApiTokenCreateSuccess(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + formErrors: [], + }); + }); + }); + }); + + describe('onApiTokenError', () => { + const values = { + ...DEFAULT_VALUES, + formErrors: expect.any(Array), + }; + + describe('formErrors', () => { + it('should set `formErrors`', () => { + mount({ + formErrors: ['I am an error'], + }); + + CredentialsLogic.actions.onApiTokenError(['I am the NEW error']); + expect(CredentialsLogic.values).toEqual({ + ...values, + formErrors: ['I am the NEW error'], + }); + }); + }); + }); + + describe('onApiTokenUpdateSuccess', () => { + const values = { + ...DEFAULT_VALUES, + apiTokens: expect.any(Array), + activeApiToken: expect.any(Object), + activeApiTokenRawName: expect.any(String), + showCredentialsForm: expect.any(Boolean), + }; + + describe('apiTokens', () => { + const existingToken = { + name: 'some_token', + type: PRIVATE, + }; + + it('should replace the existing token with the new token by name', () => { + mount({ + apiTokens: [newToken, existingToken], + }); + const updatedExistingToken = { + ...existingToken, + type: ADMIN, + }; + + CredentialsLogic.actions.onApiTokenUpdateSuccess(updatedExistingToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [newToken, updatedExistingToken], + }); + }); + + // TODO Not sure if this is a good behavior or not + it('if for some reason the existing token is not found, it adds a new token...', () => { + mount({ + apiTokens: [newToken, existingToken], + }); + const brandNewToken = { + name: 'brand new token', + type: ADMIN, + }; + + CredentialsLogic.actions.onApiTokenUpdateSuccess(brandNewToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [newToken, existingToken, brandNewToken], + }); + }); + }); + + describe('activeApiToken', () => { + it('should reset to the default value, which effectively clears out the current form', () => { + mount({ + activeApiToken: newToken, + }); + + CredentialsLogic.actions.onApiTokenUpdateSuccess({ ...newToken, type: ADMIN }); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: DEFAULT_VALUES.activeApiToken, + }); + }); + }); + + describe('activeApiTokenRawName', () => { + it('should reset to the default value, which effectively clears out the current form', () => { + mount({ + activeApiTokenRawName: 'foo', + }); + + CredentialsLogic.actions.onApiTokenUpdateSuccess({ ...newToken, type: ADMIN }); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: DEFAULT_VALUES.activeApiTokenRawName, + }); + }); + }); + + describe('showCredentialsForm', () => { + it('should reset to the default value, which closes the credentials form', () => { + mount({ + showCredentialsForm: true, + }); + + CredentialsLogic.actions.onApiTokenUpdateSuccess({ ...newToken, type: ADMIN }); + expect(CredentialsLogic.values).toEqual({ + ...values, + showCredentialsForm: false, + }); + }); + }); + }); + + describe('setCredentialsData', () => { + const meta = { + page: { + current: 1, + size: 1, + total_pages: 1, + total_results: 1, + }, + }; + + const values = { + ...DEFAULT_VALUES, + apiTokens: expect.any(Array), + meta: expect.any(Object), + isCredentialsDataComplete: expect.any(Boolean), + }; + + describe('apiTokens', () => { + it('should be set', () => { + mount(); + + CredentialsLogic.actions.setCredentialsData(meta, [newToken, newToken]); + expect(CredentialsLogic.values).toEqual({ + ...values, + apiTokens: [newToken, newToken], + }); + }); + }); + + describe('meta', () => { + it('should be set', () => { + mount(); + + CredentialsLogic.actions.setCredentialsData(meta, [newToken, newToken]); + expect(CredentialsLogic.values).toEqual({ + ...values, + meta, + }); + }); + }); + + describe('isCredentialsDataComplete', () => { + it('should be set to true so we know that data fetching has completed', () => { + mount({ + isCredentialsDataComplete: false, + }); + + CredentialsLogic.actions.setCredentialsData(meta, [newToken, newToken]); + expect(CredentialsLogic.values).toEqual({ + ...values, + isCredentialsDataComplete: true, + }); + }); + }); + }); + + describe('setCredentialsDetails', () => { + const values = { + ...DEFAULT_VALUES, + engines: expect.any(Array), + isCredentialsDetailsComplete: expect.any(Boolean), + }; + + describe('isCredentialsDataComplete', () => { + it('should be set to true so that we know data fetching has been completed', () => { + mount({ + isCredentialsDetailsComplete: false, + }); + + CredentialsLogic.actions.setCredentialsDetails(credentialsDetails); + expect(CredentialsLogic.values).toEqual({ + ...values, + isCredentialsDetailsComplete: true, + }); + }); + }); + + describe('engines', () => { + it('should set `engines` from the provided details object', () => { + mount({ + engines: [], + }); + + CredentialsLogic.actions.setCredentialsDetails(credentialsDetails); + expect(CredentialsLogic.values).toEqual({ + ...values, + engines: credentialsDetails.engines, + }); + }); + }); + }); + + describe('setNameInputBlurred', () => { + const values = { + ...DEFAULT_VALUES, + nameInputBlurred: expect.any(Boolean), + }; + + describe('nameInputBlurred', () => { + it('should set this value', () => { + mount({ + nameInputBlurred: false, + }); + + CredentialsLogic.actions.setNameInputBlurred(true); + expect(CredentialsLogic.values).toEqual({ + ...values, + nameInputBlurred: true, + }); + }); + }); + }); + + describe('setTokenReadWrite', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: expect.any(Object), + }; + + describe('activeApiToken', () => { + it('should set "read" or "write" values', () => { + mount({ + activeApiToken: { + ...newToken, + read: false, + }, + }); + + CredentialsLogic.actions.setTokenReadWrite({ name: 'read', checked: true }); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...newToken, + read: true, + }, + }); + }); + }); + }); + + describe('setTokenName', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: expect.any(Object), + activeApiTokenRawName: expect.any(String), + }; + + describe('activeApiToken', () => { + it('update the name property on the activeApiToken, formatted correctly', () => { + mount({ + activeApiToken: { + ...newToken, + name: 'bar', + }, + }); + + CredentialsLogic.actions.setTokenName('New Name'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { ...newToken, name: 'new-name' }, + }); + }); + }); + + describe('activeApiTokenRawName', () => { + it('updates the raw name, with no formatting applied', () => { + mount(); + + CredentialsLogic.actions.setTokenName('New Name'); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: 'New Name', + }); + }); + }); + }); + + describe('setTokenType', () => { + const values = { + ...DEFAULT_VALUES, + activeApiToken: { + ...newToken, + type: expect.any(String), + read: expect.any(Boolean), + write: expect.any(Boolean), + access_all_engines: expect.any(Boolean), + engines: expect.any(Array), + }, + }; + + describe('activeApiToken.access_all_engines', () => { + describe('when value is ADMIN', () => { + it('updates access_all_engines to false', () => { + mount({ + activeApiToken: { + ...newToken, + access_all_engines: true, + }, + }); + + CredentialsLogic.actions.setTokenType(ADMIN); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + access_all_engines: false, + }, + }); + }); + }); + + describe('when value is not ADMIN', () => { + it('will maintain access_all_engines value when true', () => { + mount({ + activeApiToken: { + ...newToken, + access_all_engines: true, + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + access_all_engines: true, + }, + }); + }); + + it('will maintain access_all_engines value when false', () => { + mount({ + activeApiToken: { + ...newToken, + access_all_engines: false, + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + access_all_engines: false, + }, + }); + }); + }); + }); + + describe('activeApiToken.engines', () => { + describe('when value is ADMIN', () => { + it('clears the array', () => { + mount({ + activeApiToken: { + ...newToken, + engines: [{}, {}], + }, + }); + + CredentialsLogic.actions.setTokenType(ADMIN); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + engines: [], + }, + }); + }); + }); + + describe('when value is not ADMIN', () => { + it('will maintain engines array', () => { + mount({ + activeApiToken: { + ...newToken, + engines: [{}, {}], + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + engines: [{}, {}], + }, + }); + }); + }); + }); + + describe('activeApiToken.write', () => { + describe('when value is PRIVATE', () => { + it('sets this to true', () => { + mount({ + activeApiToken: { + ...newToken, + write: false, + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + write: true, + }, + }); + }); + }); + + describe('when value is not PRIVATE', () => { + it('sets this to false', () => { + mount({ + activeApiToken: { + ...newToken, + write: true, + }, + }); + + CredentialsLogic.actions.setTokenType(ADMIN); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + write: false, + }, + }); + }); + }); + }); + + describe('activeApiToken.read', () => { + describe('when value is PRIVATE', () => { + it('sets this to true', () => { + mount({ + activeApiToken: { + ...newToken, + read: false, + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + read: true, + }, + }); + }); + }); + + describe('when value is not PRIVATE', () => { + it('sets this to false', () => { + mount({ + activeApiToken: { + ...newToken, + read: true, + }, + }); + + CredentialsLogic.actions.setTokenType(ADMIN); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + read: false, + }, + }); + }); + }); + }); + + describe('activeApiToken.type', () => { + it('sets the type value', () => { + mount({ + activeApiToken: { + ...newToken, + type: ADMIN, + }, + }); + + CredentialsLogic.actions.setTokenType(PRIVATE); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: { + ...values.activeApiToken, + type: PRIVATE, + }, + }); + }); + }); + }); + + describe('toggleCredentialsForm', () => { + const values = { + ...DEFAULT_VALUES, + activeApiTokenIsExisting: expect.any(Boolean), + activeApiToken: expect.any(Object), + activeApiTokenRawName: expect.any(String), + formErrors: expect.any(Array), + showCredentialsForm: expect.any(Boolean), + }; + + describe('showCredentialsForm', () => { + it('should toggle `showCredentialsForm`', () => { + mount({ + showCredentialsForm: false, + }); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + showCredentialsForm: true, + }); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + showCredentialsForm: false, + }); + }); + }); + + describe('formErrors', () => { + it('should reset `formErrors`', () => { + mount({ + formErrors: ['I am an error'], + }); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + formErrors: [], + }); + }); + }); + + describe('activeApiTokenRawName', () => { + it('should set `activeApiTokenRawName` to the name of the provided token', () => { + mount(); + + CredentialsLogic.actions.toggleCredentialsForm(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: 'myToken', + }); + }); + + it('should set `activeApiTokenRawName` to the default value if no token is provided', () => { + mount(); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: DEFAULT_VALUES.activeApiTokenRawName, + }); + }); + + // TODO: This fails, is this an issue? Instead of reseting back to the default value, it sets it to the previously + // used value... to be honest, this should probably just be a selector + // it('should set `activeApiTokenRawName` back to the default value if no token is provided', () => { + // mount(); + // CredentialsLogic.actions.toggleCredentialsForm(newToken); + // CredentialsLogic.actions.toggleCredentialsForm(); + // expect(CredentialsLogic.values).toEqual({ + // ...values, + // activeApiTokenRawName: DEFAULT_VALUES.activeApiTokenRawName, + // }); + // }); + }); + + describe('activeApiToken', () => { + it('should set `activeApiToken` to the provided token', () => { + mount(); + + CredentialsLogic.actions.toggleCredentialsForm(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: newToken, + }); + }); + + it('should set `activeApiToken` to the default value if no token is provided', () => { + mount({ + activeApiToken: newToken, + }); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiToken: DEFAULT_VALUES.activeApiToken, + }); + }); + }); + + // TODO: This should probably just be a selector... + describe('activeApiTokenIsExisting', () => { + it('should set `activeApiTokenIsExisting` to true when the provided token has an id', () => { + mount({ + activeApiTokenIsExisting: false, + }); + + CredentialsLogic.actions.toggleCredentialsForm(newToken); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenIsExisting: true, + }); + }); + + it('should set `activeApiTokenIsExisting` to false when the provided token has no id', () => { + mount({ + activeApiTokenIsExisting: true, + }); + const { id, ...newTokenWithoutId } = newToken; + + CredentialsLogic.actions.toggleCredentialsForm(newTokenWithoutId); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenIsExisting: false, + }); + }); + + it('should set `activeApiTokenIsExisting` to false when no token is provided', () => { + mount({ + activeApiTokenIsExisting: true, + }); + + CredentialsLogic.actions.toggleCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenIsExisting: false, + }); + }); + }); + }); + + describe('hideCredentialsForm', () => { + const values = { + ...DEFAULT_VALUES, + showCredentialsForm: expect.any(Boolean), + activeApiTokenRawName: expect.any(String), + }; + + describe('activeApiTokenRawName', () => { + it('resets this value', () => { + mount({ + activeApiTokenRawName: 'foo', + }); + + CredentialsLogic.actions.hideCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + activeApiTokenRawName: '', + }); + }); + }); + + describe('showCredentialsForm', () => { + it('resets this value', () => { + mount({ + showCredentialsForm: true, + }); + + CredentialsLogic.actions.hideCredentialsForm(); + expect(CredentialsLogic.values).toEqual({ + ...values, + showCredentialsForm: false, + }); + }); + }); + }); + + describe('resetCredentials', () => { + const values = { + ...DEFAULT_VALUES, + isCredentialsDetailsComplete: expect.any(Boolean), + isCredentialsDataComplete: expect.any(Boolean), + formErrors: expect.any(Array), + }; + + describe('isCredentialsDetailsComplete', () => { + it('should reset to false', () => { + mount({ + isCredentialsDetailsComplete: true, + }); + + CredentialsLogic.actions.resetCredentials(); + expect(CredentialsLogic.values).toEqual({ + ...values, + isCredentialsDetailsComplete: false, + }); + }); + }); + + describe('isCredentialsDataComplete', () => { + it('should reset to false', () => { + mount({ + isCredentialsDataComplete: true, + }); + + CredentialsLogic.actions.resetCredentials(); + expect(CredentialsLogic.values).toEqual({ + ...values, + isCredentialsDataComplete: false, + }); + }); + }); + + describe('formErrors', () => { + it('should reset', () => { + mount({ + formErrors: ['I am an error'], + }); + + CredentialsLogic.actions.resetCredentials(); + expect(CredentialsLogic.values).toEqual({ + ...values, + formErrors: [], + }); + }); + }); + }); + + describe('initializeCredentialsData', () => { + it('should call fetchCredentials and fetchDetails', () => { + mount(); + jest.spyOn(CredentialsLogic.actions, 'fetchCredentials').mockImplementationOnce(() => {}); + jest.spyOn(CredentialsLogic.actions, 'fetchDetails').mockImplementationOnce(() => {}); + + CredentialsLogic.actions.initializeCredentialsData(); + expect(CredentialsLogic.actions.fetchCredentials).toHaveBeenCalled(); + expect(CredentialsLogic.actions.fetchDetails).toHaveBeenCalled(); + }); + }); + + describe('fetchCredentials', () => { + const meta = { + page: { + current: 1, + size: 1, + total_pages: 1, + total_results: 1, + }, + }; + const results: object[] = []; + + it('will call an API endpoint and set the results with the `setCredentialsData` action', async () => { + mount(); + jest.spyOn(CredentialsLogic.actions, 'setCredentialsData').mockImplementationOnce(() => {}); + const promise = Promise.resolve({ meta, results }); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.fetchCredentials(2); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/credentials', { + query: { + 'page[current]': 2, + }, + }); + await promise; + expect(CredentialsLogic.actions.setCredentialsData).toHaveBeenCalledWith(meta, results); + }); + + it('handles errors', async () => { + mount(); + const promise = Promise.reject('An error occured'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.fetchCredentials(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); + } + }); + }); + + describe('fetchDetails', () => { + it('will call an API endpoint and set the results with the `setCredentialsDetails` action', async () => { + mount(); + jest + .spyOn(CredentialsLogic.actions, 'setCredentialsDetails') + .mockImplementationOnce(() => {}); + const promise = Promise.resolve(credentialsDetails); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.fetchDetails(); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/app_search/credentials/details'); + await promise; + expect(CredentialsLogic.actions.setCredentialsDetails).toHaveBeenCalledWith( + credentialsDetails + ); + }); + + it('handles errors', async () => { + mount(); + const promise = Promise.reject('An error occured'); + (HttpLogic.values.http.get as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.fetchDetails(); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); + } + }); + }); + + describe('deleteApiKey', () => { + const tokenName = 'abc123'; + + it('will call an API endpoint and set the results with the `onApiKeyDelete` action', async () => { + mount(); + jest.spyOn(CredentialsLogic.actions, 'onApiKeyDelete').mockImplementationOnce(() => {}); + const promise = Promise.resolve(); + (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.deleteApiKey(tokenName); + expect(HttpLogic.values.http.delete).toHaveBeenCalledWith( + `/api/app_search/credentials/${tokenName}` + ); + await promise; + expect(CredentialsLogic.actions.onApiKeyDelete).toHaveBeenCalledWith(tokenName); + }); + + it('handles errors', async () => { + mount(); + const promise = Promise.reject('An error occured'); + (HttpLogic.values.http.delete as jest.Mock).mockReturnValue(promise); + + CredentialsLogic.actions.deleteApiKey(tokenName); + try { + await promise; + } catch { + expect(flashAPIErrors).toHaveBeenCalledWith('An error occured'); + } + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts new file mode 100644 index 0000000000000..43f2731711823 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_logic.ts @@ -0,0 +1,264 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { kea, MakeLogicType } from 'kea'; + +import { formatApiName } from '../../utils/format_api_name'; +import { ADMIN, PRIVATE } from './constants'; + +import { HttpLogic } from '../../../shared/http'; +import { IMeta } from '../../../../../common/types'; +import { flashAPIErrors } from '../../../shared/flash_messages'; +import { IEngine } from '../../types'; +import { IApiToken, ICredentialsDetails } from './types'; + +interface ITokenReadWrite { + name: 'read' | 'write'; + checked: boolean; +} + +const defaultApiToken: IApiToken = { + name: '', + type: PRIVATE, + read: true, + write: true, + access_all_engines: true, +}; + +// TODO CREATE_MESSAGE, UPDATE_MESSAGE, and DELETE_MESSAGE from ent-search + +export interface ICredentialsLogicActions { + addEngineName(engineName: string): string; + onApiKeyDelete(tokenName: string): string; + onApiTokenCreateSuccess(apiToken: IApiToken): IApiToken; + onApiTokenError(formErrors: string[]): string[]; + onApiTokenUpdateSuccess(apiToken: IApiToken): IApiToken; + removeEngineName(engineName: string): string; + setAccessAllEngines(accessAll: boolean): boolean; + setCredentialsData(meta: IMeta, apiTokens: IApiToken[]): { meta: IMeta; apiTokens: IApiToken[] }; + setCredentialsDetails(details: ICredentialsDetails): ICredentialsDetails; + setNameInputBlurred(isBlurred: boolean): boolean; + setTokenReadWrite(tokenReadWrite: ITokenReadWrite): ITokenReadWrite; + setTokenName(name: string): string; + setTokenType(tokenType: string): string; + toggleCredentialsForm(apiToken?: IApiToken): IApiToken; + hideCredentialsForm(): { value: boolean }; + resetCredentials(): { value: boolean }; + initializeCredentialsData(): { value: boolean }; + fetchCredentials(page?: number): number; + fetchDetails(): { value: boolean }; + deleteApiKey(tokenName: string): string; +} + +export interface ICredentialsLogicValues { + activeApiToken: IApiToken; + activeApiTokenIsExisting: boolean; + activeApiTokenRawName: string; + apiTokens: IApiToken[]; + dataLoading: boolean; + engines: IEngine[]; + formErrors: string[]; + isCredentialsDataComplete: boolean; + isCredentialsDetailsComplete: boolean; + fullEngineAccessChecked: boolean; + meta: Partial; + nameInputBlurred: boolean; + showCredentialsForm: boolean; +} + +export const CredentialsLogic = kea< + MakeLogicType +>({ + path: ['enterprise_search', 'app_search', 'credentials_logic'], + actions: () => ({ + addEngineName: (engineName) => engineName, + onApiKeyDelete: (tokenName) => tokenName, + onApiTokenCreateSuccess: (apiToken) => apiToken, + onApiTokenError: (formErrors) => formErrors, + onApiTokenUpdateSuccess: (apiToken) => apiToken, + removeEngineName: (engineName) => engineName, + setAccessAllEngines: (accessAll) => accessAll, + setCredentialsData: (meta, apiTokens) => ({ meta, apiTokens }), + setCredentialsDetails: (details) => details, + setNameInputBlurred: (nameInputBlurred) => nameInputBlurred, + setTokenReadWrite: ({ name, checked }) => ({ + name, + checked, + }), + setTokenName: (name) => name, + setTokenType: (tokenType) => tokenType, + toggleCredentialsForm: (apiToken = { ...defaultApiToken }) => apiToken, + hideCredentialsForm: false, + resetCredentials: false, + initializeCredentialsData: true, + fetchCredentials: (page) => page, + fetchDetails: true, + deleteApiKey: (tokenName) => tokenName, + }), + reducers: () => ({ + apiTokens: [ + [], + { + setCredentialsData: (_, { apiTokens }) => apiTokens, + onApiTokenCreateSuccess: (apiTokens, apiToken) => [...apiTokens, apiToken], + onApiTokenUpdateSuccess: (apiTokens, apiToken) => [ + ...apiTokens.filter((token) => token.name !== apiToken.name), + apiToken, + ], + onApiKeyDelete: (apiTokens, tokenName) => + apiTokens.filter((token) => token.name !== tokenName), + }, + ], + meta: [ + {}, + { + setCredentialsData: (_, { meta }) => meta, + }, + ], + isCredentialsDetailsComplete: [ + false, + { + setCredentialsDetails: () => true, + resetCredentials: () => false, + }, + ], + isCredentialsDataComplete: [ + false, + { + setCredentialsData: () => true, + resetCredentials: () => false, + }, + ], + engines: [ + [], + { + setCredentialsDetails: (_, { engines }) => engines, + }, + ], + nameInputBlurred: [ + false, + { + setNameInputBlurred: (_, nameInputBlurred) => nameInputBlurred, + }, + ], + activeApiToken: [ + defaultApiToken, + { + addEngineName: (activeApiToken, engineName) => ({ + ...activeApiToken, + engines: [...(activeApiToken.engines || []), engineName], + }), + removeEngineName: (activeApiToken, engineName) => ({ + ...activeApiToken, + engines: (activeApiToken.engines || []).filter((name) => name !== engineName), + }), + setAccessAllEngines: (activeApiToken, accessAll) => ({ + ...activeApiToken, + access_all_engines: accessAll, + engines: accessAll ? [] : activeApiToken.engines, + }), + onApiTokenCreateSuccess: () => defaultApiToken, + onApiTokenUpdateSuccess: () => defaultApiToken, + setTokenName: (activeApiToken, name) => ({ ...activeApiToken, name: formatApiName(name) }), + setTokenReadWrite: (activeApiToken, { name, checked }) => ({ + ...activeApiToken, + [name]: checked, + }), + setTokenType: (activeApiToken, tokenType) => ({ + ...activeApiToken, + access_all_engines: tokenType === ADMIN ? false : activeApiToken.access_all_engines, + engines: tokenType === ADMIN ? [] : activeApiToken.engines, + write: tokenType === PRIVATE, + read: tokenType === PRIVATE, + type: tokenType, + }), + toggleCredentialsForm: (_, activeApiToken) => activeApiToken, + }, + ], + activeApiTokenRawName: [ + '', + { + setTokenName: (_, activeApiTokenRawName) => activeApiTokenRawName, + toggleCredentialsForm: (activeApiTokenRawName, activeApiToken) => + activeApiToken.name || activeApiTokenRawName, + hideCredentialsForm: () => '', + onApiTokenCreateSuccess: () => '', + onApiTokenUpdateSuccess: () => '', + }, + ], + activeApiTokenIsExisting: [ + false, + { + toggleCredentialsForm: (_, activeApiToken) => !!activeApiToken.id, + }, + ], + showCredentialsForm: [ + false, + { + toggleCredentialsForm: (showCredentialsForm) => !showCredentialsForm, + hideCredentialsForm: () => false, + onApiTokenCreateSuccess: () => false, + onApiTokenUpdateSuccess: () => false, + }, + ], + formErrors: [ + [], + { + onApiTokenError: (_, formErrors) => formErrors, + onApiTokenCreateSuccess: () => [], + toggleCredentialsForm: () => [], + resetCredentials: () => [], + }, + ], + }), + selectors: ({ selectors }) => ({ + // TODO fullEngineAccessChecked from ent-search + dataLoading: [ + () => [selectors.isCredentialsDetailsComplete, selectors.isCredentialsDataComplete], + (isCredentialsDetailsComplete, isCredentialsDataComplete) => { + return isCredentialsDetailsComplete === false || isCredentialsDataComplete === false; + }, + ], + }), + listeners: ({ actions, values }) => ({ + initializeCredentialsData: () => { + actions.fetchCredentials(); + actions.fetchDetails(); + }, + fetchCredentials: async (page = 1) => { + try { + const { http } = HttpLogic.values; + const query = { 'page[current]': page }; + const response = await http.get('/api/app_search/credentials', { query }); + actions.setCredentialsData(response.meta, response.results); + } catch (e) { + flashAPIErrors(e); + } + }, + fetchDetails: async () => { + try { + const { http } = HttpLogic.values; + const response = await http.get('/api/app_search/credentials/details'); + + actions.setCredentialsDetails(response); + } catch (e) { + flashAPIErrors(e); + } + }, + deleteApiKey: async (tokenName) => { + try { + const { http } = HttpLogic.values; + await http.delete(`/api/app_search/credentials/${tokenName}`); + + actions.onApiKeyDelete(tokenName); + } catch (e) { + flashAPIErrors(e); + } + }, + // TODO onApiTokenChange from ent-search + // TODO onEngineSelect from ent-search + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/types.ts new file mode 100644 index 0000000000000..9b09bd13a9086 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/types.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 { IEngine } from '../../types'; + +export interface ICredentialsDetails { + engines: IEngine[]; +} + +export interface IApiToken { + access_all_engines?: boolean; + key?: string; + engines?: string[]; + id?: number; + name: string; + read?: boolean; + type: string; + write?: boolean; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx index 7e6876bc9b3a4..53f50822cf653 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; +import '../../../../__mocks__/kea.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx index 58691cf09b4a5..cfe88d00ce14e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx @@ -4,13 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiPageContent, EuiEmptyPrompt, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { sendTelemetry } from '../../../../shared/telemetry'; +import { HttpLogic } from '../../../../shared/http'; +import { getAppSearchUrl } from '../../../../shared/enterprise_search_url'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; -import { KibanaContext, IKibanaContext } from '../../../../index'; import { CREATE_ENGINES_PATH } from '../../../routes'; import { EngineOverviewHeader } from './header'; @@ -18,10 +20,7 @@ import { EngineOverviewHeader } from './header'; import './empty_state.scss'; export const EmptyState: React.FC = () => { - const { - externalUrl: { getAppSearchUrl }, - http, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const buttonProps = { href: getAppSearchUrl(CREATE_ENGINES_PATH), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx index 7f22ce132d405..78ee5764be5a9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; +import '../../../../__mocks__/kea.mock'; +import '../../../../__mocks__/enterprise_search_url.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx index 1a1ae295d4828..6ebb2c5bf453d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiPageHeader, EuiPageHeaderSection, @@ -16,13 +17,11 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { sendTelemetry } from '../../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../../index'; +import { HttpLogic } from '../../../../shared/http'; +import { getAppSearchUrl } from '../../../../shared/enterprise_search_url'; export const EngineOverviewHeader: React.FC = () => { - const { - externalUrl: { getAppSearchUrl }, - http, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const buttonProps = { fill: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx index c2379fb33bd71..f87ea2d422780 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../../__mocks__/kea.mock'; import '../../../__mocks__/react_router_history.mock'; import React from 'react'; import { act } from 'react-dom/test-utils'; import { shallow, ReactWrapper } from 'enzyme'; -import { mountWithAsyncContext, mockKibanaContext } from '../../../__mocks__'; +import { mountAsync, mockHttpValues, setMockValues } from '../../../__mocks__'; import { LoadingState, EmptyState } from './components'; import { EngineTable } from './engine_table'; @@ -18,8 +19,6 @@ import { EngineTable } from './engine_table'; import { EngineOverview } from './'; describe('EngineOverview', () => { - const mockHttp = mockKibanaContext.http; - describe('non-happy-path states', () => { it('isLoading', () => { const wrapper = shallow(); @@ -28,15 +27,16 @@ describe('EngineOverview', () => { }); it('isEmpty', async () => { - const wrapper = await mountWithAsyncContext(, { + setMockValues({ http: { - ...mockHttp, + ...mockHttpValues.http, get: () => ({ results: [], meta: { page: { total_results: 0 } }, }), }, }); + const wrapper = await mountAsync(, { i18n: true }); expect(wrapper.find(EmptyState)).toHaveLength(1); }); @@ -65,12 +65,11 @@ describe('EngineOverview', () => { beforeEach(() => { jest.clearAllMocks(); + setMockValues({ http: { ...mockHttpValues.http, get: mockApi } }); }); it('renders and calls the engines API', async () => { - const wrapper = await mountWithAsyncContext(, { - http: { ...mockHttp, get: mockApi }, - }); + const wrapper = await mountAsync(, { i18n: true }); expect(wrapper.find(EngineTable)).toHaveLength(1); expect(mockApi).toHaveBeenNthCalledWith(1, '/api/app_search/engines', { @@ -83,10 +82,11 @@ describe('EngineOverview', () => { describe('when on a platinum license', () => { it('renders a 2nd meta engines table & makes a 2nd meta engines API call', async () => { - const wrapper = await mountWithAsyncContext(, { - http: { ...mockHttp, get: mockApi }, - license: { type: 'platinum', isActive: true }, + setMockValues({ + hasPlatinumLicense: true, + http: { ...mockHttpValues.http, get: mockApi }, }); + const wrapper = await mountAsync(, { i18n: true }); expect(wrapper.find(EngineTable)).toHaveLength(2); expect(mockApi).toHaveBeenNthCalledWith(2, '/api/app_search/engines', { @@ -103,9 +103,7 @@ describe('EngineOverview', () => { wrapper.find(EngineTable).prop('pagination'); it('passes down page data from the API', async () => { - const wrapper = await mountWithAsyncContext(, { - http: { ...mockHttp, get: mockApi }, - }); + const wrapper = await mountAsync(, { i18n: true }); const pagination = getTablePagination(wrapper); expect(pagination.totalEngines).toEqual(100); @@ -113,9 +111,7 @@ describe('EngineOverview', () => { }); it('re-polls the API on page change', async () => { - const wrapper = await mountWithAsyncContext(, { - http: { ...mockHttp, get: mockApi }, - }); + const wrapper = await mountAsync(, { i18n: true }); await act(async () => getTablePagination(wrapper).onPaginate(5)); wrapper.update(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx index 9703fde7e140a..0cb9ba106dbb8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; +import { useValues } from 'kea'; import { EuiPageContent, EuiPageContentHeader, @@ -12,13 +13,13 @@ import { EuiTitle, EuiSpacer, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; import { FlashMessages } from '../../../shared/flash_messages'; -import { LicenseContext, ILicenseContext, hasPlatinumLicense } from '../../../shared/licensing'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { LicensingLogic } from '../../../shared/licensing'; import { EngineIcon } from './assets/engine_icon'; import { MetaEngineIcon } from './assets/meta_engine_icon'; @@ -38,8 +39,8 @@ interface ISetEnginesCallbacks { } export const EngineOverview: React.FC = () => { - const { http } = useContext(KibanaContext) as IKibanaContext; - const { license } = useContext(LicenseContext) as ILicenseContext; + const { http } = useValues(HttpLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); const [isLoading, setIsLoading] = useState(true); const [engines, setEngines] = useState([]); @@ -71,13 +72,13 @@ export const EngineOverview: React.FC = () => { }, [enginesPage]); useEffect(() => { - if (hasPlatinumLicense(license)) { + if (hasPlatinumLicense) { const params = { type: 'meta', pageIndex: metaEnginesPage }; const callbacks = { setResults: setMetaEngines, setResultsTotal: setMetaEnginesTotal }; setEnginesData(params, callbacks); } - }, [license, metaEnginesPage]); + }, [hasPlatinumLicense, metaEnginesPage]); if (isLoading) return ; if (!engines.length) return ; @@ -94,10 +95,9 @@ export const EngineOverview: React.FC = () => {

- + {i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.engines', { + defaultMessage: 'Engines', + })}

@@ -119,10 +119,9 @@ export const EngineOverview: React.FC = () => {

- + {i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.metaEngines', { + defaultMessage: 'Meta Engines', + })}

diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx index 46b6e61e352de..4d97a16991b71 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/enterprise_search_url.mock'; +import { mockHttpValues, mountWithIntl } from '../../../__mocks__/'; + import React from 'react'; import { EuiBasicTable, EuiPagination, EuiButtonEmpty, EuiLink } from '@elastic/eui'; -import { mountWithContext } from '../../../__mocks__'; jest.mock('../../../shared/telemetry', () => ({ sendTelemetry: jest.fn() })); import { sendTelemetry } from '../../../shared/telemetry'; @@ -16,7 +19,7 @@ import { EngineTable } from './engine_table'; describe('EngineTable', () => { const onPaginate = jest.fn(); // onPaginate updates the engines API call upstream - const wrapper = mountWithContext( + const wrapper = mountWithIntl( { link.simulate('click'); expect(sendTelemetry).toHaveBeenCalledWith({ - http: expect.any(Object), + http: mockHttpValues.http, product: 'app_search', action: 'clicked', metric: 'engine_table_link', @@ -71,10 +74,11 @@ describe('EngineTable', () => { }); it('handles empty data', () => { - const emptyWrapper = mountWithContext( + const emptyWrapper = mountWithIntl( {} }} /> ); const emptyTable = emptyWrapper.find(EuiBasicTable); + expect(emptyTable.prop('pagination').pageIndex).toEqual(0); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx index 9c6122c88c7d7..40fb313f30b31 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx @@ -4,13 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn, EuiLink } from '@elastic/eui'; import { FormattedMessage, FormattedDate, FormattedNumber } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { getAppSearchUrl } from '../../../shared/enterprise_search_url'; import { getEngineRoute } from '../../routes'; import { ENGINES_PAGE_SIZE } from '../../../../../common/constants'; @@ -40,10 +42,7 @@ export const EngineTable: React.FC = ({ data, pagination: { totalEngines, pageIndex, onPaginate }, }) => { - const { - externalUrl: { getAppSearchUrl }, - http, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const engineLinkProps = (name: string) => ({ href: getAppSearchUrl(getEngineRoute(name)), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx index 350bc97085d7b..ab5b3c9faeea7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../__mocks__/shallow_usecontext.mock'; +import '../__mocks__/shallow_useeffect.mock'; import '../__mocks__/kea.mock'; +import '../__mocks__/enterprise_search_url.mock'; +import { setMockValues, setMockActions } from '../__mocks__'; -import React, { useContext } from 'react'; +import React from 'react'; import { Redirect } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { useValues, useActions } from 'kea'; import { Layout, SideNav, SideNavLink } from '../shared/layout'; import { SetupGuide } from './components/setup_guide'; @@ -20,14 +21,14 @@ import { AppSearch, AppSearchUnconfigured, AppSearchConfigured, AppSearchNav } f describe('AppSearch', () => { it('renders AppSearchUnconfigured when config.host is not set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } })); + setMockValues({ config: { host: '' } }); const wrapper = shallow(); expect(wrapper.find(AppSearchUnconfigured)).toHaveLength(1); }); it('renders AppSearchConfigured when config.host set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'some.url' } })); + setMockValues({ config: { host: 'some.url' } }); const wrapper = shallow(); expect(wrapper.find(AppSearchConfigured)).toHaveLength(1); @@ -46,8 +47,8 @@ describe('AppSearchUnconfigured', () => { describe('AppSearchConfigured', () => { beforeEach(() => { // Mock resets - (useValues as jest.Mock).mockImplementation(() => ({ myRole: {} })); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData: () => {} })); + setMockValues({ myRole: {} }); + setMockActions({ initializeAppData: () => {} }); }); it('renders with layout', () => { @@ -60,7 +61,7 @@ describe('AppSearchConfigured', () => { it('initializes app data with passed props', () => { const initializeAppData = jest.fn(); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData })); + setMockActions({ initializeAppData }); shallow(); @@ -69,8 +70,8 @@ describe('AppSearchConfigured', () => { it('does not re-initialize app data', () => { const initializeAppData = jest.fn(); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData })); - (useValues as jest.Mock).mockImplementation(() => ({ myRole: {}, hasInitialized: true })); + setMockActions({ initializeAppData }); + setMockValues({ myRole: {}, hasInitialized: true }); shallow(); @@ -78,7 +79,7 @@ describe('AppSearchConfigured', () => { }); it('renders ErrorConnecting', () => { - (useValues as jest.Mock).mockImplementation(() => ({ myRole: {}, errorConnecting: true })); + setMockValues({ myRole: {}, errorConnecting: true }); const wrapper = shallow(); @@ -86,7 +87,7 @@ describe('AppSearchConfigured', () => { }); it('passes readOnlyMode state', () => { - (useValues as jest.Mock).mockImplementation(() => ({ myRole: {}, readOnlyMode: true })); + setMockValues({ myRole: {}, readOnlyMode: true }); const wrapper = shallow(); @@ -108,9 +109,7 @@ describe('AppSearchNav', () => { }); it('renders the Settings link', () => { - (useValues as jest.Mock).mockImplementation(() => ({ - myRole: { canViewSettings: true }, - })); + setMockValues({ myRole: { canViewSettings: true } }); const wrapper = shallow(); expect(wrapper.find(SideNavLink).last().prop('to')).toEqual( @@ -119,9 +118,7 @@ describe('AppSearchNav', () => { }); it('renders the Credentials link', () => { - (useValues as jest.Mock).mockImplementation(() => ({ - myRole: { canViewAccountCredentials: true }, - })); + setMockValues({ myRole: { canViewAccountCredentials: true } }); const wrapper = shallow(); expect(wrapper.find(SideNavLink).last().prop('to')).toEqual( @@ -130,9 +127,7 @@ describe('AppSearchNav', () => { }); it('renders the Role Mappings link', () => { - (useValues as jest.Mock).mockImplementation(() => ({ - myRole: { canViewRoleMappings: true }, - })); + setMockValues({ myRole: { canViewRoleMappings: true } }); const wrapper = shallow(); expect(wrapper.find(SideNavLink).last().prop('to')).toEqual( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx index c848415daf612..9aa2cce9c74df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { Route, Redirect, Switch } from 'react-router-dom'; import { useActions, useValues } from 'kea'; import { i18n } from '@kbn/i18n'; -import { KibanaContext, IKibanaContext } from '../index'; +import { getAppSearchUrl } from '../shared/enterprise_search_url'; +import { KibanaLogic } from '../shared/kibana'; import { HttpLogic } from '../shared/http'; import { AppLogic } from './app_logic'; import { IInitialAppData } from '../../../common/types'; @@ -33,7 +34,7 @@ import { NotFound } from '../shared/not_found'; import { EngineOverview } from './components/engine_overview'; export const AppSearch: React.FC = (props) => { - const { config } = useContext(KibanaContext) as IKibanaContext; + const { config } = useValues(KibanaLogic); return !config.host ? : ; }; @@ -86,10 +87,6 @@ export const AppSearchConfigured: React.FC = (props) => { }; export const AppSearchNav: React.FC = () => { - const { - externalUrl: { getAppSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; - const { myRole: { canViewSettings, canViewAccountCredentials, canViewRoleMappings }, } = useValues(AppLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/types.ts index 3cabc1051c74a..568a0a3365982 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/types.ts @@ -6,3 +6,10 @@ export * from '../../../common/types/app_search'; export { IRole, TRole, TAbility } from './utils/role'; + +export interface IEngine { + name: string; + type: string; + language: string; + result_fields: object[]; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/index.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/index.test.ts new file mode 100644 index 0000000000000..352ff237e4f08 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/index.test.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { formatApiName } from '.'; + +describe('formatApiName', () => { + it('replaces non-alphanumeric characters with dashes', () => { + expect(formatApiName('f1 &&o$ 1 2 *&%da')).toEqual('f1-o-1-2-da'); + }); + + it('strips leading and trailing non-alphanumeric characters', () => { + expect(formatApiName('$$hello world**')).toEqual('hello-world'); + }); + + it('strips leading and trailing whitespace', () => { + expect(formatApiName(' test ')).toEqual('test'); + }); + + it('lowercases text', () => { + expect(formatApiName('SomeName')).toEqual('somename'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/index.ts new file mode 100644 index 0000000000000..cd1b1cfe15637 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/utils/format_api_name/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. + */ + +export const formatApiName = (rawName: string) => + rawName + .trim() + .replace(/[^a-zA-Z0-9]+/g, '-') // Replace all special/non-alphanumerical characters with dashes + .replace(/^[-]+|[-]+$/g, '') // Strip all leading and trailing dashes + .toLowerCase(); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.test.tsx new file mode 100644 index 0000000000000..8d48875a8e1f5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.test.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { ErrorStatePrompt } from '../../../shared/error_state'; +import { ErrorConnecting } from './'; + +describe('ErrorConnecting', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ErrorStatePrompt)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.tsx new file mode 100644 index 0000000000000..567c77792583d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/error_connecting.tsx @@ -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 React from 'react'; +import { EuiPage, EuiPageContent } from '@elastic/eui'; + +import { ErrorStatePrompt } from '../../../shared/error_state'; + +export const ErrorConnecting: React.FC = () => ( + + + + + +); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/index.ts new file mode 100644 index 0000000000000..c8b71e1a6e791 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/error_connecting/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 { ErrorConnecting } from './error_connecting'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx index a76b654ccddd0..b2030ec910cd8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.test.tsx @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../../__mocks__/kea.mock'; + import React from 'react'; +import { useValues } from 'kea'; import { shallow } from 'enzyme'; import { EuiCard } from '@elastic/eui'; @@ -32,7 +35,7 @@ describe('ProductCard', () => { const button = card.find(EuiButton); expect(button.prop('to')).toEqual('/app/enterprise_search/app_search'); - expect(button.prop('data-test-subj')).toEqual('LaunchAppSearchButton'); + expect(button.prop('children')).toEqual('Launch App Search'); button.simulate('click'); expect(sendTelemetry).toHaveBeenCalledWith(expect.objectContaining({ metric: 'app_search' })); @@ -47,11 +50,21 @@ describe('ProductCard', () => { const button = card.find(EuiButton); expect(button.prop('to')).toEqual('/app/enterprise_search/workplace_search'); - expect(button.prop('data-test-subj')).toEqual('LaunchWorkplaceSearchButton'); + expect(button.prop('children')).toEqual('Launch Workplace Search'); button.simulate('click'); expect(sendTelemetry).toHaveBeenCalledWith( expect.objectContaining({ metric: 'workplace_search' }) ); }); + + it('renders correct button text when host not present', () => { + (useValues as jest.Mock).mockImplementation(() => ({ config: { host: '' } })); + + const wrapper = shallow(); + const card = wrapper.find(EuiCard).dive().shallow(); + const button = card.find(EuiButton); + + expect(button.prop('children')).toEqual('Setup Workplace Search'); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx index 334ca126cabb9..1d05128adc2e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_card/product_card.tsx @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; -import upperFirst from 'lodash/upperFirst'; -import snakeCase from 'lodash/snakeCase'; +import React from 'react'; +import { useValues } from 'kea'; +import { snakeCase } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiCard, EuiTextColor } from '@elastic/eui'; import { EuiButton } from '../../../shared/react_router_helpers'; import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { KibanaLogic } from '../../../shared/kibana'; import './product_card.scss'; @@ -28,7 +29,24 @@ interface IProductCard { } export const ProductCard: React.FC = ({ product, image }) => { - const { http } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); + const { config } = useValues(KibanaLogic); + + const LAUNCH_BUTTON_TEXT = i18n.translate( + 'xpack.enterpriseSearch.overview.productCard.launchButton', + { + defaultMessage: 'Launch {productName}', + values: { productName: product.NAME }, + } + ); + + const SETUP_BUTTON_TEXT = i18n.translate( + 'xpack.enterpriseSearch.overview.productCard.setupButton', + { + defaultMessage: 'Setup {productName}', + values: { productName: product.NAME }, + } + ); return ( = ({ product, image }) => { metric: snakeCase(product.ID), }) } - data-test-subj={`Launch${upperFirst(product.ID)}Button`} > - {i18n.translate('xpack.enterpriseSearch.overview.productCard.button', { - defaultMessage: `Launch {productName}`, - values: { productName: product.NAME }, - })} + {config.host ? LAUNCH_BUTTON_TEXT : SETUP_BUTTON_TEXT} } /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/index.ts new file mode 100644 index 0000000000000..b67d130cd68f0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/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 { ProductSelector } from './product_selector'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.tsx new file mode 100644 index 0000000000000..f1f16d1a6f7a4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.test.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 '../../../__mocks__/kea.mock'; + +import React from 'react'; +import { useValues } from 'kea'; +import { shallow } from 'enzyme'; +import { EuiPage } from '@elastic/eui'; + +import { ProductSelector } from './'; +import { ProductCard } from '../product_card'; + +describe('ProductSelector', () => { + it('renders the overview page and product cards with no host set', () => { + (useValues as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } })); + const wrapper = shallow(); + + expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); + expect(wrapper.find(ProductCard)).toHaveLength(2); + }); + + describe('access checks when host is set', () => { + beforeEach(() => { + (useValues as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'localhost' } })); + }); + + it('does not render the App Search card if the user does not have access to AS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('workplaceSearch'); + }); + + it('does not render the Workplace Search card if the user does not have access to WS', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(ProductCard)).toHaveLength(1); + expect(wrapper.find(ProductCard).prop('product').ID).toEqual('appSearch'); + }); + + it('does not render any cards if the user does not have access', () => { + const wrapper = shallow(); + + expect(wrapper.find(ProductCard)).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx new file mode 100644 index 0000000000000..5c2d105e69c40 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/product_selector/product_selector.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 from 'react'; +import { useValues } from 'kea'; +import { + EuiPage, + EuiPageBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPageContentBody, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { KibanaLogic } from '../../../shared/kibana'; +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; + +import { ProductCard } from '../product_card'; + +import AppSearchImage from '../../assets/app_search.png'; +import WorkplaceSearchImage from '../../assets/workplace_search.png'; + +interface IProductSelectorProps { + access: { + hasAppSearchAccess?: boolean; + hasWorkplaceSearchAccess?: boolean; + }; +} + +export const ProductSelector: React.FC = ({ access }) => { + const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; + const { config } = useValues(KibanaLogic); + + // If Enterprise Search hasn't been set up yet, show all products. Otherwise, only show products the user has access to + const shouldShowAppSearchCard = !config.host || hasAppSearchAccess; + const shouldShowWorkplaceSearchCard = !config.host || hasWorkplaceSearchAccess; + + return ( + + + + + + + + +

+ {i18n.translate('xpack.enterpriseSearch.overview.heading', { + defaultMessage: 'Welcome to Elastic Enterprise Search', + })} +

+
+ +

+ {i18n.translate('xpack.enterpriseSearch.overview.subheading', { + defaultMessage: 'Select a product to get started', + })} +

+
+
+
+ + + {shouldShowAppSearchCard && ( + + + + )} + {shouldShowWorkplaceSearchCard && ( + + + + )} + + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png new file mode 100644 index 0000000000000..f0fcb432c29e1 Binary files /dev/null and b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/assets/getting_started.png differ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/index.ts new file mode 100644 index 0000000000000..c367424d375f9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/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 { SetupGuide } from './setup_guide'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.test.tsx new file mode 100644 index 0000000000000..63b0cc5a56cd1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.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 React from 'react'; +import { shallow } from 'enzyme'; + +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SetupGuide as SetupGuideLayout } from '../../../shared/setup_guide'; +import { SetupGuide } from './'; + +describe('SetupGuide', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SetupGuideLayout)).toHaveLength(1); + expect(wrapper.find(SetPageChrome)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx new file mode 100644 index 0000000000000..fcb3b399c75b0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/components/setup_guide/setup_guide.tsx @@ -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 React from 'react'; +import { EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { ENTERPRISE_SEARCH_PLUGIN } from '../../../../../common/constants'; +import { SetupGuide as SetupGuideLayout } from '../../../shared/setup_guide'; +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; +import GettingStarted from './assets/getting_started.png'; + +export const SetupGuide: React.FC = () => ( + + + + + + {i18n.translate('xpack.enterpriseSearch.enterpriseSearch.setupGuide.videoAlt', + + + +

+ +

+
+ + +

+ +

+
+
+); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx index cd2a22a45bbb4..803d2c8462b1b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.test.tsx @@ -6,45 +6,37 @@ import React from 'react'; import { shallow } from 'enzyme'; - import { EuiPage } from '@elastic/eui'; +import '../__mocks__/kea.mock'; +import { useValues } from 'kea'; + import { EnterpriseSearch } from './'; -import { ProductCard } from './components/product_card'; +import { SetupGuide } from './components/setup_guide'; +import { ErrorConnecting } from './components/error_connecting'; +import { ProductSelector } from './components/product_selector'; describe('EnterpriseSearch', () => { - it('renders the overview page and product cards', () => { - const wrapper = shallow( - - ); - - expect(wrapper.find(EuiPage).hasClass('enterpriseSearchOverview')).toBe(true); - expect(wrapper.find(ProductCard)).toHaveLength(2); - }); - - describe('access checks', () => { - it('does not render the App Search card if the user does not have access to AS', () => { - const wrapper = shallow( - - ); - - expect(wrapper.find(ProductCard)).toHaveLength(1); - expect(wrapper.find(ProductCard).prop('product').ID).toEqual('workplaceSearch'); + it('renders the Setup Guide and Product Selector', () => { + (useValues as jest.Mock).mockReturnValue({ + errorConnecting: false, + config: { host: 'localhost' }, }); + const wrapper = shallow(); - it('does not render the Workplace Search card if the user does not have access to WS', () => { - const wrapper = shallow( - - ); + expect(wrapper.find(SetupGuide)).toHaveLength(1); + expect(wrapper.find(ProductSelector)).toHaveLength(1); + }); - expect(wrapper.find(ProductCard)).toHaveLength(1); - expect(wrapper.find(ProductCard).prop('product').ID).toEqual('appSearch'); + it('renders the error connecting prompt when host is not configured', () => { + (useValues as jest.Mock).mockReturnValueOnce({ + errorConnecting: true, + config: { host: '' }, }); + const wrapper = shallow(); - it('does not render any cards if the user does not have access', () => { - const wrapper = shallow(); - - expect(wrapper.find(ProductCard)).toHaveLength(0); - }); + expect(wrapper.find(ErrorConnecting)).toHaveLength(1); + expect(wrapper.find(EuiPage)).toHaveLength(0); + expect(wrapper.find(ProductSelector)).toHaveLength(0); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx index 373f595a6a9ea..7b97c6c9e58b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/index.tsx @@ -5,74 +5,36 @@ */ import React from 'react'; -import { - EuiPage, - EuiPageBody, - EuiPageHeader, - EuiPageHeaderSection, - EuiPageContentBody, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; +import { Route, Switch } from 'react-router-dom'; +import { useValues } from 'kea'; +import { KibanaLogic } from '../shared/kibana'; import { IInitialAppData } from '../../../common/types'; -import { APP_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN } from '../../../common/constants'; -import { SetEnterpriseSearchChrome as SetPageChrome } from '../shared/kibana_chrome'; -import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../shared/telemetry'; +import { HttpLogic } from '../shared/http'; -import { ProductCard } from './components/product_card'; +import { ROOT_PATH, SETUP_GUIDE_PATH } from './routes'; + +import { ErrorConnecting } from './components/error_connecting'; +import { ProductSelector } from './components/product_selector'; +import { SetupGuide } from './components/setup_guide'; -import AppSearchImage from './assets/app_search.png'; -import WorkplaceSearchImage from './assets/workplace_search.png'; import './index.scss'; export const EnterpriseSearch: React.FC = ({ access = {} }) => { - const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; + const { errorConnecting } = useValues(HttpLogic); + const { config } = useValues(KibanaLogic); - return ( - - - + const showErrorConnecting = config.host && errorConnecting; - - - - -

- {i18n.translate('xpack.enterpriseSearch.overview.heading', { - defaultMessage: 'Welcome to Elastic Enterprise Search', - })} -

-
- -

- {i18n.translate('xpack.enterpriseSearch.overview.subheading', { - defaultMessage: 'Select a product to get started', - })} -

-
-
-
- - - {hasAppSearchAccess && ( - - - - )} - {hasWorkplaceSearchAccess && ( - - - - )} - - - -
-
+ return ( + + + + + + {showErrorConnecting ? : } + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.ts new file mode 100644 index 0000000000000..1f9c06e9683ab --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search/routes.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 const ROOT_PATH = '/'; +export const SETUP_GUIDE_PATH = '/setup_guide'; diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index 053c450ab925e..d5b9513d0dbb3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -5,59 +5,96 @@ */ import React from 'react'; +import { getContext } from 'kea'; -import { AppMountParameters } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; import { licensingMock } from '../../../licensing/public/mocks'; import { renderApp, renderHeaderActions } from './'; +import { EnterpriseSearch } from './enterprise_search'; import { AppSearch } from './app_search'; import { WorkplaceSearch } from './workplace_search'; +import { KibanaLogic } from './shared/kibana'; describe('renderApp', () => { - let params: AppMountParameters; - const core = coreMock.createStart(); - const plugins = { - licensing: licensingMock.createSetup(), + const kibanaDeps = { + params: coreMock.createAppMountParamters(), + core: coreMock.createStart(), + plugins: { licensing: licensingMock.createStart() }, + } as any; + const pluginData = { + config: {}, + data: {}, } as any; - const config = {}; - const data = {} as any; beforeEach(() => { jest.clearAllMocks(); - params = coreMock.createAppMountParamters(); }); - it('mounts and unmounts UI', () => { - const MockApp = () =>
Hello world!
; + const mockContainer = kibanaDeps.params.element; + const MockApp = () =>
Hello world!
; - const unmount = renderApp(MockApp, params, core, plugins, config, data); - expect(params.element.querySelector('.hello-world')).not.toBeNull(); + it('mounts and unmounts UI', () => { + const unmount = renderApp(MockApp, kibanaDeps, pluginData); + expect(mockContainer.querySelector('.hello-world')).not.toBeNull(); unmount(); - expect(params.element.innerHTML).toEqual(''); + expect(mockContainer.innerHTML).toEqual(''); }); - it('renders AppSearch', () => { - renderApp(AppSearch, params, core, plugins, config, data); - expect(params.element.querySelector('.setupGuide')).not.toBeNull(); - }); + /** + * Helper for automatically mounting and unmounting future tests + */ + let unmount: any; + const mount = (App: React.FC) => { + unmount = renderApp(App, kibanaDeps, pluginData); + }; + + describe('Enterprise Search apps', () => { + afterEach(() => unmount()); + + it('renders EnterpriseSearch', () => { + mount(EnterpriseSearch); + expect(mockContainer.querySelector('.enterpriseSearchOverview')).not.toBeNull(); + }); + + it('renders AppSearch', () => { + mount(AppSearch); + expect(mockContainer.querySelector('.setupGuide')).not.toBeNull(); + }); - it('renders WorkplaceSearch', () => { - renderApp(WorkplaceSearch, params, core, plugins, config, data); - expect(params.element.querySelector('.setupGuide')).not.toBeNull(); + it('renders WorkplaceSearch', () => { + mount(WorkplaceSearch); + expect(mockContainer.querySelector('.setupGuide')).not.toBeNull(); + }); }); -}); -describe('renderHeaderActions', () => { - it('mounts and unmounts any HeaderActions component', () => { + describe('renderHeaderActions', () => { const mockHeaderEl = document.createElement('header'); const MockHeaderActions = () => ; - const unmount = renderHeaderActions(MockHeaderActions, mockHeaderEl, {} as any); - expect(mockHeaderEl.querySelector('.hello-world')).not.toBeNull(); + it('mounts and unmounts any HeaderActions component', () => { + const store = getContext().store; - unmount(); - expect(mockHeaderEl.innerHTML).toEqual(''); + const unmountHeader = renderHeaderActions(MockHeaderActions, store, mockHeaderEl); + expect(mockHeaderEl.querySelector('.hello-world')).not.toBeNull(); + + unmountHeader(); + expect(mockHeaderEl.innerHTML).toEqual(''); + }); + + it('passes a renderHeaderActions helper to KibanaLogic, which can be used by our apps to render HeaderActions', () => { + // Setup + kibanaDeps.params.setHeaderActionMenu.mockImplementationOnce((cb: any) => cb(mockHeaderEl)); + mount(MockApp); + + // Call KibanaLogic's renderHeaderActions, which should call params.setHeaderActionMenu + KibanaLogic.values.renderHeaderActions(MockHeaderActions); + expect(kibanaDeps.params.setHeaderActionMenu).toHaveBeenCalled(); + + // renderHeaderActions should have been called and generated the correct DOM + expect(mockHeaderEl.querySelector('.hello-world')).not.toBeNull(); + unmount(); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 43056f2f65538..b9c94e351089d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -7,36 +7,20 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Router } from 'react-router-dom'; - import { Provider } from 'react-redux'; import { Store } from 'redux'; import { getContext, resetContext } from 'kea'; - import { I18nProvider } from '@kbn/i18n/react'; -import { - AppMountParameters, - CoreStart, - ApplicationStart, - HttpSetup, - ChromeBreadcrumb, -} from 'src/core/public'; -import { ClientConfigType, ClientData, PluginsSetup } from '../plugin'; -import { LicenseProvider } from './shared/licensing'; -import { FlashMessagesProvider } from './shared/flash_messages'; -import { HttpProvider } from './shared/http'; -import { IExternalUrl } from './shared/enterprise_search_url'; -import { IInitialAppData } from '../../common/types'; -export interface IKibanaContext { - config: { host?: string }; - externalUrl: IExternalUrl; - http: HttpSetup; - navigateToUrl: ApplicationStart['navigateToUrl']; - setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void; - setDocTitle(title: string): void; -} +import { AppMountParameters, CoreStart } from 'src/core/public'; +import { PluginsStart, ClientConfigType, ClientData } from '../plugin'; +import { IInitialAppData } from '../../common/types'; -export const KibanaContext = React.createContext({}); +import { mountKibanaLogic } from './shared/kibana'; +import { mountLicensingLogic } from './shared/licensing'; +import { mountHttpLogic } from './shared/http'; +import { mountFlashMessagesLogic } from './shared/flash_messages'; +import { externalUrl } from './shared/enterprise_search_url'; /** * This file serves as a reusable wrapper to share Kibana-level context and other helpers @@ -46,46 +30,50 @@ export const KibanaContext = React.createContext({}); export const renderApp = ( App: React.FC, - params: AppMountParameters, - core: CoreStart, - plugins: PluginsSetup, - config: ClientConfigType, - { externalUrl, errorConnecting, ...initialData }: ClientData + { params, core, plugins }: { params: AppMountParameters; core: CoreStart; plugins: PluginsStart }, + { config, data }: { config: ClientConfigType; data: ClientData } ) => { + const { publicUrl, errorConnecting, ...initialData } = data; + externalUrl.enterpriseSearchUrl = publicUrl || config.host || ''; + resetContext({ createStore: true }); - const store = getContext().store as Store; + const store = getContext().store; + + const unmountKibanaLogic = mountKibanaLogic({ + config, + history: params.history, + navigateToUrl: core.application.navigateToUrl, + setBreadcrumbs: core.chrome.setBreadcrumbs, + setDocTitle: core.chrome.docTitle.change, + renderHeaderActions: (HeaderActions) => + params.setHeaderActionMenu((el) => renderHeaderActions(HeaderActions, store, el)), + }); + const unmountLicensingLogic = mountLicensingLogic({ + license$: plugins.licensing.license$, + }); + const unmountHttpLogic = mountHttpLogic({ + http: core.http, + errorConnecting, + readOnlyMode: initialData.readOnlyMode, + }); + const unmountFlashMessagesLogic = mountFlashMessagesLogic(); ReactDOM.render( - - - - - - - - - - - + + + + + , params.element ); return () => { ReactDOM.unmountComponentAtNode(params.element); + unmountKibanaLogic(); + unmountLicensingLogic(); + unmountHttpLogic(); + unmountFlashMessagesLogic(); }; }; @@ -95,15 +83,17 @@ export const renderApp = ( * a custom HeaderActions component (e.g., WorkplaceSearchHeaderActions) * @see https://github.com/elastic/kibana/blob/master/docs/development/core/public/kibana-plugin-core-public.appmountparameters.setheaderactionmenu.md */ -interface IHeaderActionsProps { - externalUrl: IExternalUrl; -} export const renderHeaderActions = ( - HeaderActions: React.FC, - kibanaHeaderEl: HTMLElement, - externalUrl: IExternalUrl + HeaderActions: React.FC, + store: Store, + kibanaHeaderEl: HTMLElement ) => { - ReactDOM.render(, kibanaHeaderEl); + ReactDOM.render( + + + , + kibanaHeaderEl + ); return () => ReactDOM.unmountComponentAtNode(kibanaHeaderEl); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.test.ts new file mode 100644 index 0000000000000..55c4f465d9ed4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.test.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { externalUrl, getEnterpriseSearchUrl, getAppSearchUrl, getWorkplaceSearchUrl } from './'; + +describe('Enterprise Search external URL helpers', () => { + describe('getter/setter tests', () => { + it('defaults to an empty string', () => { + expect(externalUrl.enterpriseSearchUrl).toEqual(''); + }); + + it('sets the internal enterpriseSearchUrl value', () => { + externalUrl.enterpriseSearchUrl = 'http://localhost:3002'; + expect(externalUrl.enterpriseSearchUrl).toEqual('http://localhost:3002'); + }); + + it('does not allow mutating enterpriseSearchUrl once set', () => { + externalUrl.enterpriseSearchUrl = 'hello world'; + expect(externalUrl.enterpriseSearchUrl).toEqual('http://localhost:3002'); + }); + }); + + describe('function helpers', () => { + it('generates a public Enterprise Search URL', () => { + expect(getEnterpriseSearchUrl()).toEqual('http://localhost:3002'); + expect(getEnterpriseSearchUrl('/login')).toEqual('http://localhost:3002/login'); + }); + + it('generates a public App Search URL', () => { + expect(getAppSearchUrl()).toEqual('http://localhost:3002/as'); + expect(getAppSearchUrl('/path')).toEqual('http://localhost:3002/as/path'); + }); + + it('generates a public Workplace Search URL', () => { + expect(getWorkplaceSearchUrl()).toEqual('http://localhost:3002/ws'); + expect(getWorkplaceSearchUrl('/path')).toEqual('http://localhost:3002/ws/path'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.ts b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.ts new file mode 100644 index 0000000000000..80b506f31ad61 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/external_url.ts @@ -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. + */ + +/** + * NOTE: The externalUrl obj holds the reference to externalUrl, which should + * only ever be updated once on plugin init. We're using a getter and setter + * here to ensure it isn't accidentally mutated. + * + * Someday (8.x+), when our UI is entirely on Kibana and no longer on + * Enterprise Search's standalone UI, we can potentially deprecate this helper. + */ +export const externalUrl = { + _enterpriseSearchUrl: '', + get enterpriseSearchUrl() { + return this._enterpriseSearchUrl; + }, + set enterpriseSearchUrl(value) { + if (this._enterpriseSearchUrl) { + // enterpriseSearchUrl is set once on plugin init - we should not mutate it + return; + } + this._enterpriseSearchUrl = value; + }, +}; + +export const getEnterpriseSearchUrl = (path: string = ''): string => { + return externalUrl.enterpriseSearchUrl + path; +}; +export const getAppSearchUrl = (path: string = ''): string => { + return getEnterpriseSearchUrl('/as' + path); +}; +export const getWorkplaceSearchUrl = (path: string = ''): string => { + return getEnterpriseSearchUrl('/ws' + path); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.test.ts deleted file mode 100644 index 1092c88cbbc11..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.test.ts +++ /dev/null @@ -1,25 +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 { ExternalUrl } from './'; - -describe('Enterprise Search external URL helper', () => { - const externalUrl = new ExternalUrl('http://localhost:3002'); - - it('exposes a public enterpriseSearchUrl string', () => { - expect(externalUrl.enterpriseSearchUrl).toEqual('http://localhost:3002'); - }); - - it('generates a public App Search URL', () => { - expect(externalUrl.getAppSearchUrl()).toEqual('http://localhost:3002/as'); - expect(externalUrl.getAppSearchUrl('/path')).toEqual('http://localhost:3002/as/path'); - }); - - it('generates a public Workplace Search URL', () => { - expect(externalUrl.getWorkplaceSearchUrl()).toEqual('http://localhost:3002/ws'); - expect(externalUrl.getWorkplaceSearchUrl('/path')).toEqual('http://localhost:3002/ws/path'); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.ts b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.ts deleted file mode 100644 index 9db48d197f3bc..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/generate_external_url.ts +++ /dev/null @@ -1,38 +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. - */ - -/** - * Small helper for generating external public-facing URLs - * to the legacy/standalone Enterprise Search app - */ -export interface IExternalUrl { - enterpriseSearchUrl?: string; - getAppSearchUrl(path?: string): string; - getWorkplaceSearchUrl(path?: string): string; -} - -export class ExternalUrl { - public enterpriseSearchUrl: string; - - constructor(externalUrl: string) { - this.enterpriseSearchUrl = externalUrl; - - this.getAppSearchUrl = this.getAppSearchUrl.bind(this); - this.getWorkplaceSearchUrl = this.getWorkplaceSearchUrl.bind(this); - } - - private getExternalUrl(path: string): string { - return this.enterpriseSearchUrl + path; - } - - public getAppSearchUrl(path: string = ''): string { - return this.getExternalUrl('/as' + path); - } - - public getWorkplaceSearchUrl(path: string = ''): string { - return this.getExternalUrl('/ws' + path); - } -} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/index.ts index d2d82a43c6dd9..177d8e0535c72 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/enterprise_search_url/index.ts @@ -4,4 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ExternalUrl, IExternalUrl } from './generate_external_url'; +export { + externalUrl, + getEnterpriseSearchUrl, + getAppSearchUrl, + getWorkplaceSearchUrl, +} from './external_url'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.test.tsx index 29b773b80158a..25a02e847ccbd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/kea.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx index a2cb424dadee8..b92a5bbf1c64e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/error_state/error_state_prompt.tsx @@ -4,17 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton } from '../react_router_helpers'; -import { KibanaContext, IKibanaContext } from '../../index'; +import { KibanaLogic } from '../../shared/kibana'; import './error_state_prompt.scss'; export const ErrorStatePrompt: React.FC = () => { - const { config } = useContext(KibanaContext) as IKibanaContext; + const { config } = useValues(KibanaLogic); return ( ({ + KibanaLogic: { values: { history: mockHistory } }, +})); + +import { FlashMessagesLogic, mountFlashMessagesLogic, IFlashMessage } from './'; describe('FlashMessagesLogic', () => { - const DEFAULT_VALUES = { - messages: [], - queuedMessages: [], - historyListener: null, - }; + const mount = () => mountFlashMessagesLogic(); beforeEach(() => { jest.clearAllMocks(); resetContext({}); }); - it('has expected default values', () => { - FlashMessagesLogic.mount(); - expect(FlashMessagesLogic.values).toEqual(DEFAULT_VALUES); + it('has default values', () => { + mount(); + expect(FlashMessagesLogic.values).toEqual({ + messages: [], + queuedMessages: [], + historyListener: expect.any(Function), + }); }); describe('setFlashMessages()', () => { @@ -33,7 +38,7 @@ describe('FlashMessagesLogic', () => { { type: 'info', message: 'Everything is fine, nothing is ruined' }, ]; - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setFlashMessages(messages); expect(FlashMessagesLogic.values.messages).toEqual(messages); @@ -42,7 +47,7 @@ describe('FlashMessagesLogic', () => { it('automatically converts to an array if a single message obj is passed in', () => { const message = { type: 'success', message: 'I turn into an array!' } as IFlashMessage; - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setFlashMessages(message); expect(FlashMessagesLogic.values.messages).toEqual([message]); @@ -51,7 +56,7 @@ describe('FlashMessagesLogic', () => { describe('clearFlashMessages()', () => { it('sets messages back to an empty array', () => { - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setFlashMessages('test' as any); FlashMessagesLogic.actions.clearFlashMessages(); @@ -63,7 +68,7 @@ describe('FlashMessagesLogic', () => { it('sets an array of messages', () => { const queuedMessage: IFlashMessage = { type: 'error', message: 'You deleted a thing' }; - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setQueuedMessages(queuedMessage); expect(FlashMessagesLogic.values.queuedMessages).toEqual([queuedMessage]); @@ -72,7 +77,7 @@ describe('FlashMessagesLogic', () => { describe('clearQueuedMessages()', () => { it('sets queued messages back to an empty array', () => { - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setQueuedMessages('test' as any); FlashMessagesLogic.actions.clearQueuedMessages(); @@ -83,30 +88,25 @@ describe('FlashMessagesLogic', () => { describe('history listener logic', () => { describe('setHistoryListener()', () => { it('sets the historyListener value', () => { - FlashMessagesLogic.mount(); + mount(); FlashMessagesLogic.actions.setHistoryListener('test' as any); expect(FlashMessagesLogic.values.historyListener).toEqual('test'); }); }); - describe('listenToHistory()', () => { + describe('on mount', () => { it('listens for history changes and clears messages on change', () => { - FlashMessagesLogic.mount(); + mount(); + expect(mockHistory.listen).toHaveBeenCalled(); + FlashMessagesLogic.actions.setQueuedMessages(['queuedMessages'] as any); jest.spyOn(FlashMessagesLogic.actions, 'clearFlashMessages'); jest.spyOn(FlashMessagesLogic.actions, 'setFlashMessages'); jest.spyOn(FlashMessagesLogic.actions, 'clearQueuedMessages'); jest.spyOn(FlashMessagesLogic.actions, 'setHistoryListener'); - const mockListener = jest.fn(() => jest.fn()); - const history = { listen: mockListener } as any; - FlashMessagesLogic.actions.listenToHistory(history); - - expect(mockListener).toHaveBeenCalled(); - expect(FlashMessagesLogic.actions.setHistoryListener).toHaveBeenCalled(); - - const mockHistoryChange = (mockListener.mock.calls[0] as any)[0]; + const mockHistoryChange = (mockHistory.listen.mock.calls[0] as any)[0]; mockHistoryChange(); expect(FlashMessagesLogic.actions.clearFlashMessages).toHaveBeenCalled(); expect(FlashMessagesLogic.actions.setFlashMessages).toHaveBeenCalledWith([ @@ -116,19 +116,20 @@ describe('FlashMessagesLogic', () => { }); }); - describe('beforeUnmount', () => { - it('removes history listener on unmount', () => { + describe('on unmount', () => { + it('removes history listener', () => { const mockUnlistener = jest.fn(); - const unmount = FlashMessagesLogic.mount(); + mockHistory.listen.mockReturnValueOnce(mockUnlistener); - FlashMessagesLogic.actions.setHistoryListener(mockUnlistener); + const unmount = mount(); unmount(); expect(mockUnlistener).toHaveBeenCalled(); }); it('does not crash if no listener exists', () => { - const unmount = FlashMessagesLogic.mount(); + const unmount = mount(); + FlashMessagesLogic.actions.setHistoryListener(null as any); unmount(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.ts index 37a8f16acad6d..5a05a03adeb6b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_logic.ts @@ -6,7 +6,8 @@ import { kea, MakeLogicType } from 'kea'; import { ReactNode } from 'react'; -import { History } from 'history'; + +import { KibanaLogic } from '../kibana'; export interface IFlashMessage { type: 'success' | 'info' | 'warning' | 'error'; @@ -24,7 +25,6 @@ export interface IFlashMessagesActions { clearFlashMessages(): void; setQueuedMessages(messages: IFlashMessage | IFlashMessage[]): { messages: IFlashMessage[] }; clearQueuedMessages(): void; - listenToHistory(history: History): History; setHistoryListener(historyListener: Function): { historyListener: Function }; } @@ -38,7 +38,6 @@ export const FlashMessagesLogic = kea null, setQueuedMessages: (messages) => ({ messages: convertToArray(messages) }), clearQueuedMessages: () => null, - listenToHistory: (history) => history, setHistoryListener: (historyListener) => ({ historyListener }), }, reducers: { @@ -63,21 +62,27 @@ export const FlashMessagesLogic = kea ({ - listenToHistory: (history) => { + events: ({ values, actions }) => ({ + afterMount: () => { // On React Router navigation, clear previous flash messages and load any queued messages - const unlisten = history.listen(() => { + const unlisten = KibanaLogic.values.history.listen(() => { actions.clearFlashMessages(); actions.setFlashMessages(values.queuedMessages); actions.clearQueuedMessages(); }); actions.setHistoryListener(unlisten); }, - }), - events: ({ values }) => ({ beforeUnmount: () => { const { historyListener: removeHistoryListener } = values; if (removeHistoryListener) removeHistoryListener(); }, }), }); + +/** + * Mount/props helper + */ +export const mountFlashMessagesLogic = () => { + const unmount = FlashMessagesLogic.mount(); + return unmount; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.test.tsx deleted file mode 100644 index bcd7abd6d7ce2..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import '../../__mocks__/shallow_usecontext.mock'; -import '../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow } from 'enzyme'; -import { useValues, useActions } from 'kea'; - -import { mockHistory } from '../../__mocks__'; - -import { FlashMessagesProvider } from './'; - -describe('FlashMessagesProvider', () => { - const props = { history: mockHistory as any }; - const listenToHistory = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - (useActions as jest.Mock).mockImplementationOnce(() => ({ listenToHistory })); - }); - - it('does not render', () => { - const wrapper = shallow(); - - expect(wrapper.isEmptyRender()).toBe(true); - }); - - it('listens to history on mount', () => { - shallow(); - - expect(listenToHistory).toHaveBeenCalledWith(mockHistory); - }); - - it('does not add another history listener if one already exists', () => { - (useValues as jest.Mock).mockImplementationOnce(() => ({ historyListener: 'exists' as any })); - - shallow(); - - expect(listenToHistory).not.toHaveBeenCalledWith(props); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.tsx deleted file mode 100644 index a3ceabcf6ac8a..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/flash_messages_provider.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect } from 'react'; -import { useValues, useActions } from 'kea'; -import { History } from 'history'; - -import { FlashMessagesLogic } from './flash_messages_logic'; - -interface IFlashMessagesProviderProps { - history: History; -} - -export const FlashMessagesProvider: React.FC = ({ history }) => { - const { historyListener } = useValues(FlashMessagesLogic); - const { listenToHistory } = useActions(FlashMessagesLogic); - - useEffect(() => { - if (!historyListener) listenToHistory(history); - }, []); - - return null; -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/index.ts index c4daeb44420c8..21c1a60efa6b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/index.ts @@ -10,7 +10,7 @@ export { IFlashMessage, IFlashMessagesValues, IFlashMessagesActions, + mountFlashMessagesLogic, } from './flash_messages_logic'; -export { FlashMessagesProvider } from './flash_messages_provider'; export { flashAPIErrors } from './handle_api_errors'; export { setSuccessMessage, setErrorMessage, setQueuedSuccessMessage } from './set_message_helpers'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts index c3c60d77f4577..46027fdfb22b1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/set_message_helpers.test.ts @@ -4,8 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { mockHistory } from '../../__mocks__'; +jest.mock('../kibana', () => ({ + KibanaLogic: { values: { history: mockHistory } }, +})); + import { FlashMessagesLogic, + mountFlashMessagesLogic, setSuccessMessage, setErrorMessage, setQueuedSuccessMessage, @@ -15,7 +21,7 @@ describe('Flash Message Helpers', () => { const message = 'I am a message'; beforeEach(() => { - FlashMessagesLogic.mount(); + mountFlashMessagesLogic(); }); it('setSuccessMessage()', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts index b65499be2f7c0..df32b5496c367 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts @@ -8,31 +8,20 @@ import { resetContext } from 'kea'; import { httpServiceMock } from 'src/core/public/mocks'; -import { HttpLogic } from './http_logic'; +import { HttpLogic, mountHttpLogic } from './http_logic'; describe('HttpLogic', () => { const mockHttp = httpServiceMock.createSetupContract(); - const DEFAULT_VALUES = { - http: null, - httpInterceptors: [], - errorConnecting: false, - readOnlyMode: false, - }; + const mount = () => mountHttpLogic({ http: mockHttp }); beforeEach(() => { jest.clearAllMocks(); resetContext({}); }); - it('has expected default values', () => { - HttpLogic.mount(); - expect(HttpLogic.values).toEqual(DEFAULT_VALUES); - }); - - describe('initializeHttp()', () => { - it('sets values based on passed props', () => { - HttpLogic.mount(); - HttpLogic.actions.initializeHttp({ + describe('mounts', () => { + it('sets values from props', () => { + mountHttpLogic({ http: mockHttp, errorConnecting: true, readOnlyMode: true, @@ -40,7 +29,7 @@ describe('HttpLogic', () => { expect(HttpLogic.values).toEqual({ http: mockHttp, - httpInterceptors: [], + httpInterceptors: expect.any(Array), errorConnecting: true, readOnlyMode: true, }); @@ -49,7 +38,9 @@ describe('HttpLogic', () => { describe('setErrorConnecting()', () => { it('sets errorConnecting value', () => { - HttpLogic.mount(); + mount(); + expect(HttpLogic.values.errorConnecting).toEqual(false); + HttpLogic.actions.setErrorConnecting(true); expect(HttpLogic.values.errorConnecting).toEqual(true); @@ -60,7 +51,9 @@ describe('HttpLogic', () => { describe('setReadOnlyMode()', () => { it('sets readOnlyMode value', () => { - HttpLogic.mount(); + mount(); + expect(HttpLogic.values.readOnlyMode).toEqual(false); + HttpLogic.actions.setReadOnlyMode(true); expect(HttpLogic.values.readOnlyMode).toEqual(true); @@ -72,10 +65,8 @@ describe('HttpLogic', () => { describe('http interceptors', () => { describe('initializeHttpInterceptors()', () => { beforeEach(() => { - HttpLogic.mount(); + mount(); jest.spyOn(HttpLogic.actions, 'setHttpInterceptors'); - HttpLogic.actions.initializeHttp({ http: mockHttp }); - HttpLogic.actions.initializeHttpInterceptors(); }); it('calls http.intercept and sets an array of interceptors', () => { @@ -165,7 +156,7 @@ describe('HttpLogic', () => { }); it('sets httpInterceptors and calls all valid remove functions on unmount', () => { - const unmount = HttpLogic.mount(); + const unmount = mount(); const httpInterceptors = [jest.fn(), undefined, jest.fn()] as any; HttpLogic.actions.setHttpInterceptors(httpInterceptors); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts index 72380142fe399..d16e507bfb3bc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts @@ -7,7 +7,6 @@ import { kea, MakeLogicType } from 'kea'; import { HttpSetup, HttpInterceptorResponseError, HttpResponse } from 'src/core/public'; -import { IHttpProviderProps } from './http_provider'; import { READ_ONLY_MODE_HEADER } from '../../../../common/constants'; @@ -18,7 +17,6 @@ export interface IHttpValues { readOnlyMode: boolean; } export interface IHttpActions { - initializeHttp({ http, errorConnecting, readOnlyMode }: IHttpProviderProps): IHttpProviderProps; initializeHttpInterceptors(): void; setHttpInterceptors(httpInterceptors: Function[]): { httpInterceptors: Function[] }; setErrorConnecting(errorConnecting: boolean): { errorConnecting: boolean }; @@ -28,19 +26,13 @@ export interface IHttpActions { export const HttpLogic = kea>({ path: ['enterprise_search', 'http_logic'], actions: { - initializeHttp: (props) => props, initializeHttpInterceptors: () => null, setHttpInterceptors: (httpInterceptors) => ({ httpInterceptors }), setErrorConnecting: (errorConnecting) => ({ errorConnecting }), setReadOnlyMode: (readOnlyMode) => ({ readOnlyMode }), }, - reducers: { - http: [ - (null as unknown) as HttpSetup, - { - initializeHttp: (_, { http }) => http, - }, - ], + reducers: ({ props }) => ({ + http: [props.http, {}], httpInterceptors: [ [], { @@ -48,20 +40,18 @@ export const HttpLogic = kea>({ }, ], errorConnecting: [ - false, + props.errorConnecting || false, { - initializeHttp: (_, { errorConnecting }) => !!errorConnecting, setErrorConnecting: (_, { errorConnecting }) => errorConnecting, }, ], readOnlyMode: [ - false, + props.readOnlyMode || false, { - initializeHttp: (_, { readOnlyMode }) => !!readOnlyMode, setReadOnlyMode: (_, { readOnlyMode }) => readOnlyMode, }, ], - }, + }), listeners: ({ values, actions }) => ({ initializeHttpInterceptors: () => { const httpInterceptors = []; @@ -103,7 +93,10 @@ export const HttpLogic = kea>({ actions.setHttpInterceptors(httpInterceptors); }, }), - events: ({ values }) => ({ + events: ({ values, actions }) => ({ + afterMount: () => { + actions.initializeHttpInterceptors(); + }, beforeUnmount: () => { values.httpInterceptors.forEach((removeInterceptorFn?: Function) => { if (removeInterceptorFn) removeInterceptorFn(); @@ -112,6 +105,20 @@ export const HttpLogic = kea>({ }), }); +/** + * Mount/props helper + */ +interface IHttpLogicProps { + http: HttpSetup; + errorConnecting?: boolean; + readOnlyMode?: boolean; +} +export const mountHttpLogic = (props: IHttpLogicProps) => { + HttpLogic(props); + const unmount = HttpLogic.mount(); + return unmount; +}; + /** * Small helper that checks whether or not an http call is for an Enterprise Search API */ diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx deleted file mode 100644 index 902c910f10d7c..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import '../../__mocks__/shallow_usecontext.mock'; -import '../../__mocks__/kea.mock'; - -import React from 'react'; -import { shallow } from 'enzyme'; -import { useActions } from 'kea'; - -import { HttpProvider } from './'; - -describe('HttpProvider', () => { - const props = { - http: {} as any, - errorConnecting: false, - readOnlyMode: false, - }; - const initializeHttp = jest.fn(); - const initializeHttpInterceptors = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - (useActions as jest.Mock).mockImplementationOnce(() => ({ - initializeHttp, - initializeHttpInterceptors, - })); - }); - - it('does not render', () => { - const wrapper = shallow(); - - expect(wrapper.isEmptyRender()).toBe(true); - }); - - it('calls initialization actions on mount', () => { - shallow(); - - expect(initializeHttp).toHaveBeenCalledWith(props); - expect(initializeHttpInterceptors).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx deleted file mode 100644 index db1b0d611079a..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect } from 'react'; -import { useActions } from 'kea'; - -import { HttpSetup } from 'src/core/public'; - -import { HttpLogic } from './http_logic'; - -export interface IHttpProviderProps { - http: HttpSetup; - errorConnecting?: boolean; - readOnlyMode?: boolean; -} - -export const HttpProvider: React.FC = (props) => { - const { initializeHttp, initializeHttpInterceptors } = useActions(HttpLogic); - - useEffect(() => { - initializeHttp(props); - initializeHttpInterceptors(); - }, []); - - return null; -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts index db65e80ca25c2..46a52415f8564 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { HttpLogic, IHttpValues, IHttpActions } from './http_logic'; -export { HttpProvider } from './http_provider'; +export { HttpLogic, IHttpValues, IHttpActions, mountHttpLogic } from './http_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/index.ts new file mode 100644 index 0000000000000..5751dd3a47de2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/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 { KibanaLogic, mountKibanaLogic } from './kibana_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts new file mode 100644 index 0000000000000..4d51362a7e11b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { resetContext } from 'kea'; + +import { mockKibanaValues } from '../../__mocks__'; + +import { KibanaLogic, mountKibanaLogic } from './kibana_logic'; + +describe('KibanaLogic', () => { + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + }); + + describe('mounts', () => { + it('sets values from props', () => { + mountKibanaLogic(mockKibanaValues); + + expect(KibanaLogic.values).toEqual({ + ...mockKibanaValues, + navigateToUrl: expect.any(Function), + }); + }); + + it('gracefully handles missing configs', () => { + mountKibanaLogic({ ...mockKibanaValues, config: undefined } as any); + + expect(KibanaLogic.values.config).toEqual({}); + }); + }); + + describe('navigateToUrl()', () => { + beforeEach(() => mountKibanaLogic(mockKibanaValues)); + + it('runs paths through createHref before calling navigateToUrl', () => { + KibanaLogic.values.navigateToUrl('/test'); + + expect(mockKibanaValues.navigateToUrl).toHaveBeenCalledWith('/app/enterprise_search/test'); + }); + + it('does not run paths through createHref if the shouldNotCreateHref option is passed', () => { + KibanaLogic.values.navigateToUrl('/test', { shouldNotCreateHref: true }); + + expect(mockKibanaValues.navigateToUrl).toHaveBeenCalledWith('/test'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts new file mode 100644 index 0000000000000..9519a62ac352b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { kea, MakeLogicType } from 'kea'; + +import { FC } from 'react'; +import { History } from 'history'; +import { ApplicationStart, ChromeBreadcrumb } from 'src/core/public'; + +import { createHref, ICreateHrefOptions } from '../react_router_helpers'; + +interface IKibanaLogicProps { + config: { host?: string }; + history: History; + navigateToUrl: ApplicationStart['navigateToUrl']; + setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void; + setDocTitle(title: string): void; + renderHeaderActions(HeaderActions: FC): void; +} +export interface IKibanaValues extends IKibanaLogicProps { + navigateToUrl(path: string, options?: ICreateHrefOptions): Promise; +} + +export const KibanaLogic = kea>({ + path: ['enterprise_search', 'kibana_logic'], + reducers: ({ props }) => ({ + config: [props.config || {}, {}], + history: [props.history, {}], + navigateToUrl: [ + (url: string, options?: ICreateHrefOptions) => { + const href = createHref(url, props.history, options); + return props.navigateToUrl(href); + }, + {}, + ], + setBreadcrumbs: [props.setBreadcrumbs, {}], + setDocTitle: [props.setDocTitle, {}], + renderHeaderActions: [props.renderHeaderActions, {}], + }), +}); + +export const mountKibanaLogic = (props: IKibanaLogicProps) => { + KibanaLogic(props); + const unmount = KibanaLogic.mount(); + return unmount; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts index 3c8b3a7218862..61a4397486346 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.test.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; -import '../../__mocks__/react_router_history.mock'; -import { mockKibanaContext, mockHistory } from '../../__mocks__'; +import '../../__mocks__/kea.mock'; +import { mockKibanaValues, mockHistory } from '../../__mocks__'; -jest.mock('../react_router_helpers', () => ({ letBrowserHandleEvent: jest.fn(() => false) })); +jest.mock('../react_router_helpers', () => ({ + letBrowserHandleEvent: jest.fn(() => false), + createHref: jest.requireActual('../react_router_helpers').createHref, +})); import { letBrowserHandleEvent } from '../react_router_helpers'; import { @@ -50,21 +52,23 @@ describe('useBreadcrumbs', () => { it('prevents default navigation and uses React Router history on click', () => { const breadcrumb = useBreadcrumbs([{ text: '', path: '/test' }])[0] as any; + + expect(breadcrumb.href).toEqual('/app/enterprise_search/test'); + expect(mockHistory.createHref).toHaveBeenCalled(); + const event = { preventDefault: jest.fn() }; breadcrumb.onClick(event); - expect(mockKibanaContext.navigateToUrl).toHaveBeenCalledWith('/app/enterprise_search/test'); - expect(mockHistory.createHref).toHaveBeenCalled(); expect(event.preventDefault).toHaveBeenCalled(); + expect(mockKibanaValues.navigateToUrl).toHaveBeenCalled(); }); it('does not call createHref if shouldNotCreateHref is passed', () => { const breadcrumb = useBreadcrumbs([ { text: '', path: '/test', shouldNotCreateHref: true }, ])[0] as any; - breadcrumb.onClick({ preventDefault: () => null }); - expect(mockKibanaContext.navigateToUrl).toHaveBeenCalledWith('/test'); + expect(breadcrumb.href).toEqual('/test'); expect(mockHistory.createHref).not.toHaveBeenCalled(); }); @@ -74,7 +78,7 @@ describe('useBreadcrumbs', () => { (letBrowserHandleEvent as jest.Mock).mockImplementationOnce(() => true); breadcrumb.onClick(); - expect(mockKibanaContext.navigateToUrl).not.toHaveBeenCalled(); + expect(mockKibanaValues.navigateToUrl).not.toHaveBeenCalled(); }); it('does not generate link behavior if path is excluded', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts index 19714608e73e9..9ef23e6b176d9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/generate_breadcrumbs.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useContext } from 'react'; -import { useHistory } from 'react-router-dom'; +import { useValues } from 'kea'; import { EuiBreadcrumb } from '@elastic/eui'; -import { KibanaContext, IKibanaContext } from '../../index'; +import { KibanaLogic } from '../../shared/kibana'; import { ENTERPRISE_SEARCH_PLUGIN, @@ -16,7 +15,7 @@ import { WORKPLACE_SEARCH_PLUGIN, } from '../../../../common/constants'; -import { letBrowserHandleEvent } from '../react_router_helpers'; +import { letBrowserHandleEvent, createHref } from '../react_router_helpers'; /** * Generate React-Router-friendly EUI breadcrumb objects @@ -33,20 +32,17 @@ interface IBreadcrumb { export type TBreadcrumbs = IBreadcrumb[]; export const useBreadcrumbs = (breadcrumbs: TBreadcrumbs) => { - const history = useHistory(); - const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; + const { navigateToUrl, history } = useValues(KibanaLogic); return breadcrumbs.map(({ text, path, shouldNotCreateHref }) => { const breadcrumb = { text } as EuiBreadcrumb; if (path) { - const href = shouldNotCreateHref ? path : (history.createHref({ pathname: path }) as string); - - breadcrumb.href = href; + breadcrumb.href = createHref(path, history, { shouldNotCreateHref }); breadcrumb.onClick = (event) => { if (letBrowserHandleEvent(event)) return; event.preventDefault(); - navigateToUrl(href); + navigateToUrl(path, { shouldNotCreateHref }); }; } diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx index 61a066bb92216..2aee224304f89 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.test.tsx @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/kea.mock'; +import '../../__mocks__/shallow_useeffect.mock'; import '../../__mocks__/react_router_history.mock'; +import { mockKibanaValues } from '../../__mocks__'; import React from 'react'; - -import { mockKibanaContext, mountWithKibanaContext } from '../../__mocks__'; +import { shallow } from 'enzyme'; jest.mock('./generate_breadcrumbs', () => ({ useEnterpriseSearchBreadcrumbs: jest.fn(() => (crumbs: any) => crumbs), @@ -37,13 +38,13 @@ describe('Set Kibana Chrome helpers', () => { }); afterEach(() => { - expect(mockKibanaContext.setBreadcrumbs).toHaveBeenCalled(); - expect(mockKibanaContext.setDocTitle).toHaveBeenCalled(); + expect(mockKibanaValues.setBreadcrumbs).toHaveBeenCalled(); + expect(mockKibanaValues.setDocTitle).toHaveBeenCalled(); }); describe('SetEnterpriseSearchChrome', () => { it('sets breadcrumbs and document title', () => { - mountWithKibanaContext(); + shallow(); expect(enterpriseSearchTitle).toHaveBeenCalledWith(['Hello World']); expect(useEnterpriseSearchBreadcrumbs).toHaveBeenCalledWith([ @@ -55,7 +56,7 @@ describe('Set Kibana Chrome helpers', () => { }); it('sets empty breadcrumbs and document title when isRoot is true', () => { - mountWithKibanaContext(); + shallow(); expect(enterpriseSearchTitle).toHaveBeenCalledWith([]); expect(useEnterpriseSearchBreadcrumbs).toHaveBeenCalledWith([]); @@ -64,7 +65,7 @@ describe('Set Kibana Chrome helpers', () => { describe('SetAppSearchChrome', () => { it('sets breadcrumbs and document title', () => { - mountWithKibanaContext(); + shallow(); expect(appSearchTitle).toHaveBeenCalledWith(['Engines']); expect(useAppSearchBreadcrumbs).toHaveBeenCalledWith([ @@ -76,7 +77,7 @@ describe('Set Kibana Chrome helpers', () => { }); it('sets empty breadcrumbs and document title when isRoot is true', () => { - mountWithKibanaContext(); + shallow(); expect(appSearchTitle).toHaveBeenCalledWith([]); expect(useAppSearchBreadcrumbs).toHaveBeenCalledWith([]); @@ -85,7 +86,7 @@ describe('Set Kibana Chrome helpers', () => { describe('SetWorkplaceSearchChrome', () => { it('sets breadcrumbs and document title', () => { - mountWithKibanaContext(); + shallow(); expect(workplaceSearchTitle).toHaveBeenCalledWith(['Sources']); expect(useWorkplaceSearchBreadcrumbs).toHaveBeenCalledWith([ @@ -97,7 +98,7 @@ describe('Set Kibana Chrome helpers', () => { }); it('sets empty breadcrumbs and document title when isRoot is true', () => { - mountWithKibanaContext(); + shallow(); expect(workplaceSearchTitle).toHaveBeenCalledWith([]); expect(useWorkplaceSearchBreadcrumbs).toHaveBeenCalledWith([]); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx index 5e8d972e1a135..2ae3ca0137d54 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_chrome/set_chrome.tsx @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect } from 'react'; +import React, { useEffect } from 'react'; +import { useValues } from 'kea'; import { useHistory } from 'react-router-dom'; import { EuiBreadcrumb } from '@elastic/eui'; -import { KibanaContext, IKibanaContext } from '../../index'; +import { KibanaLogic } from '../kibana'; + import { useEnterpriseSearchBreadcrumbs, useAppSearchBreadcrumbs, @@ -41,7 +43,7 @@ type TBreadcrumbsProps = IBreadcrumbsProps | IRootBreadcrumbsProps; export const SetEnterpriseSearchChrome: React.FC = ({ text, isRoot }) => { const history = useHistory(); - const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); const title = isRoot ? [] : [text]; const docTitle = enterpriseSearchTitle(title as TTitle | []); @@ -59,7 +61,7 @@ export const SetEnterpriseSearchChrome: React.FC = ({ text, i export const SetAppSearchChrome: React.FC = ({ text, isRoot }) => { const history = useHistory(); - const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); const title = isRoot ? [] : [text]; const docTitle = appSearchTitle(title as TTitle | []); @@ -77,7 +79,7 @@ export const SetAppSearchChrome: React.FC = ({ text, isRoot } export const SetWorkplaceSearchChrome: React.FC = ({ text, isRoot }) => { const history = useHistory(); - const { setBreadcrumbs, setDocTitle } = useContext(KibanaContext) as IKibanaContext; + const { setBreadcrumbs, setDocTitle } = useValues(KibanaLogic); const title = isRoot ? [] : [text]; const docTitle = workplaceSearchTitle(title as TTitle | []); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts index 29c11ffa1cef8..4e371b337c40a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { LicenseContext, LicenseProvider, ILicenseContext } from './license_context'; -export { hasPlatinumLicense, hasGoldLicense } from './license_checks'; +export { LicensingLogic, mountLicensingLogic } from './licensing_logic'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts deleted file mode 100644 index 40f0f6380c21c..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts +++ /dev/null @@ -1,54 +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 { hasPlatinumLicense, hasGoldLicense } from './license_checks'; - -describe('hasPlatinumLicense', () => { - it('is true for platinum licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - }); - - it('is true for enterprise licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'enterprise' } as any)).toEqual(true); - }); - - it('is true for trial licenses', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - }); - - it('is false if the current license is expired', () => { - expect(hasPlatinumLicense({ isActive: false, type: 'platinum' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'enterprise' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'trial' } as any)).toEqual(false); - }); - - it('is false for licenses below platinum', () => { - expect(hasPlatinumLicense({ isActive: true, type: 'basic' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: false, type: 'standard' } as any)).toEqual(false); - expect(hasPlatinumLicense({ isActive: true, type: 'gold' } as any)).toEqual(false); - }); -}); - -describe('hasGoldLicense', () => { - it('is true for gold+ and trial licenses', () => { - expect(hasGoldLicense({ isActive: true, type: 'gold' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'enterprise' } as any)).toEqual(true); - expect(hasGoldLicense({ isActive: true, type: 'trial' } as any)).toEqual(true); - }); - - it('is false if the current license is expired', () => { - expect(hasGoldLicense({ isActive: false, type: 'gold' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'platinum' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'enterprise' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'trial' } as any)).toEqual(false); - }); - - it('is false for licenses below gold', () => { - expect(hasGoldLicense({ isActive: true, type: 'basic' } as any)).toEqual(false); - expect(hasGoldLicense({ isActive: false, type: 'standard' } as any)).toEqual(false); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts deleted file mode 100644 index d13d0909243be..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.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 { ILicense } from '../../../../../licensing/public'; - -export const hasPlatinumLicense = (license?: ILicense) => { - const qualifyingLicenses = ['platinum', 'enterprise', 'trial']; - return license?.isActive && qualifyingLicenses.includes(license?.type as string); -}; - -export const hasGoldLicense = (license?: ILicense) => { - const qualifyingLicenses = ['gold', 'platinum', 'enterprise', 'trial']; - return license?.isActive && qualifyingLicenses.includes(license?.type as string); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx deleted file mode 100644 index c65474ec1f590..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useContext } from 'react'; - -import { mountWithContext } from '../../__mocks__'; -import { LicenseContext, ILicenseContext } from './'; - -describe('LicenseProvider', () => { - const MockComponent: React.FC = () => { - const { license } = useContext(LicenseContext) as ILicenseContext; - return
{license?.type}
; - }; - - it('renders children', () => { - const wrapper = mountWithContext(, { license: { type: 'basic' } }); - - expect(wrapper.find('.license-test')).toHaveLength(1); - expect(wrapper.text()).toEqual('basic'); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx deleted file mode 100644 index 9b47959ff7544..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import useObservable from 'react-use/lib/useObservable'; -import { Observable } from 'rxjs'; - -import { ILicense } from '../../../../../licensing/public'; - -export interface ILicenseContext { - license: ILicense; -} -interface ILicenseContextProps { - license$: Observable; - children: React.ReactNode; -} - -export const LicenseContext = React.createContext({}); - -export const LicenseProvider: React.FC = ({ license$, children }) => { - // Listen for changes to license subscription - const license = useObservable(license$); - - // Render rest of application and pass down license via context - return ; -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts new file mode 100644 index 0000000000000..153a5ae765468 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.test.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { resetContext } from 'kea'; +import { BehaviorSubject } from 'rxjs'; + +import { licensingMock } from '../../../../../licensing/public/mocks'; + +import { LicensingLogic, mountLicensingLogic } from './licensing_logic'; + +describe('LicensingLogic', () => { + const mockLicense = licensingMock.createLicense(); + const mockLicense$ = new BehaviorSubject(mockLicense); + const mount = () => mountLicensingLogic({ license$: mockLicense$ }); + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + }); + + describe('setLicense()', () => { + it('sets license value', () => { + mount(); + LicensingLogic.actions.setLicense('test' as any); + expect(LicensingLogic.values.license).toEqual('test'); + }); + }); + + describe('setLicenseSubscription()', () => { + it('sets licenseSubscription value', () => { + mount(); + LicensingLogic.actions.setLicenseSubscription('test' as any); + expect(LicensingLogic.values.licenseSubscription).toEqual('test'); + }); + }); + + describe('licensing subscription', () => { + describe('on mount', () => { + it('subscribes to the license observable', () => { + mount(); + expect(LicensingLogic.values.license).toEqual(mockLicense); + expect(LicensingLogic.values.licenseSubscription).not.toBeNull(); + }); + }); + + describe('on subscription update', () => { + it('updates the license value', () => { + mount(); + + const nextMockLicense = licensingMock.createLicense({ license: { status: 'invalid' } }); + mockLicense$.next(nextMockLicense); + + expect(LicensingLogic.values.license).toEqual(nextMockLicense); + }); + }); + + describe('on unmount', () => { + it('unsubscribes to the license observable', () => { + const mockUnsubscribe = jest.fn(); + const unmount = mountLicensingLogic({ + license$: { subscribe: () => ({ unsubscribe: mockUnsubscribe }) } as any, + }); + unmount(); + expect(mockUnsubscribe).toHaveBeenCalled(); + }); + + it('does not crash if no subscription exists', () => { + const unmount = mount(); + LicensingLogic.actions.setLicenseSubscription(null as any); + unmount(); + }); + }); + }); + + describe('license check selectors', () => { + beforeEach(() => { + mount(); + }); + + const updateLicense = (license: any) => { + const updatedLicense = licensingMock.createLicense({ license }); + mockLicense$.next(updatedLicense); + }; + + describe('hasPlatinumLicense', () => { + it('is true for platinum+ and trial licenses', () => { + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(true); + }); + + it('is false if the current license is expired', () => { + updateLicense({ status: 'expired', type: 'platinum' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'enterprise' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + }); + + it('is false for licenses below platinum', () => { + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.hasPlatinumLicense).toEqual(false); + }); + }); + + describe('hasGoldLicense', () => { + it('is true for gold+ and trial licenses', () => { + updateLicense({ status: 'active', type: 'gold' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'platinum' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'enterprise' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + + updateLicense({ status: 'active', type: 'trial' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(true); + }); + + it('is false if the current license is expired', () => { + updateLicense({ status: 'expired', type: 'gold' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'platinum' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'enterprise' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'expired', type: 'trial' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + }); + + it('is false for licenses below gold', () => { + updateLicense({ status: 'active', type: 'basic' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + + updateLicense({ status: 'active', type: 'standard' }); + expect(LicensingLogic.values.hasGoldLicense).toEqual(false); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts new file mode 100644 index 0000000000000..ae31b2ec6168a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/licensing_logic.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { kea, MakeLogicType } from 'kea'; +import { Observable, Subscription } from 'rxjs'; + +import { ILicense } from '../../../../../licensing/public'; + +export interface ILicensingValues { + license: ILicense | null; + licenseSubscription: Subscription | null; + hasPlatinumLicense: boolean; + hasGoldLicense: boolean; +} +export interface ILicensingActions { + setLicense(license: ILicense): ILicense; + setLicenseSubscription(licenseSubscription: Subscription): Subscription; +} + +export const LicensingLogic = kea>({ + path: ['enterprise_search', 'licensing_logic'], + actions: { + setLicense: (license) => license, + setLicenseSubscription: (licenseSubscription) => licenseSubscription, + }, + reducers: { + license: [ + null, + { + setLicense: (_, license) => license, + }, + ], + licenseSubscription: [ + null, + { + setLicenseSubscription: (_, licenseSubscription) => licenseSubscription, + }, + ], + }, + selectors: { + hasPlatinumLicense: [ + (selectors) => [selectors.license], + (license) => { + const qualifyingLicenses = ['platinum', 'enterprise', 'trial']; + return license?.isActive && qualifyingLicenses.includes(license?.type); + }, + ], + hasGoldLicense: [ + (selectors) => [selectors.license], + (license) => { + const qualifyingLicenses = ['gold', 'platinum', 'enterprise', 'trial']; + return license?.isActive && qualifyingLicenses.includes(license?.type); + }, + ], + }, + events: ({ props, actions, values }) => ({ + afterMount: () => { + const licenseSubscription = props.license$.subscribe(async (license: ILicense) => { + actions.setLicense(license); + }); + actions.setLicenseSubscription(licenseSubscription); + }, + beforeUnmount: () => { + if (values.licenseSubscription) values.licenseSubscription.unsubscribe(); + }, + }), +}); + +/** + * Mount/props helper + */ +interface ILicensingLogicProps { + license$: Observable; +} +export const mountLicensingLogic = (props: ILicensingLogicProps) => { + LicensingLogic(props); + const unmount = LicensingLogic.mount(); + return unmount; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx index ce9071ad7b9d0..62c0af31cffd9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.test.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/kea.mock'; -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { shallow } from 'enzyme'; import { EuiButton as EuiButtonExternal, EuiEmptyPrompt } from '@elastic/eui'; @@ -18,13 +19,6 @@ import { WorkplaceSearchLogo } from './assets/workplace_search_logo'; import { NotFound } from './'; describe('NotFound', () => { - const basicLicense = { isActive: true, type: 'basic' }; - const goldLicense = { isActive: true, type: 'gold' }; - - beforeEach(() => { - (useContext as jest.Mock).mockImplementation(() => ({ license: basicLicense })); - }); - it('renders an App Search 404 view', () => { const wrapper = shallow(); const prompt = wrapper.find(EuiEmptyPrompt).dive().shallow(); @@ -50,7 +44,7 @@ describe('NotFound', () => { }); it('changes the support URL if the user has a gold+ license', () => { - (useContext as jest.Mock).mockImplementation(() => ({ license: goldLicense })); + (useValues as jest.Mock).mockReturnValueOnce({ hasGoldLicense: true }); const wrapper = shallow(); const prompt = wrapper.find(EuiEmptyPrompt).dive().shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx index bd988854225fb..40bb5efcc6330 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/not_found/not_found.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { EuiPageContent, @@ -24,7 +25,7 @@ import { import { EuiButton } from '../react_router_helpers'; import { SetAppSearchChrome, SetWorkplaceSearchChrome } from '../kibana_chrome'; import { SendAppSearchTelemetry, SendWorkplaceSearchTelemetry } from '../telemetry'; -import { LicenseContext, ILicenseContext, hasGoldLicense } from '../licensing'; +import { LicensingLogic } from '../licensing'; import { AppSearchLogo } from './assets/app_search_logo'; import { WorkplaceSearchLogo } from './assets/workplace_search_logo'; @@ -39,8 +40,8 @@ interface NotFoundProps { } export const NotFound: React.FC = ({ product = {} }) => { - const { license } = useContext(LicenseContext) as ILicenseContext; - const supportUrl = hasGoldLicense(license) ? LICENSED_SUPPORT_URL : product.SUPPORT_URL; + const { hasGoldLicense } = useValues(LicensingLogic); + const supportUrl = hasGoldLicense ? LICENSED_SUPPORT_URL : product.SUPPORT_URL; let Logo; let SetPageChrome; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts new file mode 100644 index 0000000000000..5f96beeb42ae4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.test.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mockHistory } from '../../__mocks__'; + +import { createHref } from './'; + +describe('createHref', () => { + it('generates a path with the React Router basename included', () => { + expect(createHref('/test', mockHistory)).toEqual('/app/enterprise_search/test'); + }); + + it('does not include the basename if shouldNotCreateHref is passed', () => { + expect(createHref('/test', mockHistory, { shouldNotCreateHref: true })).toEqual('/test'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.ts new file mode 100644 index 0000000000000..cc8279c80a092 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/create_href.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 { History } from 'history'; + +/** + * This helper uses React Router's createHref function to generate links with router basenames accounted for. + * For example, if we perform navigateToUrl('/engines') within App Search, we expect the app basename + * to be taken into account to be intelligently routed to '/app/enterprise_search/app_search/engines'. + * + * This helper accomplishes that, while still giving us an escape hatch for navigation *between* apps. + * For example, if we want to navigate the user from App Search to Enterprise Search we could + * navigateToUrl('/app/enterprise_search', { shouldNotCreateHref: true }) + */ +export interface ICreateHrefOptions { + shouldNotCreateHref?: boolean; +} +export const createHref = ( + path: string, + history: History, + options?: ICreateHrefOptions +): string => { + return options?.shouldNotCreateHref ? path : history.createHref({ pathname: path }); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx index 0c7bac99085dd..82fbb8940d460 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../__mocks__/shallow_usecontext.mock'; -import '../../__mocks__/react_router_history.mock'; +import '../../__mocks__/kea.mock'; import React from 'react'; import { shallow, mount } from 'enzyme'; import { EuiLink, EuiButton } from '@elastic/eui'; -import { mockKibanaContext, mockHistory } from '../../__mocks__'; +import { mockKibanaValues, mockHistory } from '../../__mocks__'; import { EuiReactRouterLink, EuiReactRouterButton } from './eui_link'; @@ -69,7 +68,7 @@ describe('EUI & React Router Component Helpers', () => { wrapper.find(EuiLink).simulate('click', simulatedEvent); expect(simulatedEvent.preventDefault).toHaveBeenCalled(); - expect(mockKibanaContext.navigateToUrl).toHaveBeenCalled(); + expect(mockKibanaValues.navigateToUrl).toHaveBeenCalled(); }); it('does not prevent default browser behavior on new tab/window clicks', () => { @@ -81,7 +80,7 @@ describe('EUI & React Router Component Helpers', () => { }; wrapper.find(EuiLink).simulate('click', simulatedEvent); - expect(mockKibanaContext.navigateToUrl).not.toHaveBeenCalled(); + expect(mockKibanaValues.navigateToUrl).not.toHaveBeenCalled(); }); it('calls inherited onClick actions in addition to default navigation', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx index e3b46632ddf9e..e0aa5afdf38c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; -import { useHistory } from 'react-router-dom'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps } from '@elastic/eui'; -import { KibanaContext, IKibanaContext } from '../../index'; -import { letBrowserHandleEvent } from './link_events'; +import { KibanaLogic } from '../../shared/kibana'; +import { letBrowserHandleEvent, createHref } from './'; /** * Generates either an EuiLink or EuiButton with a React-Router-ified link @@ -32,11 +32,10 @@ export const EuiReactRouterHelper: React.FC = ({ shouldNotCreateHref, children, }) => { - const history = useHistory(); - const { navigateToUrl } = useContext(KibanaContext) as IKibanaContext; + const { navigateToUrl, history } = useValues(KibanaLogic); // Generate the correct link href (with basename etc. accounted for) - const href = shouldNotCreateHref ? to : history.createHref({ pathname: to }); + const href = createHref(to, history, { shouldNotCreateHref }); const reactRouterLinkClick = (event: React.MouseEvent) => { if (onClick) onClick(); // Run any passed click events (e.g. telemetry) @@ -46,7 +45,7 @@ export const EuiReactRouterHelper: React.FC = ({ event.preventDefault(); // Perform SPA navigation. - navigateToUrl(href); + navigateToUrl(to, { shouldNotCreateHref }); }; const reactRouterProps = { href, onClick: reactRouterLinkClick }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts index 46dc328633153..6915d3222c45c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/index.ts @@ -5,5 +5,6 @@ */ export { letBrowserHandleEvent } from './link_events'; +export { createHref, ICreateHrefOptions } from './create_href'; export { EuiReactRouterLink as EuiLink } from './eui_link'; export { EuiReactRouterButton as EuiButton } from './eui_link'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.test.tsx index 0423ae61779af..802a10e3b3db7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/setup_guide/setup_guide.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EuiSteps, EuiIcon, EuiLink } from '@elastic/eui'; -import { mountWithContext } from '../../__mocks__'; +import { mountWithIntl } from '../../__mocks__'; import { SetupGuide } from './'; @@ -27,7 +27,7 @@ describe('SetupGuide', () => { }); it('renders with optional auth links', () => { - const wrapper = mountWithContext( + const wrapper = mountWithIntl( { - const httpMock = httpServiceMock.createSetupContract(); - beforeEach(() => { jest.clearAllMocks(); }); @@ -27,13 +28,13 @@ describe('Shared Telemetry Helpers', () => { describe('sendTelemetry', () => { it('successfully calls the server-side telemetry endpoint', () => { sendTelemetry({ - http: httpMock, + http: mockHttpValues.http, product: 'enterprise_search', action: 'viewed', metric: 'setup_guide', }); - expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { + expect(mockHttpValues.http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { headers, body: '{"product":"enterprise_search","action":"viewed","metric":"setup_guide"}', }); @@ -50,33 +51,27 @@ describe('Shared Telemetry Helpers', () => { describe('React component helpers', () => { it('SendEnterpriseSearchTelemetry component', () => { - mountWithKibanaContext(, { - http: httpMock, - }); + shallow(); - expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { + expect(mockHttpValues.http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { headers, body: '{"product":"enterprise_search","action":"viewed","metric":"page"}', }); }); it('SendAppSearchTelemetry component', () => { - mountWithKibanaContext(, { - http: httpMock, - }); + shallow(); - expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { + expect(mockHttpValues.http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { headers, body: '{"product":"app_search","action":"clicked","metric":"button"}', }); }); it('SendWorkplaceSearchTelemetry component', () => { - mountWithKibanaContext(, { - http: httpMock, - }); + shallow(); - expect(httpMock.put).toHaveBeenCalledWith('/api/enterprise_search/telemetry', { + expect(mockHttpValues.http.put).toHaveBeenCalledWith('/api/enterprise_search/stats', { headers, body: '{"product":"workplace_search","action":"error","metric":"not_found"}', }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx index 4df1428221de6..2f87597897b41 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect } from 'react'; +import React, { useEffect } from 'react'; +import { useValues } from 'kea'; import { HttpSetup } from 'src/core/public'; import { JSON_HEADER as headers } from '../../../../common/constants'; -import { KibanaContext, IKibanaContext } from '../../index'; +import { HttpLogic } from '../http'; interface ISendTelemetryProps { action: 'viewed' | 'error' | 'clicked'; @@ -27,7 +28,7 @@ interface ISendTelemetry extends ISendTelemetryProps { export const sendTelemetry = async ({ http, product, action, metric }: ISendTelemetry) => { try { const body = JSON.stringify({ product, action, metric }); - await http.put('/api/enterprise_search/telemetry', { headers, body }); + await http.put('/api/enterprise_search/stats', { headers, body }); } catch (error) { throw new Error('Unable to send telemetry'); } @@ -41,7 +42,7 @@ export const SendEnterpriseSearchTelemetry: React.FC = ({ action, metric, }) => { - const { http } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); useEffect(() => { sendTelemetry({ http, action, metric, product: 'enterprise_search' }); @@ -51,7 +52,7 @@ export const SendEnterpriseSearchTelemetry: React.FC = ({ }; export const SendAppSearchTelemetry: React.FC = ({ action, metric }) => { - const { http } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); useEffect(() => { sendTelemetry({ http, action, metric, product: 'app_search' }); @@ -61,7 +62,7 @@ export const SendAppSearchTelemetry: React.FC = ({ action, }; export const SendWorkplaceSearchTelemetry: React.FC = ({ action, metric }) => { - const { http } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); useEffect(() => { sendTelemetry({ http, action, metric, product: 'workplace_search' }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts index c52eceb2d2fdd..974e07069ddba 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts @@ -50,5 +50,15 @@ describe('AppLogic', () => { expect(AppLogic.values).toEqual(expectedLogicValues); }); + + it('gracefully handles missing initial data', () => { + AppLogic.actions.initializeAppData({}); + + expect(AppLogic.values).toEqual({ + ...DEFAULT_VALUES, + hasInitialized: true, + isFederatedAuth: false, + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts index 94bd1d529b65f..629d1969a8f59 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts @@ -21,6 +21,9 @@ export interface IAppActions { initializeAppData(props: IInitialAppData): IInitialAppData; } +const emptyOrg = {} as IOrganization; +const emptyAccount = {} as IAccount; + export const AppLogic = kea>({ path: ['enterprise_search', 'workplace_search', 'app_logic'], actions: { @@ -43,15 +46,15 @@ export const AppLogic = kea>({ }, ], organization: [ - {} as IOrganization, + emptyOrg, { - initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.organization, + initializeAppData: (_, { workplaceSearch }) => workplaceSearch?.organization || emptyOrg, }, ], account: [ - {} as IAccount, + emptyAccount, { - initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.account, + initializeAppData: (_, { workplaceSearch }) => workplaceSearch?.account || emptyAccount, }, ], }, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx index a006c5e3775d5..0ebd59eda5be7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx @@ -4,26 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ +import { externalUrl } from '../../../shared/enterprise_search_url'; + import React from 'react'; import { shallow } from 'enzyme'; - import { EuiButtonEmpty } from '@elastic/eui'; -import { ExternalUrl } from '../../../shared/enterprise_search_url'; import { WorkplaceSearchHeaderActions } from './'; describe('WorkplaceSearchHeaderActions', () => { - const externalUrl = new ExternalUrl('http://localhost:3002'); + it('does not render without an Enterprise Search URL set', () => { + const wrapper = shallow(); - it('renders a link to the search application', () => { - const wrapper = shallow(); - - expect(wrapper.find(EuiButtonEmpty).prop('href')).toEqual('http://localhost:3002/ws/search'); + expect(wrapper.isEmptyRender()).toBe(true); }); - it('does not render without an Enterprise Search host URL set', () => { - const wrapper = shallow(); + it('renders a link to the search application', () => { + externalUrl.enterpriseSearchUrl = 'http://localhost:3002'; - expect(wrapper.isEmptyRender()).toBe(true); + const wrapper = shallow(); + + expect(wrapper.find(EuiButtonEmpty).prop('href')).toEqual('http://localhost:3002/ws/search'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx index fa32d598f848d..b7da5b4281aa0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx @@ -8,15 +8,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty } from '@elastic/eui'; -import { IExternalUrl } from '../../../shared/enterprise_search_url'; +import { externalUrl, getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; -interface IProps { - externalUrl: IExternalUrl; -} - -export const WorkplaceSearchHeaderActions: React.FC = ({ externalUrl }) => { - const { enterpriseSearchUrl, getWorkplaceSearchUrl } = externalUrl; - if (!enterpriseSearchUrl) return null; +export const WorkplaceSearchHeaderActions: React.FC = () => { + if (!externalUrl.enterpriseSearchUrl) return null; return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx index 0e85d8467cff0..2553284744e4d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.test.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; +import '../../../__mocks__/enterprise_search_url.mock'; + import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx index 9fb627ed09791..5572716391112 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/nav.tsx @@ -3,13 +3,13 @@ * 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 } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer } from '@elastic/eui'; import { WORKPLACE_SEARCH_PLUGIN } from '../../../../../common/constants'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; import { SideNav, SideNavLink } from '../../../shared/layout'; import { @@ -22,10 +22,6 @@ import { } from '../../routes'; export const WorkplaceSearchNav: React.FC = () => { - const { - externalUrl: { getWorkplaceSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; - // TODO: icons return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/content_section/content_section.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/content_section/content_section.test.tsx index de417ffe8a888..cc827d7edb0af 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/content_section/content_section.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/content_section/content_section.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; import { EuiTitle, EuiSpacer } from '@elastic/eui'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/loading/loading.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/loading/loading.test.tsx index 8d168b436cc3b..8b4a999824e35 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/loading/loading.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/loading/loading.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; import { EuiLoadingSpinner } from '@elastic/eui'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.test.tsx index 429a2c509813d..2013b2609f33b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; +import '../../../../__mocks__/kea.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.tsx index a914000654165..344b442d9a678 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/product_button/product_button.tsx @@ -4,19 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiButton, EuiButtonProps, EuiLinkProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { sendTelemetry } from '../../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../../index'; +import { HttpLogic } from '../../../../shared/http'; +import { getWorkplaceSearchUrl } from '../../../../shared/enterprise_search_url'; export const ProductButton: React.FC = () => { - const { - externalUrl: { getWorkplaceSearchUrl }, - http, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const buttonProps = { fill: true, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx index dd37ba9b6d859..c17b89c93a28b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx index ddbe327a40a30..67d435e330c3d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_icon/source_icon.tsx @@ -6,6 +6,8 @@ import React from 'react'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import _camelCase from 'lodash/camelCase'; import { images } from '../assets'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.test.tsx index 2bde3b70f82b5..1fc21859851e5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx index 17ca8e58a80fa..a2e252c886354 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/source_row/source_row.tsx @@ -7,6 +7,8 @@ import React from 'react'; import classNames from 'classnames'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import _kebabCase from 'lodash/kebabCase'; import { Link } from 'react-router-dom'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx index b0b07c46b4ea8..1bb9ff255f7ed 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/shared/view_content_header/view_content_header.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; import { EuiFlexGroup } from '@elastic/eui'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx index fc1943264d72b..25544b4a9bb68 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../__mocks__/shallow_usecontext.mock'; +import '../__mocks__/shallow_useeffect.mock'; import '../__mocks__/kea.mock'; +import { setMockValues, setMockActions, mockKibanaValues } from '../__mocks__'; -import React, { useContext } from 'react'; +import React from 'react'; import { Redirect } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { useValues, useActions } from 'kea'; import { Layout } from '../shared/layout'; +import { WorkplaceSearchHeaderActions } from './components/layout'; import { SetupGuide } from './views/setup_guide'; import { ErrorState } from './views/error_state'; import { Overview } from './views/overview'; @@ -21,14 +22,14 @@ import { WorkplaceSearch, WorkplaceSearchUnconfigured, WorkplaceSearchConfigured describe('WorkplaceSearch', () => { it('renders WorkplaceSearchUnconfigured when config.host is not set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: '' } })); + setMockValues({ config: { host: '' } }); const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchUnconfigured)).toHaveLength(1); }); it('renders WorkplaceSearchConfigured when config.host set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ config: { host: 'some.url' } })); + setMockValues({ config: { host: 'some.url' } }); const wrapper = shallow(); expect(wrapper.find(WorkplaceSearchConfigured)).toHaveLength(1); @@ -46,39 +47,40 @@ describe('WorkplaceSearchUnconfigured', () => { describe('WorkplaceSearchConfigured', () => { beforeEach(() => { - // Mock resets - (useValues as jest.Mock).mockImplementation(() => ({})); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData: () => {} })); + jest.clearAllMocks(); + setMockActions({ initializeAppData: () => {} }); }); - it('renders with layout', () => { + it('renders layout and header actions', () => { const wrapper = shallow(); expect(wrapper.find(Layout).prop('readOnlyMode')).toBeFalsy(); expect(wrapper.find(Overview)).toHaveLength(1); + expect(mockKibanaValues.renderHeaderActions).toHaveBeenCalledWith(WorkplaceSearchHeaderActions); }); it('initializes app data with passed props', () => { const initializeAppData = jest.fn(); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData })); + setMockActions({ initializeAppData }); shallow(); expect(initializeAppData).toHaveBeenCalledWith({ isFederatedAuth: true }); }); - it('does not re-initialize app data', () => { + it('does not re-initialize app data or re-render header actions', () => { const initializeAppData = jest.fn(); - (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData })); - (useValues as jest.Mock).mockImplementation(() => ({ hasInitialized: true })); + setMockActions({ initializeAppData }); + setMockValues({ hasInitialized: true }); shallow(); expect(initializeAppData).not.toHaveBeenCalled(); + expect(mockKibanaValues.renderHeaderActions).not.toHaveBeenCalled(); }); it('renders ErrorState', () => { - (useValues as jest.Mock).mockImplementation(() => ({ errorConnecting: true })); + setMockValues({ errorConnecting: true }); const wrapper = shallow(); @@ -86,7 +88,7 @@ describe('WorkplaceSearchConfigured', () => { }); it('passes readOnlyMode state', () => { - (useValues as jest.Mock).mockImplementation(() => ({ readOnlyMode: true })); + setMockValues({ readOnlyMode: true }); const wrapper = shallow(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index a68dfaf8ea471..b4c4217659043 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -4,17 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { Route, Redirect, Switch } from 'react-router-dom'; import { useActions, useValues } from 'kea'; import { WORKPLACE_SEARCH_PLUGIN } from '../../../common/constants'; import { IInitialAppData } from '../../../common/types'; -import { KibanaContext, IKibanaContext } from '../index'; +import { KibanaLogic } from '../shared/kibana'; import { HttpLogic } from '../shared/http'; import { AppLogic } from './app_logic'; import { Layout } from '../shared/layout'; -import { WorkplaceSearchNav } from './components/layout/nav'; +import { WorkplaceSearchNav, WorkplaceSearchHeaderActions } from './components/layout'; import { SETUP_GUIDE_PATH } from './routes'; @@ -24,17 +24,21 @@ import { NotFound } from '../shared/not_found'; import { Overview } from './views/overview'; export const WorkplaceSearch: React.FC = (props) => { - const { config } = useContext(KibanaContext) as IKibanaContext; + const { config } = useValues(KibanaLogic); return !config.host ? : ; }; export const WorkplaceSearchConfigured: React.FC = (props) => { const { hasInitialized } = useValues(AppLogic); const { initializeAppData } = useActions(AppLogic); + const { renderHeaderActions } = useValues(KibanaLogic); const { errorConnecting, readOnlyMode } = useValues(HttpLogic); useEffect(() => { - if (!hasInitialized) initializeAppData(props); + if (!hasInitialized) { + initializeAppData(props); + renderHeaderActions(WorkplaceSearchHeaderActions); + } }, [hasInitialized]); return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.test.tsx index ab5cd7f0de90f..a757e187da098 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/error_state/error_state.test.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; - import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.test.tsx index 1d7c565935e97..6be033d7225a8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.test.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; +import '../../../__mocks__/kea.mock'; +import '../../../__mocks__/enterprise_search_url.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.tsx index 786357358dfa6..c1070d57f2856 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_card.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; +import { useValues } from 'kea'; import { EuiButton, @@ -17,8 +18,10 @@ import { EuiButtonEmptyProps, EuiLinkProps, } from '@elastic/eui'; + import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; interface IOnboardingCardProps { title: React.ReactNode; @@ -39,10 +42,7 @@ export const OnboardingCard: React.FC = ({ actionPath, complete, }) => { - const { - http, - externalUrl: { getWorkplaceSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const onClick = () => sendTelemetry({ diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx index 0f3eee074caef..37b3340b96a6a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; import './__mocks__/overview_logic.mock'; import { setMockValues } from './__mocks__'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx index 0baadfc912ad5..132824833909d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useValues } from 'kea'; @@ -23,7 +23,8 @@ import { } from '@elastic/eui'; import sharedSourcesIcon from '../../components/shared/assets/share_circle.svg'; import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; import { ORG_SOURCES_PATH, USERS_PATH, ORG_SETTINGS_PATH } from '../../routes'; import { ContentSection } from '../../components/shared/content_section'; @@ -135,10 +136,7 @@ export const OnboardingSteps: React.FC = () => { }; export const OrgNameOnboarding: React.FC = () => { - const { - http, - externalUrl: { getWorkplaceSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const onClick = () => sendTelemetry({ diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.test.tsx index d9b05c5da777d..d9d03245f6141 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; import './__mocks__/overview_logic.mock'; import { setMockValues } from './__mocks__'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx index 31613098f9fcc..989ff800483f6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; import './__mocks__/overview_logic.mock'; import { setMockValues } from './__mocks__'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx index 0813999c9a078..d1b5228123d94 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; import moment from 'moment'; import { useValues } from 'kea'; @@ -14,7 +14,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { ContentSection } from '../../components/shared/content_section'; import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { HttpLogic } from '../../../shared/http'; +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; import { SOURCE_DETAILS_PATH, getContentSourcePath } from '../../routes'; import { AppLogic } from '../../app_logic'; @@ -93,10 +94,7 @@ export const RecentActivityItem: React.FC = ({ timestamp, sourceId, }) => { - const { - http, - externalUrl: { getWorkplaceSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; + const { http } = useValues(HttpLogic); const onClick = () => sendTelemetry({ diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.test.tsx index edf266231b39e..013b23d2a9ec0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; +import '../../../__mocks__/enterprise_search_url.mock'; import React from 'react'; import { shallow } from 'enzyme'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.tsx index 3e1d285698c0c..6c4f43b1a3a22 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/statistic_card.tsx @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; - +import React from 'react'; import { EuiCard, EuiFlexItem, EuiTitle, EuiTextColor } from '@elastic/eui'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; interface IStatisticCardProps { title: string; @@ -17,10 +16,6 @@ interface IStatisticCardProps { } export const StatisticCard: React.FC = ({ title, count = 0, actionPath }) => { - const { - externalUrl: { getWorkplaceSearchUrl }, - } = useContext(KibanaContext) as IKibanaContext; - const linkProps = actionPath ? { href: getWorkplaceSearchUrl(actionPath), diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index c23bb23be3979..e054a49ef1f74 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -7,7 +7,6 @@ import { AppMountParameters, CoreSetup, - CoreStart, HttpSetup, Plugin, PluginInitializerContext, @@ -17,26 +16,27 @@ import { FeatureCatalogueCategory, HomePublicPluginSetup, } from '../../../../src/plugins/home/public'; -import { LicensingPluginSetup } from '../../licensing/public'; +import { LicensingPluginStart } from '../../licensing/public'; import { APP_SEARCH_PLUGIN, ENTERPRISE_SEARCH_PLUGIN, WORKPLACE_SEARCH_PLUGIN, } from '../common/constants'; import { IInitialAppData } from '../common/types'; -import { ExternalUrl, IExternalUrl } from './applications/shared/enterprise_search_url'; export interface ClientConfigType { host?: string; } export interface ClientData extends IInitialAppData { - externalUrl: IExternalUrl; + publicUrl?: string; errorConnecting?: boolean; } export interface PluginsSetup { home?: HomePublicPluginSetup; - licensing: LicensingPluginSetup; +} +export interface PluginsStart { + licensing: LicensingPluginStart; } export class EnterpriseSearchPlugin implements Plugin { @@ -46,7 +46,6 @@ export class EnterpriseSearchPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); - this.data.externalUrl = new ExternalUrl(this.config.host || ''); } public setup(core: CoreSetup, plugins: PluginsSetup) { @@ -57,16 +56,17 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: ENTERPRISE_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; + const kibanaDeps = await this.getKibanaDeps(core, params); + const { chrome, http } = kibanaDeps.core; chrome.docTitle.change(ENTERPRISE_SEARCH_PLUGIN.NAME); - await this.getInitialData(coreStart.http); + await this.getInitialData(http); + const pluginData = this.getPluginData(); const { renderApp } = await import('./applications'); const { EnterpriseSearch } = await import('./applications/enterprise_search'); - return renderApp(EnterpriseSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(EnterpriseSearch, kibanaDeps, pluginData); }, }); @@ -77,16 +77,17 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: APP_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; + const kibanaDeps = await this.getKibanaDeps(core, params); + const { chrome, http } = kibanaDeps.core; chrome.docTitle.change(APP_SEARCH_PLUGIN.NAME); - await this.getInitialData(coreStart.http); + await this.getInitialData(http); + const pluginData = this.getPluginData(); const { renderApp } = await import('./applications'); const { AppSearch } = await import('./applications/app_search'); - return renderApp(AppSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(AppSearch, kibanaDeps, pluginData); }, }); @@ -97,23 +98,17 @@ export class EnterpriseSearchPlugin implements Plugin { appRoute: WORKPLACE_SEARCH_PLUGIN.URL, category: DEFAULT_APP_CATEGORIES.enterpriseSearch, mount: async (params: AppMountParameters) => { - const [coreStart] = await core.getStartServices(); - const { chrome } = coreStart; + const kibanaDeps = await this.getKibanaDeps(core, params); + const { chrome, http } = kibanaDeps.core; chrome.docTitle.change(WORKPLACE_SEARCH_PLUGIN.NAME); - await this.getInitialData(coreStart.http); + await this.getInitialData(http); + const pluginData = this.getPluginData(); - const { renderApp, renderHeaderActions } = await import('./applications'); + const { renderApp } = await import('./applications'); const { WorkplaceSearch } = await import('./applications/workplace_search'); - const { WorkplaceSearchHeaderActions } = await import( - './applications/workplace_search/components/layout' - ); - params.setHeaderActionMenu((element) => - renderHeaderActions(WorkplaceSearchHeaderActions, element, this.data.externalUrl) - ); - - return renderApp(WorkplaceSearch, params, coreStart, plugins, this.config, this.data); + return renderApp(WorkplaceSearch, kibanaDeps, pluginData); }, }); @@ -149,23 +144,31 @@ export class EnterpriseSearchPlugin implements Plugin { } } - public start(core: CoreStart) {} + public start() {} public stop() {} + private async getKibanaDeps(core: CoreSetup, params: AppMountParameters) { + // Helper for using start dependencies on mount (instead of setup dependencies) + // and for grouping Kibana-related args together (vs. plugin-specific args) + const [coreStart, pluginsStart] = await core.getStartServices(); + return { params, core: coreStart, plugins: pluginsStart as PluginsStart }; + } + + private getPluginData() { + // Small helper for grouping plugin data related args together + return { config: this.config, data: this.data }; + } + private async getInitialData(http: HttpSetup) { if (!this.config.host) return; // No API to call if (this.hasInitialized) return; // We've already made an initial call try { - const { publicUrl, ...initialData } = await http.get('/api/enterprise_search/config_data'); - this.data = { ...this.data, ...initialData }; - if (publicUrl) this.data.externalUrl = new ExternalUrl(publicUrl); - + this.data = await http.get('/api/enterprise_search/config_data'); this.hasInitialized = true; } catch { this.data.errorConnecting = true; - // The plugin will attempt to re-fetch config data on page change } } } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts index 000e6d63b5999..6b5f4a05b3aa6 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts @@ -25,41 +25,6 @@ describe('credentials routes', () => { it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/credentials/collection', - hasValidData: expect.any(Function), - }); - }); - - describe('hasValidData', () => { - it('should correctly validate that a response has data', () => { - const response = { - meta: { - page: { - current: 1, - total_pages: 1, - total_results: 1, - size: 25, - }, - }, - results: [ - { - id: 'loco_moco_account_id:5f3575de2b76ff13405f3155|name:asdfasdf', - key: 'search-fe49u2z8d5gvf9s4ekda2ad4', - name: 'asdfasdf', - type: 'search', - access_all_engines: true, - }, - ], - }; - - expect(mockRequestHandler.hasValidData(response)).toBe(true); - }); - - it('should correctly validate that a response does not have data', () => { - const response = { - foo: 'bar', - }; - - expect(mockRequestHandler.hasValidData(response)).toBe(false); }); }); @@ -75,4 +40,52 @@ describe('credentials routes', () => { }); }); }); + + describe('GET /api/app_search/credentials/details', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ method: 'get', payload: 'query' }); + + registerCredentialsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/credentials/details', + }); + }); + }); + + describe('DELETE /api/app_search/credentials/{name}', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ method: 'delete', payload: 'params' }); + + registerCredentialsRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request to enterprise search', () => { + const mockRequest = { + params: { + name: 'abc123', + }, + }; + + mockRouter.callRoute(mockRequest); + + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/credentials/abc123', + }); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts index 432f54c8e5b1c..0f2c1133192c5 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts @@ -8,25 +8,6 @@ import { schema } from '@kbn/config-schema'; import { IRouteDependencies } from '../../plugin'; -interface ICredential { - id: string; - key: string; - name: string; - type: string; - access_all_engines: boolean; -} -interface ICredentialsResponse { - results: ICredential[]; - meta?: { - page?: { - current: number; - total_results: number; - total_pages: number; - size: number; - }; - }; -} - export function registerCredentialsRoutes({ router, enterpriseSearchRequestHandler, @@ -42,9 +23,30 @@ export function registerCredentialsRoutes({ }, enterpriseSearchRequestHandler.createRequest({ path: '/as/credentials/collection', - hasValidData: (body?: ICredentialsResponse) => { - return Array.isArray(body?.results) && typeof body?.meta?.page?.total_results === 'number'; - }, }) ); + router.get( + { + path: '/api/app_search/credentials/details', + validate: false, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/credentials/details', + }) + ); + router.delete( + { + path: '/api/app_search/credentials/{name}', + validate: { + params: schema.object({ + name: schema.string(), + }), + }, + }, + async (context, request, response) => { + return enterpriseSearchRequestHandler.createRequest({ + path: `/as/credentials/${request.params.name}`, + })(context, request, response); + } + ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.test.ts index acddd3539965a..bd6f4b9da91fd 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.test.ts @@ -35,7 +35,7 @@ describe('Enterprise Search Telemetry API', () => { }); }); - describe('PUT /api/enterprise_search/telemetry', () => { + describe('PUT /api/enterprise_search/stats', () => { it('increments the saved objects counter for App Search', async () => { (incrementUICounter as jest.Mock).mockImplementation(jest.fn(() => successResponse)); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts index bfc07c8b64ef5..8f6638ddc099e 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/telemetry.ts @@ -25,7 +25,7 @@ export function registerTelemetryRoute({ }: IRouteDependencies) { router.put( { - path: '/api/enterprise_search/telemetry', + path: '/api/enterprise_search/stats', validate: { body: schema.object({ product: schema.oneOf([ diff --git a/x-pack/plugins/file_upload/public/util/indexing_service.js b/x-pack/plugins/file_upload/public/util/indexing_service.js index eb22b0228b48a..28cdb602455b5 100644 --- a/x-pack/plugins/file_upload/public/util/indexing_service.js +++ b/x-pack/plugins/file_upload/public/util/indexing_service.js @@ -189,19 +189,16 @@ async function chunkDataAndWriteToIndex({ id, index, data, mappings, settings }) } export async function createIndexPattern(indexPatternName) { - const indexPatterns = await indexPatternService.get(); try { - Object.assign(indexPatterns, { - id: '', - title: indexPatternName, - }); - - await indexPatterns.create(true); - const id = await getIndexPatternId(indexPatternName); - const indexPattern = await indexPatternService.get(id); + const indexPattern = await indexPatternService.createAndSave( + { + title: indexPatternName, + }, + true + ); return { success: true, - id, + id: indexPattern.id, fields: indexPattern.fields, }; } catch (error) { @@ -212,18 +209,6 @@ export async function createIndexPattern(indexPatternName) { } } -async function getIndexPatternId(name) { - const savedObjectSearch = await savedObjectsClient.find({ type: 'index-pattern', perPage: 1000 }); - const indexPatternSavedObjects = savedObjectSearch.savedObjects; - - if (indexPatternSavedObjects) { - const ip = indexPatternSavedObjects.find((i) => i.attributes.title === name); - return ip !== undefined ? ip.id : undefined; - } else { - return undefined; - } -} - export const getExistingIndexNames = async () => { const indexes = await httpService({ url: `/api/index_management/indices`, diff --git a/x-pack/plugins/global_search/server/services/context.mock.ts b/x-pack/plugins/global_search/server/services/context.mock.ts index 7c72686529c15..325d6ea5c6d19 100644 --- a/x-pack/plugins/global_search/server/services/context.mock.ts +++ b/x-pack/plugins/global_search/server/services/context.mock.ts @@ -4,14 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { of } from 'rxjs'; +import { Capabilities } from 'src/core/server'; import { savedObjectsTypeRegistryMock, savedObjectsClientMock, elasticsearchServiceMock, uiSettingsServiceMock, + capabilitiesServiceMock, } from '../../../../../src/core/server/mocks'; -const createContextMock = () => { +const createContextMock = (capabilities: Partial = {}) => { return { core: { savedObjects: { @@ -26,6 +29,10 @@ const createContextMock = () => { uiSettings: { client: uiSettingsServiceMock.createClient(), }, + capabilities: of({ + ...capabilitiesServiceMock.createCapabilities(), + ...capabilities, + } as Capabilities), }, }; }; diff --git a/x-pack/plugins/global_search/server/services/context.test.ts b/x-pack/plugins/global_search/server/services/context.test.ts index 397a1ea170349..640f4c11b363f 100644 --- a/x-pack/plugins/global_search/server/services/context.test.ts +++ b/x-pack/plugins/global_search/server/services/context.test.ts @@ -27,11 +27,15 @@ describe('getContextFactory', () => { expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalledTimes(1); expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalledWith(soClient); + expect(coreStart.capabilities.resolveCapabilities).toHaveBeenCalledTimes(1); + expect(coreStart.capabilities.resolveCapabilities).toHaveBeenCalledWith(request); + expect(context).toEqual({ core: { savedObjects: expect.any(Object), elasticsearch: expect.any(Object), uiSettings: expect.any(Object), + capabilities: expect.any(Object), }, }); }); diff --git a/x-pack/plugins/global_search/server/services/context.ts b/x-pack/plugins/global_search/server/services/context.ts index b15deccaae018..62fddcfb152b3 100644 --- a/x-pack/plugins/global_search/server/services/context.ts +++ b/x-pack/plugins/global_search/server/services/context.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { from } from 'rxjs'; import { CoreStart, KibanaRequest } from 'src/core/server'; import { GlobalSearchProviderContext } from '../types'; @@ -30,6 +31,7 @@ export const getContextFactory = (coreStart: CoreStart) => ( uiSettings: { client: coreStart.uiSettings.asScopedToClient(soClient), }, + capabilities: from(coreStart.capabilities.resolveCapabilities(request)), }, }; }; diff --git a/x-pack/plugins/global_search/server/types.ts b/x-pack/plugins/global_search/server/types.ts index 7d3f5ebc5d079..07d21f54d7bf5 100644 --- a/x-pack/plugins/global_search/server/types.ts +++ b/x-pack/plugins/global_search/server/types.ts @@ -10,6 +10,7 @@ import { ILegacyScopedClusterClient, IUiSettingsClient, SavedObjectsClientContract, + Capabilities, } from 'src/core/server'; import { GlobalSearchBatchedResults, @@ -52,6 +53,7 @@ export interface GlobalSearchProviderContext { uiSettings: { client: IUiSettingsClient; }; + capabilities: Observable; }; } diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index 54066cee414d8..0dde28db0436d 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -109,7 +109,7 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) { complete: () => {}, }); }, - 250, + 350, [searchValue] ); @@ -161,6 +161,9 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) { defaultMessage: 'Search Elastic', }), }} + popoverProps={{ + repositionOnScroll: true, + }} emptyMessage={

diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts index 0085331c5be5f..8798fe6694c96 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.test.ts @@ -4,7 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsFindResult, SavedObjectsType, SavedObjectTypeRegistry } from 'src/core/server'; +import { + SavedObjectsFindResult, + SavedObjectsType, + SavedObjectTypeRegistry, + Capabilities, +} from 'src/core/server'; import { mapToResult, mapToResults } from './map_object_to_result'; const createType = (props: Partial): SavedObjectsType => { @@ -111,18 +116,17 @@ describe('mapToResult', () => { describe('mapToResults', () => { let typeRegistry: SavedObjectTypeRegistry; + let capabilities: Capabilities; beforeEach(() => { typeRegistry = new SavedObjectTypeRegistry(); - }); - it('converts savedObjects to results', () => { typeRegistry.registerType( createType({ name: 'typeA', management: { defaultSearchField: 'title', - getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }), + getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: 'test.typeA' }), }, }) ); @@ -131,7 +135,7 @@ describe('mapToResults', () => { name: 'typeB', management: { defaultSearchField: 'description', - getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }), + getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'test.typeB' }), }, }) ); @@ -140,11 +144,37 @@ describe('mapToResults', () => { name: 'typeC', management: { defaultSearchField: 'excerpt', - getInAppUrl: (obj) => ({ path: `/type-c/${obj.id}`, uiCapabilitiesPath: 'bar' }), + getInAppUrl: (obj) => ({ path: `/type-c/${obj.id}`, uiCapabilitiesPath: 'test.typeC' }), }, }) ); + typeRegistry.registerType( + createType({ + name: 'inaccessibleType', + management: { + defaultSearchField: 'excerpt', + getInAppUrl: (obj) => ({ + path: `/inaccessible-type/${obj.id}`, + uiCapabilitiesPath: 'test.inaccessibleType', + }), + }, + }) + ); + + capabilities = { + navLinks: {}, + management: {}, + catalogue: {}, + test: { + typeA: true, + typeB: true, + typeC: true, + inacessibleType: false, + }, + }; + }); + it('converts savedObjects to results', () => { const results = [ createObject( { @@ -181,7 +211,7 @@ describe('mapToResults', () => { ), ]; - expect(mapToResults(results, typeRegistry)).toEqual([ + expect(mapToResults(results, typeRegistry, capabilities)).toEqual([ { id: 'resultA', title: 'titleA', @@ -205,4 +235,41 @@ describe('mapToResults', () => { }, ]); }); + + it('filters results without permissions', () => { + const results = [ + createObject( + { + id: 'resultA', + type: 'typeA', + score: 100, + }, + { + title: 'titleA', + field: 'noise', + } + ), + createObject( + { + id: 'inaccessibleResult', + type: 'inaccessibleType', + score: 92, + }, + { + excerpt: 'inaccessibleTitle', + title: 'inaccessible', + } + ), + ]; + + expect(mapToResults(results, typeRegistry, capabilities)).toEqual([ + { + id: 'resultA', + title: 'titleA', + type: 'typeA', + url: '/type-a/resultA', + score: 100, + }, + ]); + }); }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts index c93558b1a3cf4..14641e1aaffff 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/map_object_to_result.ts @@ -4,18 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ +import get from 'lodash/get'; import { SavedObjectsType, ISavedObjectTypeRegistry, SavedObjectsFindResult, + Capabilities, } from 'src/core/server'; import { GlobalSearchProviderResult } from '../../../../global_search/server'; export const mapToResults = ( objects: Array>, - registry: ISavedObjectTypeRegistry + registry: ISavedObjectTypeRegistry, + capabilities: Capabilities ): GlobalSearchProviderResult[] => { - return objects.map((obj) => mapToResult(obj, registry.getType(obj.type)!)); + return objects + .filter((obj) => isAccessible(obj, registry.getType(obj.type)!, capabilities)) + .map((obj) => mapToResult(obj, registry.getType(obj.type)!)); +}; + +const isAccessible = ( + object: SavedObjectsFindResult, + type: SavedObjectsType, + capabilities: Capabilities +): boolean => { + const { getInAppUrl } = type.management ?? {}; + if (getInAppUrl === undefined) { + throw new Error('Trying to map an object from a type without management metadata'); + } + const { uiCapabilitiesPath } = getInAppUrl(object); + return Boolean(get(capabilities, uiCapabilitiesPath) ?? false); }; export const mapToResult = ( diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts index 11e3a40ddee17..352191658ed0d 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.test.ts @@ -33,16 +33,20 @@ const createFindResponse = ( total: results.length, }); -const createType = (props: Partial): SavedObjectsType => { +const createType = ( + props: Pick & Partial +): SavedObjectsType => { return { - name: 'type', hidden: false, namespaceType: 'single', mappings: { properties: {} }, ...props, management: { defaultSearchField: 'field', - getInAppUrl: (obj) => ({ path: `/object/${obj.id}`, uiCapabilitiesPath: '' }), + getInAppUrl: (obj) => ({ + path: `/object/${obj.id}`, + uiCapabilitiesPath: `types.${props.name}`, + }), ...props.management, }, }; @@ -82,7 +86,7 @@ describe('savedObjectsResultProvider', () => { name: 'typeA', management: { defaultSearchField: 'title', - getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: '' }), + getInAppUrl: (obj) => ({ path: `/type-a/${obj.id}`, uiCapabilitiesPath: 'test.typeA' }), }, }) ); @@ -91,12 +95,17 @@ describe('savedObjectsResultProvider', () => { name: 'typeB', management: { defaultSearchField: 'description', - getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'foo' }), + getInAppUrl: (obj) => ({ path: `/type-b/${obj.id}`, uiCapabilitiesPath: 'test.typeB' }), }, }) ); - context = globalSearchPluginMock.createProviderContext(); + context = globalSearchPluginMock.createProviderContext({ + test: { + typeA: true, + typeB: true, + }, + }); context.core.savedObjects.client.find.mockResolvedValue(createFindResponse([])); context.core.savedObjects.typeRegistry = registry as any; }); diff --git a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts index 8a3d3d732531f..1c79380fe17fd 100644 --- a/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts +++ b/x-pack/plugins/global_search_providers/server/providers/saved_objects/provider.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { from } from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { from, combineLatest } from 'rxjs'; +import { map, takeUntil, first } from 'rxjs/operators'; import { GlobalSearchResultProvider } from '../../../../global_search/server'; import { mapToResults } from './map_object_to_result'; @@ -13,7 +13,10 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = return { id: 'savedObjects', find: (term, { aborted$, maxResults, preference }, { core }) => { - const { typeRegistry, client } = core.savedObjects; + const { + capabilities, + savedObjects: { client, typeRegistry }, + } = core; const searchableTypes = typeRegistry .getVisibleTypes() @@ -31,9 +34,9 @@ export const createSavedObjectsResultProvider = (): GlobalSearchResultProvider = type: searchableTypes.map((type) => type.name), }); - return from(responsePromise).pipe( + return combineLatest([from(responsePromise), capabilities.pipe(first())]).pipe( takeUntil(aborted$), - map((res) => mapToResults(res.saved_objects, typeRegistry)) + map(([res, cap]) => mapToResults(res.saved_objects, typeRegistry, cap)) ); }, }; diff --git a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js index 75569f472e219..b523dc3c1ba6c 100644 --- a/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js +++ b/x-pack/plugins/grokdebugger/public/components/grok_debugger/grok_debugger.js @@ -5,6 +5,8 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import isEmpty from 'lodash/isEmpty'; import './brace_imports'; diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md index 3b72ac85810c6..28b2a4637da89 100644 --- a/x-pack/plugins/index_lifecycle_management/README.md +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -1,6 +1,8 @@ # Index Lifecycle Management -## Quick steps for testing ILM in Index Management +## Testing + +### Quick steps for testing ILM in Index Management You can test that the `Frozen` badge, phase filtering, and lifecycle information is surfaced in Index Management by running this series of requests in Console: @@ -92,4 +94,26 @@ After about a minute, there should be an error on this index. When you click the ILM information in the detail panel as well as an error. You can dismiss the error by clicking `Manage > Retry lifecycle step`. -![image](https://user-images.githubusercontent.com/1238659/78087984-a6811000-7377-11ea-880e-1a7b182c14f1.png) \ No newline at end of file +![image](https://user-images.githubusercontent.com/1238659/78087984-a6811000-7377-11ea-880e-1a7b182c14f1.png) + +### Data tier notifications + +When creating or editing an ILM policy the UI should notify users that under certain conditions their data will not be +moved to a tier corresponding to a phase. For instance, when a cluster only has hot-tier nodes. We test the UI +with this cluster state by starting an ES node with the `data_hot` role. Using this command: + +```bash +yarn es snapshot --license=trial -E node.roles=data_hot,master,data_content +``` + +This will create a cluster where we have a single node that belongs to the hot-tier. In the data allocation section of +both the warm and cold phase you should see notice like the following: + +![image](https://user-images.githubusercontent.com/8155004/94132944-4b306600-fe60-11ea-9c3d-02229e3055b8.png) + +Default configuration for a node is that it belongs to all tiers, in which case you should not see this notice. Test +this by running: + +```bash +yarn es snapshot --license=trial +``` \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index f195228775772..dfbe19ba21a94 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -503,6 +503,30 @@ describe('edit policy', () => { expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); expect(findTestSubject(rendered, 'defaultAllocationWarning').exists()).toBeTruthy(); }); + test('should show default allocation notice when hot tier exists, but not warm tier', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeTruthy(); + }); + test('should not show default allocation notice when node with "data" role exists', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + }); }); describe('cold phase', () => { beforeEach(() => { @@ -610,6 +634,30 @@ describe('edit policy', () => { expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); expect(findTestSubject(rendered, 'defaultAllocationWarning').exists()).toBeTruthy(); }); + test('should show default allocation notice when warm or hot tiers exists, but not cold tier', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeTruthy(); + }); + test('should not show default allocation notice when node with "data" role exists', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + }); }); describe('delete phase', () => { test('should allow 0 for phase timing', async () => { diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts new file mode 100644 index 0000000000000..8a1acf72949e6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.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. + */ + +// Order of node roles matters here, the warm phase prefers allocating data +// to the data_warm role. +import { NodeDataRole, PhaseWithAllocation } from '../types'; + +const WARM_PHASE_NODE_PREFERENCE: NodeDataRole[] = ['data_warm', 'data_hot']; + +const COLD_PHASE_NODE_PREFERENCE: NodeDataRole[] = ['data_cold', 'data_warm', 'data_hot']; + +export const phaseToNodePreferenceMap: Record = Object.freeze({ + warm: WARM_PHASE_NODE_PREFERENCE, + cold: COLD_PHASE_NODE_PREFERENCE, +}); diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts index 5c89b917163d8..522dc6d82a4e9 100644 --- a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts @@ -7,6 +7,8 @@ import { i18n } from '@kbn/i18n'; import { LicenseType } from '../../../licensing/common/types'; +export { phaseToNodePreferenceMap } from './data_tiers'; + const basicLicense: LicenseType = 'basic'; export const PLUGIN = { diff --git a/x-pack/plugins/index_lifecycle_management/common/types/api.ts b/x-pack/plugins/index_lifecycle_management/common/types/api.ts index 16b8fbd127ab6..fcdbdf2c9cc90 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/api.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/api.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export type NodeDataRole = 'data' | 'data_hot' | 'data_warm' | 'data_cold' | 'data_frozen'; +import { NodeDataRoleWithCatchAll } from '.'; export interface ListNodesRouteResponse { nodesByAttributes: { [attributePair: string]: string[] }; - nodesByRoles: { [role in NodeDataRole]?: string[] }; + nodesByRoles: { [role in NodeDataRoleWithCatchAll]?: string[] }; } diff --git a/x-pack/plugins/index_lifecycle_management/common/types/index.ts b/x-pack/plugins/index_lifecycle_management/common/types/index.ts index a23dc647f1f65..1f41370e48f18 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/index.ts @@ -7,3 +7,10 @@ export * from './api'; export * from './policies'; + +/** + * These roles reflect how nodes are stratified into different data tiers. The "data" role + * is a catch-all that can be used to store data in any phase. + */ +export type NodeDataRole = 'data_hot' | 'data_warm' | 'data_cold'; +export type NodeDataRoleWithCatchAll = 'data' | NodeDataRole; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts deleted file mode 100644 index 2ef0fb145551f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts +++ /dev/null @@ -1,36 +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 { - NodeDataRole, - ListNodesRouteResponse, - PhaseWithAllocation, -} from '../../../../common/types'; - -/** - * Given a phase and current node roles, determine whether the phase - * can use default data tier allocation. - * - * This can only be checked for phases that have an allocate action. - */ -export const isPhaseDefaultDataAllocationCompatible = ( - phase: PhaseWithAllocation, - nodesByRoles: ListNodesRouteResponse['nodesByRoles'] -): boolean => { - // The 'data' role covers all node roles, so if we have at least one node with the data role - // we can use default allocation. - if (nodesByRoles.data?.length) { - return true; - } - - // Otherwise we need to check whether a node role for the specific phase exists - if (nodesByRoles[`data_${phase}` as NodeDataRole]?.length) { - return true; - } - - // Otherwise default allocation has nowhere to allocate new shards to in this phase. - return false; -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts new file mode 100644 index 0000000000000..6daae57330886 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.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 { + NodeDataRole, + ListNodesRouteResponse, + PhaseWithAllocation, +} from '../../../../common/types'; + +import { phaseToNodePreferenceMap } from '../../../../common/constants'; + +export type AllocationNodeRole = NodeDataRole | 'none'; + +/** + * Given a phase and current cluster node roles, determine which nodes the phase + * will allocate data to. For instance, for the warm phase, with warm + * tier nodes, we would expect "data_warm". + * + * If no nodes can be identified for allocation (very special case) then + * we return "none". + */ +export const getAvailableNodeRoleForPhase = ( + phase: PhaseWithAllocation, + nodesByRoles: ListNodesRouteResponse['nodesByRoles'] +): AllocationNodeRole => { + const preferredNodeRoles = phaseToNodePreferenceMap[phase]; + + // The 'data' role covers all node roles, so if we have at least one node with the data role + // we can allocate to our first preference. + if (nodesByRoles.data?.length) { + return preferredNodeRoles[0]; + } + + return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)) ?? 'none'; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts index 67a512cefe00c..87f2cbc08ecc0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts @@ -6,4 +6,4 @@ export * from './determine_allocation_type'; -export * from './check_phase_compatibility'; +export * from './get_available_node_roles_for_phase'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.ts new file mode 100644 index 0000000000000..872efa740b131 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.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 { NodeDataRole, PhaseWithAllocation } from '../../../../common/types'; +import { phaseToNodePreferenceMap } from '../../../../common/constants'; + +export const isNodeRoleFirstPreference = (phase: PhaseWithAllocation, nodeRole: NodeDataRole) => { + return phaseToNodePreferenceMap[phase][0] === nodeRole; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx new file mode 100644 index 0000000000000..8faa9bb2972c2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; + +import { PhaseWithAllocation, NodeDataRole } from '../../../../../../common/types'; + +import { AllocationNodeRole } from '../../../../lib'; + +const i18nTextsNodeRoleToDataTier: Record = { + data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { + defaultMessage: 'hot', + }), + data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { + defaultMessage: 'warm', + }), + data_cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel', { + defaultMessage: 'cold', + }), +}; + +const i18nTexts = { + notice: { + warm: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title', + { defaultMessage: 'No nodes assigned to the warm tier' } + ), + body: (nodeRole: NodeDataRole) => + i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm', { + defaultMessage: + 'This policy will move data in the warm phase to {tier} tier nodes instead.', + values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, + }), + }, + cold: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title', + { defaultMessage: 'No nodes assigned to the cold tier' } + ), + body: (nodeRole: NodeDataRole) => + i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold', { + defaultMessage: + 'This policy will move data in the cold phase to {tier} tier nodes instead.', + values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, + }), + }, + }, + warning: { + warm: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', + { defaultMessage: 'No nodes assigned to the warm tier' } + ), + body: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', + { + defaultMessage: + 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + } + ), + }, + cold: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', + { defaultMessage: 'No nodes assigned to the cold tier' } + ), + body: i18n.translate( + 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', + { + defaultMessage: + 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + } + ), + }, + }, +}; + +interface Props { + phase: PhaseWithAllocation; + targetNodeRole: AllocationNodeRole; +} + +export const DefaultAllocationNotice: FunctionComponent = ({ phase, targetNodeRole }) => { + const content = + targetNodeRole === 'none' ? ( + + {i18nTexts.warning[phase].body} + + ) : ( + + {i18nTexts.notice[phase].body(targetNodeRole)} + + ); + + return ( + <> + + {content} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx deleted file mode 100644 index 5aba411b6fe53..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx +++ /dev/null @@ -1,59 +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 { i18n } from '@kbn/i18n'; -import React, { FunctionComponent } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; - -import { PhaseWithAllocation } from '../../../../../../common/types'; - -const i18nTexts = { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the warm tier to use role-based allocation. The policy will fail to complete allocation if there are no warm nodes.', - } - ), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the cold tier to use role-based allocation. The policy will fail to complete allocation if there are no cold nodes.', - } - ), - }, -}; - -interface Props { - phase: PhaseWithAllocation; -} - -export const DefaultAllocationWarning: FunctionComponent = ({ phase }) => { - return ( - <> - - - {i18nTexts[phase].body} - - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts index 26464a75ae14c..dcbdf960fd380 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts @@ -8,5 +8,5 @@ export { NodesDataProvider } from './node_data_provider'; export { NodeAllocation } from './node_allocation'; export { NodeAttrsDetails } from './node_attrs_details'; export { DataTierAllocation } from './data_tier_allocation'; -export { DefaultAllocationWarning } from './default_allocation_warning'; +export { DefaultAllocationNotice } from './default_allocation_notice'; export { NoNodeAttributesWarning } from './no_node_attributes_warning'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts index 2428cade0898e..c39545112ee52 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts @@ -18,7 +18,7 @@ export { NodeAllocation, NodeAttrsDetails, NodesDataProvider, - DefaultAllocationWarning, + DefaultAllocationNotice, } from './data_tier_allocation'; export { DescribedFormField } from './described_form_field'; export { Forcemerge } from './forcemerge'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx index 241a98fffa6df..b9b1b8b663ec8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx @@ -33,7 +33,7 @@ const i18nTexts = { dataTierAllocation: { description: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.description', { defaultMessage: - 'Move data to data nodes optimized for less frequent, read-only access. Store cold data on less-expensive hardware.', + 'Move data to nodes optimized for less frequent, read-only access. Store data in the cold phase on less-expensive hardware.', }), }, }; @@ -192,8 +192,7 @@ export const ColdPhase: FunctionComponent = ({ {' '} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index 6475e5286a778..623d443a1db01 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -9,15 +9,16 @@ import { i18n } from '@kbn/i18n'; import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; import { PhaseWithAllocationAction, PhaseWithAllocation } from '../../../../../../common/types'; +import { PhaseValidationErrors } from '../../../../services/policies/policy_validation'; +import { getAvailableNodeRoleForPhase } from '../../../../lib/data_tiers'; +import { isNodeRoleFirstPreference } from '../../../../lib/data_tiers/is_node_role_first_preference'; import { DataTierAllocation, - DefaultAllocationWarning, + DefaultAllocationNotice, NoNodeAttributesWarning, NodesDataProvider, } from '../../components/data_tier_allocation'; -import { PhaseValidationErrors } from '../../../../services/policies/policy_validation'; -import { isPhaseDefaultDataAllocationCompatible } from '../../../../lib/data_tiers'; const i18nTexts = { title: i18n.translate('xpack.indexLifecycleMgmt.common.dataTier.title', { @@ -48,9 +49,34 @@ export const DataTierAllocationField: FunctionComponent = ({ return ( {(nodesData) => { - const isCompatible = isPhaseDefaultDataAllocationCompatible(phase, nodesData.nodesByRoles); const hasNodeAttrs = Boolean(Object.keys(nodesData.nodesByAttributes ?? {}).length); + const renderDefaultAllocationNotice = () => { + if (phaseData.dataTierAllocationType !== 'default') { + return null; + } + + const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesData.nodesByRoles); + if ( + allocationNodeRole !== 'none' && + isNodeRoleFirstPreference(phase, allocationNodeRole) + ) { + return null; + } + + return ; + }; + + const renderNodeAttributesWarning = () => { + if (phaseData.dataTierAllocationType !== 'custom') { + return null; + } + if (hasNodeAttrs) { + return null; + } + return ; + }; + return ( {i18nTexts.title}} @@ -70,14 +96,8 @@ export const DataTierAllocationField: FunctionComponent = ({ /> {/* Data tier related warnings */} - - {phaseData.dataTierAllocationType === 'default' && !isCompatible && ( - - )} - - {phaseData.dataTierAllocationType === 'custom' && !hasNodeAttrs && ( - - )} + {renderDefaultAllocationNotice()} + {renderNodeAttributesWarning()} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx index 16a740b1171c9..b837eed1256c5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx @@ -45,8 +45,7 @@ const i18nTexts = { ), dataTierAllocation: { description: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.description', { - defaultMessage: - 'Move warm data to nodes optimized for read-only access. Store warm data on less-expensive hardware.', + defaultMessage: 'Move data to nodes optimized for less-frequent, read-only access.', }), }, }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/services/policies/policy_serialization.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/services/policies/policy_serialization.test.ts index 753ffb111cf13..222887069a0dc 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/services/policies/policy_serialization.test.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/services/policies/policy_serialization.test.ts @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import cloneDeep from 'lodash/cloneDeep'; import { serializePolicy } from './policy_serialization'; import { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/services/policies/shared/serialize_phase_with_allocation.ts b/x-pack/plugins/index_lifecycle_management/public/application/services/policies/shared/serialize_phase_with_allocation.ts index 5a9db3069aea6..c29ae3ab22831 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/services/policies/shared/serialize_phase_with_allocation.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/services/policies/shared/serialize_phase_with_allocation.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import cloneDeep from 'lodash/cloneDeep'; import { diff --git a/x-pack/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx index 6fbe177d24e06..22e6f09907d75 100644 --- a/x-pack/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -8,7 +8,7 @@ import React, { createContext, useContext } from 'react'; import { ScopedHistory } from 'kibana/public'; import { ManagementAppMountParams } from 'src/plugins/management/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; -import { CoreStart } from '../../../../../src/core/public'; +import { CoreSetup, CoreStart } from '../../../../../src/core/public'; import { IngestManagerSetup } from '../../../ingest_manager/public'; import { IndexMgmtMetricsType } from '../types'; @@ -34,6 +34,7 @@ export interface AppDependencies { }; history: ScopedHistory; setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; + uiSettings: CoreSetup['uiSettings']; } export const AppContextProvider = ({ diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts index b3bf071948956..c47ea4a884111 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts @@ -73,6 +73,10 @@ export * from './meta_parameter'; export * from './ignore_above_parameter'; +export { RuntimeTypeParameter } from './runtime_type_parameter'; + +export { PainlessScriptParameter } from './painless_script_parameter'; + export const PARAMETER_SERIALIZERS = [relationsSerializer, dynamicSerializer]; export const PARAMETER_DESERIALIZERS = [relationsDeserializer, dynamicDeserializer]; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/painless_script_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/painless_script_parameter.tsx new file mode 100644 index 0000000000000..19746034b530c --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/painless_script_parameter.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { EuiFormRow, EuiDescribedFormGroup } from '@elastic/eui'; + +import { CodeEditor, UseField } from '../../../shared_imports'; +import { getFieldConfig } from '../../../lib'; +import { EditFieldFormRow } from '../fields/edit_field'; + +interface Props { + stack?: boolean; +} + +export const PainlessScriptParameter = ({ stack }: Props) => { + return ( + + {(scriptField) => { + const error = scriptField.getErrorsMessages(); + const isInvalid = error ? Boolean(error.length) : false; + + const field = ( + + + + ); + + const fieldTitle = i18n.translate('xpack.idxMgmt.mappingsEditor.painlessScript.title', { + defaultMessage: 'Emitted value', + }); + + const fieldDescription = i18n.translate( + 'xpack.idxMgmt.mappingsEditor.painlessScript.description', + { + defaultMessage: 'Use emit() to define the value of this runtime field.', + } + ); + + if (stack) { + return ( + + {field} + + ); + } + + return ( + {fieldTitle}} + description={fieldDescription} + fullWidth={true} + > + {field} + + ); + }} + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx index 6575fe1fac7b8..62810df44b5af 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx @@ -93,17 +93,17 @@ export const PathParameter = ({ field, allFields }: Props) => { <> {!Boolean(suggestedFields.length) && ( <> - -

- {i18n.translate( - 'xpack.idxMgmt.mappingsEditor.aliasType.noFieldsAddedWarningMessage', - { - defaultMessage: - 'You need to add at least one field before creating an alias.', - } - )} -

- + )} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/runtime_type_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/runtime_type_parameter.tsx new file mode 100644 index 0000000000000..4bdb15af5e7d9 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/runtime_type_parameter.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFormRow, + EuiComboBox, + EuiComboBoxOptionOption, + EuiDescribedFormGroup, + EuiSpacer, +} from '@elastic/eui'; + +import { UseField } from '../../../shared_imports'; +import { DataType } from '../../../types'; +import { getFieldConfig } from '../../../lib'; +import { RUNTIME_FIELD_OPTIONS, TYPE_DEFINITION } from '../../../constants'; +import { EditFieldFormRow, FieldDescriptionSection } from '../fields/edit_field'; + +interface Props { + stack?: boolean; +} + +export const RuntimeTypeParameter = ({ stack }: Props) => { + return ( + + {(runtimeTypeField) => { + const { label, value, setValue } = runtimeTypeField; + const typeDefinition = + TYPE_DEFINITION[(value as EuiComboBoxOptionOption[])[0]!.value as DataType]; + + const field = ( + <> + + + + + + + {/* Field description */} + {typeDefinition && ( + + {typeDefinition.description?.() as JSX.Element} + + )} + + ); + + const fieldTitle = i18n.translate('xpack.idxMgmt.mappingsEditor.runtimeType.title', { + defaultMessage: 'Emitted type', + }); + + const fieldDescription = i18n.translate( + 'xpack.idxMgmt.mappingsEditor.runtimeType.description', + { + defaultMessage: 'Select the type of value emitted by the runtime field.', + } + ); + + if (stack) { + return ( + + {field} + + ); + } + + return ( + {fieldTitle}} + description={fieldDescription} + fullWidth={true} + > + {field} + + ); + }} + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx index 6752bb6d6af2b..69d56032eed6a 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx @@ -56,14 +56,17 @@ export const TermVectorParameter = ({ field, defaultToggleValue }: Props) => { {formData.term_vector === 'with_positions_offsets' && ( <> - -

- {i18n.translate('xpack.idxMgmt.mappingsEditor.termVectorFieldWarningMessage', { + - + } + )} + /> )} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index a8170b1d59fbb..cff2b9af4fd10 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -14,15 +14,17 @@ import { EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector, + EuiSpacer, } from '@elastic/eui'; import { useForm, Form, FormDataProvider } from '../../../../shared_imports'; -import { EUI_SIZE } from '../../../../constants'; +import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants'; import { useDispatch } from '../../../../mappings_state_context'; import { fieldSerializer } from '../../../../lib'; -import { Field, NormalizedFields } from '../../../../types'; +import { Field, NormalizedFields, MainType } from '../../../../types'; import { NameParameter, TypeParameter, SubTypeParameter } from '../../field_parameters'; -import { getParametersFormForType } from './required_parameters_forms'; +import { FieldBetaBadge } from '../field_beta_badge'; +import { getRequiredParametersFormForType } from './required_parameters_forms'; const formWrapper = (props: any) =>

; @@ -195,18 +197,27 @@ export const CreateField = React.memo(function CreateFieldComponent({ {({ type, subType }) => { - const ParametersForm = getParametersFormForType( + const RequiredParametersForm = getRequiredParametersFormForType( type?.[0].value, subType?.[0].value ); - if (!ParametersForm) { + if (!RequiredParametersForm) { return null; } + const typeDefinition = TYPE_DEFINITION[type?.[0].value as MainType]; + return (
- + {typeDefinition.isBeta ? ( + <> + + + + ) : null} + +
); }} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts index 1112bf99713ed..5c04b2fbb336c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts @@ -11,6 +11,7 @@ import { AliasTypeRequiredParameters } from './alias_type'; import { TokenCountTypeRequiredParameters } from './token_count_type'; import { ScaledFloatTypeRequiredParameters } from './scaled_float_type'; import { DenseVectorRequiredParameters } from './dense_vector_type'; +import { RuntimeTypeRequiredParameters } from './runtime_type'; export interface ComponentProps { allFields: NormalizedFields['byId']; @@ -21,9 +22,10 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { token_count: TokenCountTypeRequiredParameters, scaled_float: ScaledFloatTypeRequiredParameters, dense_vector: DenseVectorRequiredParameters, + runtime: RuntimeTypeRequiredParameters, }; -export const getParametersFormForType = ( +export const getRequiredParametersFormForType = ( type: MainType, subType?: SubType ): ComponentType | undefined => diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/runtime_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/runtime_type.tsx new file mode 100644 index 0000000000000..54907295f8a15 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/runtime_type.tsx @@ -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 React from 'react'; + +import { RuntimeTypeParameter, PainlessScriptParameter } from '../../../field_parameters'; + +export const RuntimeTypeRequiredParameters = () => { + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx index 3b55c5ac076c2..e91a666cc4221 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx @@ -5,12 +5,13 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FormDataProvider } from '../../../../shared_imports'; -import { MainType, SubType, Field } from '../../../../types'; +import { MainType, SubType, Field, DataTypeDefinition } from '../../../../types'; import { TYPE_DEFINITION } from '../../../../constants'; import { NameParameter, TypeParameter, SubTypeParameter } from '../../field_parameters'; +import { FieldBetaBadge } from '../field_beta_badge'; import { FieldDescriptionSection } from './field_description_section'; interface Props { @@ -19,6 +20,25 @@ interface Props { isMultiField: boolean; } +const getTypeDefinition = (type: MainType, subType: SubType): DataTypeDefinition | undefined => { + if (!type) { + return; + } + + const typeDefinition = TYPE_DEFINITION[type]; + const hasSubType = typeDefinition.subTypes !== undefined; + + if (hasSubType) { + if (subType) { + return TYPE_DEFINITION[subType]; + } + + return; + } + + return typeDefinition; +}; + export const EditFieldHeaderForm = React.memo( ({ defaultValue, isRootLevelField, isMultiField }: Props) => { return ( @@ -56,27 +76,29 @@ export const EditFieldHeaderForm = React.memo( {/* Field description */} - - - {({ type, subType }) => { - if (!type) { - return null; - } - const typeDefinition = TYPE_DEFINITION[type[0].value as MainType]; - const hasSubType = typeDefinition.subTypes !== undefined; - - if (hasSubType) { - if (subType) { - const subTypeDefinition = TYPE_DEFINITION[subType as SubType]; - return (subTypeDefinition?.description?.() as JSX.Element) ?? null; - } - return null; - } + + {({ type, subType }) => { + const typeDefinition = getTypeDefinition( + type[0].value as MainType, + subType && (subType[0].value as SubType) + ); + const description = (typeDefinition?.description?.() as JSX.Element) ?? null; - return typeDefinition.description?.() as JSX.Element; - }} - - + return ( + <> + + + {typeDefinition?.isBeta && } + + + + + {description} + + + ); + }} +
); } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx index 2040d7f40d5cb..2301f7a47bf2f 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; interface Props { @@ -19,7 +19,6 @@ export const FieldDescriptionSection = ({ children, isMultiField }: Props) => { return (
- {children} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_beta_badge.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_beta_badge.tsx new file mode 100644 index 0000000000000..99c725e8a00b3 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_beta_badge.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 React from 'react'; +import { EuiBetaBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export const FieldBetaBadge = () => { + const betaText = i18n.translate('xpack.idxMgmt.mappingsEditor.fieldBetaBadgeLabel', { + defaultMessage: 'Beta', + }); + + const tooltipText = i18n.translate('xpack.idxMgmt.mappingsEditor.fieldBetaBadgeTooltip', { + defaultMessage: 'This field type is not GA. Please help us by reporting any bugs.', + }); + + return ; +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts index 6b092c5561b3b..4d36b4dd2578d 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts @@ -31,6 +31,7 @@ import { JoinType } from './join_type'; import { HistogramType } from './histogram_type'; import { ConstantKeywordType } from './constant_keyword_type'; import { RankFeatureType } from './rank_feature_type'; +import { RuntimeType } from './runtime_type'; import { WildcardType } from './wildcard_type'; import { PointType } from './point_type'; @@ -60,6 +61,7 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { histogram: HistogramType, constant_keyword: ConstantKeywordType, rank_feature: RankFeatureType, + runtime: RuntimeType, wildcard: WildcardType, point: PointType, }; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/runtime_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/runtime_type.tsx new file mode 100644 index 0000000000000..dcf5a74e0e304 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/runtime_type.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { RuntimeTypeParameter, PainlessScriptParameter } from '../../field_parameters'; +import { BasicParametersSection } from '../edit_field'; + +export const RuntimeType = () => { + return ( + + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx index 4ab0ea0fb355b..1939f09fa6762 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx @@ -16,7 +16,7 @@ import { import { i18n } from '@kbn/i18n'; import { NormalizedField, NormalizedFields } from '../../../types'; -import { getTypeLabelFromType } from '../../../lib'; +import { getTypeLabelFromField } from '../../../lib'; import { CHILD_FIELD_INDENT_SIZE, LEFT_PADDING_SIZE_FIELD_ITEM_WRAPPER } from '../../../constants'; import { FieldsList } from './fields_list'; @@ -67,6 +67,7 @@ function FieldListItemComponent( isExpanded, path, } = field; + // When there aren't any "child" fields (the maxNestedDepth === 0), there is no toggle icon on the left of any field. // For that reason, we need to compensate and substract some indent to left align on the page. const substractIndentAmount = maxNestedDepth === 0 ? CHILD_FIELD_INDENT_SIZE * 0.5 : 0; @@ -280,10 +281,10 @@ function FieldListItemComponent( ? i18n.translate('xpack.idxMgmt.mappingsEditor.multiFieldBadgeLabel', { defaultMessage: '{dataType} multi-field', values: { - dataType: getTypeLabelFromType(source.type), + dataType: getTypeLabelFromField(source), }, }) - : getTypeLabelFromType(source.type)} + : getTypeLabelFromField(source)} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx index a2d9a50f28394..a4cc4b4776e2b 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { SearchResult } from '../../../types'; import { TYPE_DEFINITION } from '../../../constants'; import { useDispatch } from '../../../mappings_state_context'; -import { getTypeLabelFromType } from '../../../lib'; +import { getTypeLabelFromField } from '../../../lib'; import { DeleteFieldProvider } from '../fields/delete_field_provider'; interface Props { @@ -121,7 +121,7 @@ export const SearchResultItem = React.memo(function FieldListItemFlatComponent({ dataType: TYPE_DEFINITION[source.type].label, }, }) - : getTypeLabelFromType(source.type)} + : getTypeLabelFromField(source)} diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx index 293ae56d57ace..7bcd8f32f1a7d 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx @@ -13,6 +13,25 @@ import { documentationService } from '../../../services/documentation'; import { MainType, SubType, DataType, DataTypeDefinition } from '../types'; export const TYPE_DEFINITION: { [key in DataType]: DataTypeDefinition } = { + runtime: { + value: 'runtime', + isBeta: true, + label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.runtimeFieldDescription', { + defaultMessage: 'Runtime', + }), + // TODO: Add this once the page exists. + // documentation: { + // main: '/runtime_field.html', + // }, + description: () => ( +

+ +

+ ), + }, text: { value: 'text', label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.textDescription', { @@ -896,6 +915,7 @@ export const MAIN_TYPES: MainType[] = [ 'range', 'rank_feature', 'rank_features', + 'runtime', 'search_as_you_type', 'shape', 'text', diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx index d16bf68b80e5d..25fdac5089b86 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx @@ -18,6 +18,7 @@ export const TYPE_NOT_ALLOWED_MULTIFIELD: DataType[] = [ 'object', 'nested', 'alias', + 'runtime', ]; export const FIELD_TYPES_OPTIONS = Object.entries(MAIN_DATA_TYPE_DEFINITION).map( @@ -27,6 +28,35 @@ export const FIELD_TYPES_OPTIONS = Object.entries(MAIN_DATA_TYPE_DEFINITION).map }) ) as ComboBoxOption[]; +export const RUNTIME_FIELD_OPTIONS = [ + { + label: 'Keyword', + value: 'keyword', + }, + { + label: 'Long', + value: 'long', + }, + { + label: 'Double', + value: 'double', + }, + { + label: 'Date', + value: 'date', + }, + { + label: 'IP', + value: 'ip', + }, + { + label: 'Boolean', + value: 'boolean', + }, +] as ComboBoxOption[]; + +export const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const; + interface SuperSelectOptionConfig { inputDisplay: string; dropdownDisplay: JSX.Element; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx index 4ffedc8ca114d..4c9786d88bfa2 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx @@ -20,6 +20,7 @@ import { import { AliasOption, DataType, + RuntimeType, ComboBoxOption, ParameterName, ParameterDefinition, @@ -27,6 +28,7 @@ import { import { documentationService } from '../../../services/documentation'; import { INDEX_DEFAULT } from './default_values'; import { TYPE_DEFINITION } from './data_types_definition'; +import { RUNTIME_FIELD_OPTIONS } from './field_options'; const { toInt } = fieldFormatters; const { emptyField, containsCharsField, numberGreaterThanField, isJsonField } = fieldValidators; @@ -185,6 +187,52 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio }, schema: t.string, }, + runtime_type: { + fieldConfig: { + type: FIELD_TYPES.COMBO_BOX, + label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.runtimeTypeLabel', { + defaultMessage: 'Type', + }), + defaultValue: 'keyword', + deserializer: (fieldType: RuntimeType | undefined) => { + if (typeof fieldType === 'string' && Boolean(fieldType)) { + const label = + RUNTIME_FIELD_OPTIONS.find(({ value }) => value === fieldType)?.label ?? fieldType; + return [ + { + label, + value: fieldType, + }, + ]; + } + return []; + }, + serializer: (value: ComboBoxOption[]) => (value.length === 0 ? '' : value[0].value), + }, + schema: t.string, + }, + script: { + fieldConfig: { + defaultValue: '', + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.idxMgmt.mappingsEditor.parameters.painlessScriptLabel', { + defaultMessage: 'Script', + }), + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.validations.scriptIsRequiredErrorMessage', + { + defaultMessage: 'Script must emit() a value.', + } + ) + ), + }, + ], + }, + schema: t.string, + }, store: { fieldConfig: { type: FIELD_TYPES.CHECKBOX, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts index 8cd1bbf0764ab..0a59cafdcef47 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/index.ts @@ -4,7 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './utils'; +export { + getUniqueId, + getChildFieldsName, + getFieldMeta, + getTypeLabelFromField, + getFieldConfig, + getTypeMetaFromSource, + normalize, + deNormalize, + updateFieldsPathAfterFieldNameChange, + getAllChildFields, + getAllDescendantAliases, + getFieldAncestors, + filterTypesForMultiField, + filterTypesForNonRootFields, + getMaxNestedDepth, + buildFieldTreeFromIds, + shouldDeleteChildFieldsAfterTypeChange, + canUseMappingsEditor, + stripUndefinedValues, +} from './utils'; export * from './serializers'; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts index bc495b05e07b7..e1988c071314e 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../constants', () => ({ MAIN_DATA_TYPE_DEFINITION: {} })); +jest.mock('../constants', () => { + const { TYPE_DEFINITION } = jest.requireActual('../constants'); + return { MAIN_DATA_TYPE_DEFINITION: {}, TYPE_DEFINITION }; +}); -import { stripUndefinedValues } from './utils'; +import { stripUndefinedValues, getTypeLabelFromField } from './utils'; describe('utils', () => { describe('stripUndefinedValues()', () => { @@ -53,4 +56,46 @@ describe('utils', () => { expect(stripUndefinedValues(dataIN)).toEqual(dataOUT); }); }); + + describe('getTypeLabelFromField()', () => { + test('returns an unprocessed label for non-runtime fields', () => { + expect( + getTypeLabelFromField({ + name: 'testField', + type: 'keyword', + }) + ).toBe('Keyword'); + }); + + test(`returns a label prepended with 'Other' for unrecognized fields`, () => { + expect( + getTypeLabelFromField({ + name: 'testField', + // @ts-ignore + type: 'hyperdrive', + }) + ).toBe('Other: hyperdrive'); + }); + + test("returns a label prepended with 'Runtime' for runtime fields", () => { + expect( + getTypeLabelFromField({ + name: 'testField', + type: 'runtime', + runtime_type: 'keyword', + }) + ).toBe('Runtime Keyword'); + }); + + test("returns a label prepended with 'Runtime Other' for unrecognized runtime fields", () => { + expect( + getTypeLabelFromField({ + name: 'testField', + type: 'runtime', + // @ts-ignore + runtime_type: 'hyperdrive', + }) + ).toBe('Runtime Other: hyperdrive'); + }); + }); }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts index 8b3ff60005305..d96f20683216a 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts @@ -71,8 +71,23 @@ export const getFieldMeta = (field: Field, isMultiField?: boolean): FieldMeta => }; }; -export const getTypeLabelFromType = (type: DataType) => - TYPE_DEFINITION[type] ? TYPE_DEFINITION[type].label : `${TYPE_DEFINITION.other.label}: ${type}`; +const getTypeLabel = (type?: DataType): string => { + return type && TYPE_DEFINITION[type] + ? TYPE_DEFINITION[type].label + : `${TYPE_DEFINITION.other.label}: ${type}`; +}; + +export const getTypeLabelFromField = (field: Field) => { + const { type, runtime_type: runtimeType } = field; + const typeLabel = getTypeLabel(type); + + if (type === 'runtime') { + const runtimeTypeLabel = getTypeLabel(runtimeType); + return `${typeLabel} ${runtimeTypeLabel}`; + } + + return typeLabel; +}; export const getFieldConfig = ( param: ParameterName, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts index 097d039527950..54b2486108183 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts @@ -51,3 +51,5 @@ export { OnJsonEditorUpdateHandler, GlobalFlyout, } from '../../../../../../../src/plugins/es_ui_shared/public'; + +export { CodeEditor } from '../../../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts index ca38a8d1e6c33..48282abd1d799 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts @@ -8,7 +8,7 @@ import { ReactNode } from 'react'; import { GenericObject } from './mappings_editor'; import { FieldConfig } from '../shared_imports'; -import { PARAMETERS_DEFINITION } from '../constants'; +import { PARAMETERS_DEFINITION, RUNTIME_FIELD_TYPES } from '../constants'; export interface DataTypeDefinition { label: string; @@ -19,6 +19,7 @@ export interface DataTypeDefinition { }; subTypes?: { label: string; types: SubType[] }; description?: () => ReactNode; + isBeta?: boolean; } export interface ParameterDefinition { @@ -35,6 +36,7 @@ export interface ParameterDefinition { } export type MainType = + | 'runtime' | 'text' | 'keyword' | 'numeric' @@ -73,6 +75,8 @@ export type SubType = NumericType | RangeType; export type DataType = MainType | SubType; +export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; + export type NumericType = | 'long' | 'integer' @@ -151,6 +155,8 @@ export type ParameterName = | 'depth_limit' | 'relations' | 'max_shingle_size' + | 'runtime_type' + | 'script' | 'value' | 'meta'; @@ -168,6 +174,7 @@ export interface Fields { interface FieldBasic { name: string; type: DataType; + runtime_type?: DataType; subType?: SubType; properties?: { [key: string]: Omit }; fields?: { [key: string]: Omit }; diff --git a/x-pack/plugins/index_management/public/application/index.tsx b/x-pack/plugins/index_management/public/application/index.tsx index f881c2e01cefc..d8b5da8361c43 100644 --- a/x-pack/plugins/index_management/public/application/index.tsx +++ b/x-pack/plugins/index_management/public/application/index.tsx @@ -11,7 +11,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { CoreStart } from '../../../../../src/core/public'; import { API_BASE_PATH } from '../../common'; -import { GlobalFlyout } from '../shared_imports'; +import { createKibanaReactContext, GlobalFlyout } from '../shared_imports'; import { AppContextProvider, AppDependencies } from './app_context'; import { App } from './app'; @@ -30,7 +30,12 @@ export const renderApp = ( const { i18n, docLinks, notifications, application } = core; const { Context: I18nContext } = i18n; - const { services, history, setBreadcrumbs } = dependencies; + const { services, history, setBreadcrumbs, uiSettings } = dependencies; + + // uiSettings is required by the CodeEditor component used to edit runtime field Painless scripts. + const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + uiSettings, + }); const componentTemplateProviderValues = { httpClient: services.httpService.httpClient, @@ -44,17 +49,19 @@ export const renderApp = ( render( - - - - - - - - - - - + + + + + + + + + + + + + , elem ); diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index 6257b68430cf0..f7b728c875762 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -41,6 +41,7 @@ export async function mountManagementSection( fatalErrors, application, chrome: { docTitle }, + uiSettings, } = core; docTitle.change(PLUGIN.getI18nName(i18n)); @@ -60,6 +61,7 @@ export async function mountManagementSection( services, history, setBreadcrumbs, + uiSettings, }; const unmountAppCallback = renderApp(element, { core, dependencies: appDependencies }); diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index d58545768732e..acb3eb512e2c1 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -44,4 +44,7 @@ export { export { isJSON } from '../../../../src/plugins/es_ui_shared/static/validators/string'; -export { reactRouterNavigate } from '../../../../src/plugins/kibana_react/public'; +export { + createKibanaReactContext, + reactRouterNavigate, +} from '../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts index 30aeeb6b45362..ae9633f3e22b9 100644 --- a/x-pack/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -84,7 +84,9 @@ export class IndexMgmtServerPlugin implements Plugin; + +// Comparators // +export enum Comparator { + GT = 'more than', + GT_OR_EQ = 'more than or equals', + LT = 'less than', + LT_OR_EQ = 'less than or equals', + EQ = 'equals', + NOT_EQ = 'does not equal', + MATCH = 'matches', + NOT_MATCH = 'does not match', + MATCH_PHRASE = 'matches phrase', + NOT_MATCH_PHRASE = 'does not match phrase', +} + +const ComparatorRT = rt.keyof({ + [Comparator.GT]: null, + [Comparator.GT_OR_EQ]: null, + [Comparator.LT]: null, + [Comparator.LT_OR_EQ]: null, + [Comparator.EQ]: null, + [Comparator.NOT_EQ]: null, + [Comparator.MATCH]: null, + [Comparator.NOT_MATCH]: null, + [Comparator.MATCH_PHRASE]: null, + [Comparator.NOT_MATCH_PHRASE]: null, +}); + +// Maps our comparators to i18n strings, some comparators have more specific wording +// depending on the field type the comparator is being used with. +export const ComparatorToi18nMap = { + [Comparator.GT]: i18n.translate('xpack.infra.logs.alerting.comparator.gt', { + defaultMessage: 'more than', + }), + [Comparator.GT_OR_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.gtOrEq', { + defaultMessage: 'more than or equals', + }), + [Comparator.LT]: i18n.translate('xpack.infra.logs.alerting.comparator.lt', { + defaultMessage: 'less than', + }), + [Comparator.LT_OR_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.ltOrEq', { + defaultMessage: 'less than or equals', + }), + [Comparator.EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.eq', { + defaultMessage: 'is', + }), + [Comparator.NOT_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.notEq', { + defaultMessage: 'is not', + }), + [`${Comparator.EQ}:number`]: i18n.translate('xpack.infra.logs.alerting.comparator.eqNumber', { + defaultMessage: 'equals', + }), + [`${Comparator.NOT_EQ}:number`]: i18n.translate( + 'xpack.infra.logs.alerting.comparator.notEqNumber', + { + defaultMessage: 'does not equal', + } + ), + [Comparator.MATCH]: i18n.translate('xpack.infra.logs.alerting.comparator.match', { + defaultMessage: 'matches', + }), + [Comparator.NOT_MATCH]: i18n.translate('xpack.infra.logs.alerting.comparator.notMatch', { + defaultMessage: 'does not match', + }), + [Comparator.MATCH_PHRASE]: i18n.translate('xpack.infra.logs.alerting.comparator.matchPhrase', { + defaultMessage: 'matches phrase', + }), + [Comparator.NOT_MATCH_PHRASE]: i18n.translate( + 'xpack.infra.logs.alerting.comparator.notMatchPhrase', + { + defaultMessage: 'does not match phrase', + } + ), +}; + +// Alert parameters // +export enum AlertStates { + OK, + ALERT, + NO_DATA, + ERROR, +} + +const ThresholdRT = rt.type({ + comparator: ComparatorRT, + value: rt.number, +}); + +export type Threshold = rt.TypeOf; + +export const CriterionRT = rt.type({ + field: rt.string, + comparator: ComparatorRT, + value: rt.union([rt.string, rt.number]), +}); + +export type Criterion = rt.TypeOf; +export const criteriaRT = rt.array(CriterionRT); +export type Criteria = rt.TypeOf; + +export const countCriteriaRT = criteriaRT; +export type CountCriteria = rt.TypeOf; + +export const ratioCriteriaRT = rt.tuple([criteriaRT, criteriaRT]); +export type RatioCriteria = rt.TypeOf; + +export const TimeUnitRT = rt.union([ + rt.literal('s'), + rt.literal('m'), + rt.literal('h'), + rt.literal('d'), +]); +export type TimeUnit = rt.TypeOf; + +export const timeSizeRT = rt.number; +export const groupByRT = rt.array(rt.string); + +const RequiredAlertParamsRT = rt.type({ + // NOTE: "count" would be better named as "threshold", but this would require a + // migration of encrypted saved objects, so we'll keep "count" until it's problematic. + count: ThresholdRT, + timeUnit: TimeUnitRT, + timeSize: timeSizeRT, +}); + +const OptionalAlertParamsRT = rt.partial({ + groupBy: groupByRT, +}); + +export const alertParamsRT = rt.intersection([ + rt.type({ + criteria: countCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); + +export type CountAlertParams = rt.TypeOf; + +export const ratioAlertParamsRT = rt.intersection([ + rt.type({ + criteria: ratioCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); + +export type RatioAlertParams = rt.TypeOf; + +export const AlertParamsRT = rt.union([alertParamsRT, ratioAlertParamsRT]); +export type AlertParams = rt.TypeOf; + +export const isRatioAlert = (criteria: AlertParams['criteria']): criteria is RatioCriteria => { + return criteria.length > 0 && Array.isArray(criteria[0]) ? true : false; +}; + +export const isRatioAlertParams = (params: AlertParams): params is RatioAlertParams => { + return isRatioAlert(params.criteria); +}; + +export const getNumerator = (criteria: RatioCriteria): Criteria => { + return criteria[0]; +}; + +export const getDenominator = (criteria: RatioCriteria): Criteria => { + return criteria[1]; +}; + +export const hasGroupBy = (alertParams: AlertParams) => { + const { groupBy } = alertParams; + return groupBy && groupBy.length > 0 ? true : false; +}; + +// Chart previews // +const chartPreviewHistogramBucket = rt.type({ + key: rt.number, + doc_count: rt.number, +}); + +// ES query responses // +export const UngroupedSearchQueryResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.intersection([ + rt.type({ + hits: rt.type({ + total: rt.type({ + value: rt.number, + }), + }), + }), + // Chart preview buckets + rt.partial({ + aggregations: rt.type({ + histogramBuckets: rt.type({ + buckets: rt.array(chartPreviewHistogramBucket), + }), + }), + }), + ]), +]); + +export type UngroupedSearchQueryResponse = rt.TypeOf; + +export const GroupedSearchQueryResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.type({ + aggregations: rt.type({ + groups: rt.intersection([ + rt.type({ + buckets: rt.array( + rt.type({ + key: rt.record(rt.string, rt.string), + doc_count: rt.number, + filtered_results: rt.intersection([ + rt.type({ + doc_count: rt.number, + }), + // Chart preview buckets + rt.partial({ + histogramBuckets: rt.type({ + buckets: rt.array(chartPreviewHistogramBucket), + }), + }), + ]), + }) + ), + }), + rt.partial({ + after_key: rt.record(rt.string, rt.string), + }), + ]), + }), + hits: rt.type({ + total: rt.type({ + value: rt.number, + }), + }), + }), +]); + +export type GroupedSearchQueryResponse = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/alerting/logs/types.ts b/x-pack/plugins/infra/common/alerting/logs/types.ts deleted file mode 100644 index 1b736f52aa7e2..0000000000000 --- a/x-pack/plugins/infra/common/alerting/logs/types.ts +++ /dev/null @@ -1,198 +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 { i18n } from '@kbn/i18n'; -import * as rt from 'io-ts'; -import { commonSearchSuccessResponseFieldsRT } from '../../utils/elasticsearch_runtime_types'; - -export const LOG_DOCUMENT_COUNT_ALERT_TYPE_ID = 'logs.alert.document.count'; - -export enum Comparator { - GT = 'more than', - GT_OR_EQ = 'more than or equals', - LT = 'less than', - LT_OR_EQ = 'less than or equals', - EQ = 'equals', - NOT_EQ = 'does not equal', - MATCH = 'matches', - NOT_MATCH = 'does not match', - MATCH_PHRASE = 'matches phrase', - NOT_MATCH_PHRASE = 'does not match phrase', -} - -const ComparatorRT = rt.keyof({ - [Comparator.GT]: null, - [Comparator.GT_OR_EQ]: null, - [Comparator.LT]: null, - [Comparator.LT_OR_EQ]: null, - [Comparator.EQ]: null, - [Comparator.NOT_EQ]: null, - [Comparator.MATCH]: null, - [Comparator.NOT_MATCH]: null, - [Comparator.MATCH_PHRASE]: null, - [Comparator.NOT_MATCH_PHRASE]: null, -}); - -// Maps our comparators to i18n strings, some comparators have more specific wording -// depending on the field type the comparator is being used with. -export const ComparatorToi18nMap = { - [Comparator.GT]: i18n.translate('xpack.infra.logs.alerting.comparator.gt', { - defaultMessage: 'more than', - }), - [Comparator.GT_OR_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.gtOrEq', { - defaultMessage: 'more than or equals', - }), - [Comparator.LT]: i18n.translate('xpack.infra.logs.alerting.comparator.lt', { - defaultMessage: 'less than', - }), - [Comparator.LT_OR_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.ltOrEq', { - defaultMessage: 'less than or equals', - }), - [Comparator.EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.eq', { - defaultMessage: 'is', - }), - [Comparator.NOT_EQ]: i18n.translate('xpack.infra.logs.alerting.comparator.notEq', { - defaultMessage: 'is not', - }), - [`${Comparator.EQ}:number`]: i18n.translate('xpack.infra.logs.alerting.comparator.eqNumber', { - defaultMessage: 'equals', - }), - [`${Comparator.NOT_EQ}:number`]: i18n.translate( - 'xpack.infra.logs.alerting.comparator.notEqNumber', - { - defaultMessage: 'does not equal', - } - ), - [Comparator.MATCH]: i18n.translate('xpack.infra.logs.alerting.comparator.match', { - defaultMessage: 'matches', - }), - [Comparator.NOT_MATCH]: i18n.translate('xpack.infra.logs.alerting.comparator.notMatch', { - defaultMessage: 'does not match', - }), - [Comparator.MATCH_PHRASE]: i18n.translate('xpack.infra.logs.alerting.comparator.matchPhrase', { - defaultMessage: 'matches phrase', - }), - [Comparator.NOT_MATCH_PHRASE]: i18n.translate( - 'xpack.infra.logs.alerting.comparator.notMatchPhrase', - { - defaultMessage: 'does not match phrase', - } - ), -}; - -export enum AlertStates { - OK, - ALERT, - NO_DATA, - ERROR, -} - -const DocumentCountRT = rt.type({ - comparator: ComparatorRT, - value: rt.number, -}); - -export type DocumentCount = rt.TypeOf; - -export const CriterionRT = rt.type({ - field: rt.string, - comparator: ComparatorRT, - value: rt.union([rt.string, rt.number]), -}); - -export type Criterion = rt.TypeOf; -export const criteriaRT = rt.array(CriterionRT); - -export const TimeUnitRT = rt.union([ - rt.literal('s'), - rt.literal('m'), - rt.literal('h'), - rt.literal('d'), -]); -export type TimeUnit = rt.TypeOf; - -export const timeSizeRT = rt.number; -export const groupByRT = rt.array(rt.string); - -export const LogDocumentCountAlertParamsRT = rt.intersection([ - rt.type({ - count: DocumentCountRT, - criteria: criteriaRT, - timeUnit: TimeUnitRT, - timeSize: timeSizeRT, - }), - rt.partial({ - groupBy: groupByRT, - }), -]); - -export type LogDocumentCountAlertParams = rt.TypeOf; - -const chartPreviewHistogramBucket = rt.type({ - key: rt.number, - doc_count: rt.number, -}); - -export const UngroupedSearchQueryResponseRT = rt.intersection([ - commonSearchSuccessResponseFieldsRT, - rt.intersection([ - rt.type({ - hits: rt.type({ - total: rt.type({ - value: rt.number, - }), - }), - }), - // Chart preview buckets - rt.partial({ - aggregations: rt.type({ - histogramBuckets: rt.type({ - buckets: rt.array(chartPreviewHistogramBucket), - }), - }), - }), - ]), -]); - -export type UngroupedSearchQueryResponse = rt.TypeOf; - -export const GroupedSearchQueryResponseRT = rt.intersection([ - commonSearchSuccessResponseFieldsRT, - rt.type({ - aggregations: rt.type({ - groups: rt.intersection([ - rt.type({ - buckets: rt.array( - rt.type({ - key: rt.record(rt.string, rt.string), - doc_count: rt.number, - filtered_results: rt.intersection([ - rt.type({ - doc_count: rt.number, - }), - // Chart preview buckets - rt.partial({ - histogramBuckets: rt.type({ - buckets: rt.array(chartPreviewHistogramBucket), - }), - }), - ]), - }) - ), - }), - rt.partial({ - after_key: rt.record(rt.string, rt.string), - }), - ]), - }), - hits: rt.type({ - total: rt.type({ - value: rt.number, - }), - }), - }), -]); - -export type GroupedSearchQueryResponse = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/index.ts b/x-pack/plugins/infra/common/http_api/infra_ml/index.ts new file mode 100644 index 0000000000000..38684cb22e237 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './results'; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/common.ts new file mode 100644 index 0000000000000..deb8e6401008d --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/common.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 * as rt from 'io-ts'; + +// [Sort field value, tiebreaker value] +export const paginationCursorRT = rt.tuple([ + rt.union([rt.string, rt.number]), + rt.union([rt.string, rt.number]), +]); + +export type PaginationCursor = rt.TypeOf; + +export const anomalyTypeRT = rt.keyof({ + metrics_hosts: null, + metrics_k8s: null, +}); + +export type AnomalyType = rt.TypeOf; + +const sortOptionsRT = rt.keyof({ + anomalyScore: null, + dataset: null, + startTime: null, +}); + +const sortDirectionsRT = rt.keyof({ + asc: null, + desc: null, +}); + +const paginationPreviousPageCursorRT = rt.type({ + searchBefore: paginationCursorRT, +}); + +const paginationNextPageCursorRT = rt.type({ + searchAfter: paginationCursorRT, +}); + +export const paginationRT = rt.intersection([ + rt.type({ + pageSize: rt.number, + }), + rt.partial({ + cursor: rt.union([paginationPreviousPageCursorRT, paginationNextPageCursorRT]), + }), +]); + +export type Pagination = rt.TypeOf; + +export const sortRT = rt.type({ + field: sortOptionsRT, + direction: sortDirectionsRT, +}); + +export type Sort = rt.TypeOf; + +export const metricRT = rt.keyof({ + memory_usage: null, + network_in: null, + network_out: null, +}); + +export type Metric = rt.TypeOf; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/index.ts new file mode 100644 index 0000000000000..efd597a043e07 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/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 * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; +export * from './common'; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts new file mode 100644 index 0000000000000..a08dd438a32c8 --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_hosts_anomalies.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +import { timeRangeRT, routeTimingMetadataRT } from '../../shared'; +import { anomalyTypeRT, paginationCursorRT, sortRT, paginationRT, metricRT } from './common'; + +export const INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH = + '/api/infra/infra_ml/results/metrics_hosts_anomalies'; + +const metricsHostAnomalyCommonFieldsRT = rt.type({ + id: rt.string, + anomalyScore: rt.number, + typical: rt.number, + actual: rt.number, + type: anomalyTypeRT, + influencers: rt.array(rt.string), + duration: rt.number, + startTime: rt.number, + jobId: rt.string, +}); +const metricsHostsAnomalyRT = metricsHostAnomalyCommonFieldsRT; + +export type MetricsHostsAnomaly = rt.TypeOf; + +export const getMetricsHostsAnomaliesSuccessReponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.intersection([ + rt.type({ + anomalies: rt.array(metricsHostsAnomalyRT), + // Signifies there are more entries backwards or forwards. If this was a request + // for a previous page, there are more previous pages, if this was a request for a next page, + // there are more next pages. + hasMoreEntries: rt.boolean, + }), + rt.partial({ + paginationCursors: rt.type({ + // The cursor to use to fetch the previous page + previousPageCursor: paginationCursorRT, + // The cursor to use to fetch the next page + nextPageCursor: paginationCursorRT, + }), + }), + ]), + }), + rt.partial({ + timing: routeTimingMetadataRT, + }), +]); + +export type GetMetricsHostsAnomaliesSuccessResponsePayload = rt.TypeOf< + typeof getMetricsHostsAnomaliesSuccessReponsePayloadRT +>; + +export const getMetricsHostsAnomaliesRequestPayloadRT = rt.type({ + data: rt.intersection([ + rt.type({ + // the ID of the source configuration + sourceId: rt.string, + // the time range to fetch the log entry anomalies from + timeRange: timeRangeRT, + }), + rt.partial({ + metric: metricRT, + // Pagination properties + pagination: paginationRT, + // Sort properties + sort: sortRT, + }), + ]), +}); + +export type GetMetricsHostsAnomaliesRequestPayload = rt.TypeOf< + typeof getMetricsHostsAnomaliesRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts new file mode 100644 index 0000000000000..7450bb39276ac --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/infra_ml/results/metrics_k8s_anomalies.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +import { timeRangeRT, routeTimingMetadataRT } from '../../shared'; +import { paginationCursorRT, anomalyTypeRT, sortRT, paginationRT, metricRT } from './common'; + +export const INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH = + '/api/infra/infra_ml/results/metrics_k8s_anomalies'; + +const metricsK8sAnomalyCommonFieldsRT = rt.type({ + id: rt.string, + anomalyScore: rt.number, + typical: rt.number, + actual: rt.number, + type: anomalyTypeRT, + influencers: rt.array(rt.string), + duration: rt.number, + startTime: rt.number, + jobId: rt.string, +}); +const metricsK8sAnomalyRT = metricsK8sAnomalyCommonFieldsRT; + +export type MetricsK8sAnomaly = rt.TypeOf; + +export const getMetricsK8sAnomaliesSuccessReponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.intersection([ + rt.type({ + anomalies: rt.array(metricsK8sAnomalyRT), + // Signifies there are more entries backwards or forwards. If this was a request + // for a previous page, there are more previous pages, if this was a request for a next page, + // there are more next pages. + hasMoreEntries: rt.boolean, + }), + rt.partial({ + paginationCursors: rt.type({ + // The cursor to use to fetch the previous page + previousPageCursor: paginationCursorRT, + // The cursor to use to fetch the next page + nextPageCursor: paginationCursorRT, + }), + }), + ]), + }), + rt.partial({ + timing: routeTimingMetadataRT, + }), +]); + +export type GetMetricsK8sAnomaliesSuccessResponsePayload = rt.TypeOf< + typeof getMetricsK8sAnomaliesSuccessReponsePayloadRT +>; + +export const getMetricsK8sAnomaliesRequestPayloadRT = rt.type({ + data: rt.intersection([ + rt.type({ + // the ID of the source configuration + sourceId: rt.string, + // the time range to fetch the log entry anomalies from + timeRange: timeRangeRT, + }), + rt.partial({ + metric: metricRT, + // Pagination properties + pagination: paginationRT, + // Sort properties + sort: sortRT, + // Dataset filters + datasets: rt.array(rt.string), + }), + ]), +}); + +export type GetMetricsK8sAnomaliesRequestPayload = rt.TypeOf< + typeof getMetricsK8sAnomaliesRequestPayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts index 15914bd1b2209..3226287d4cbde 100644 --- a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts +++ b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts @@ -5,7 +5,12 @@ */ import * as rt from 'io-ts'; -import { criteriaRT, TimeUnitRT, timeSizeRT, groupByRT } from '../../alerting/logs/types'; +import { + criteriaRT, + TimeUnitRT, + timeSizeRT, + groupByRT, +} from '../../alerting/logs/log_threshold/types'; export const LOG_ALERTS_CHART_PREVIEW_DATA_PATH = '/api/infra/log_alerts/chart_preview_data'; diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts index a01042616a872..e696477253823 100644 --- a/x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/index.ts @@ -6,6 +6,7 @@ export * from './log_entry_categories'; export * from './log_entry_category_datasets'; +export * from './log_entry_category_datasets_stats'; export * from './log_entry_category_examples'; export * from './log_entry_rate'; export * from './log_entry_examples'; diff --git a/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets_stats.ts b/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets_stats.ts new file mode 100644 index 0000000000000..4511678242f1c --- /dev/null +++ b/x-pack/plugins/infra/common/http_api/log_analysis/results/log_entry_category_datasets_stats.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +import { timeRangeRT, routeTimingMetadataRT } from '../../shared'; + +export const LOG_ANALYSIS_GET_LATEST_LOG_ENTRY_CATEGORY_DATASETS_STATS_PATH = + '/api/infra/log_analysis/results/latest_log_entry_category_datasets_stats'; + +const categorizerStatusRT = rt.keyof({ + ok: null, + warn: null, +}); + +export type CategorizerStatus = rt.TypeOf; + +/** + * request + */ + +export const getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT = rt.type({ + data: rt.type({ + // the ids of the categorization jobs + jobIds: rt.array(rt.string), + // the time range to fetch the category datasets stats for + timeRange: timeRangeRT, + // the categorizer statuses to include stats for, empty means all + includeCategorizerStatuses: rt.array(categorizerStatusRT), + }), +}); + +export type GetLatestLogEntryCategoryDatasetsStatsRequestPayload = rt.TypeOf< + typeof getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT +>; + +/** + * response + */ + +const logEntryCategoriesDatasetStatsRT = rt.type({ + categorization_status: categorizerStatusRT, + categorized_doc_count: rt.number, + dataset: rt.string, + dead_category_count: rt.number, + failed_category_count: rt.number, + frequent_category_count: rt.number, + job_id: rt.string, + log_time: rt.number, + rare_category_count: rt.number, + total_category_count: rt.number, +}); + +export type LogEntryCategoriesDatasetStats = rt.TypeOf; + +export const getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT = rt.intersection([ + rt.type({ + data: rt.type({ + datasetStats: rt.array(logEntryCategoriesDatasetStatsRT), + }), + }), + rt.partial({ + timing: routeTimingMetadataRT, + }), +]); + +export type GetLatestLogEntryCategoryDatasetsStatsSuccessResponsePayload = rt.TypeOf< + typeof getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT +>; diff --git a/x-pack/plugins/infra/common/http_api/snapshot_api.ts b/x-pack/plugins/infra/common/http_api/snapshot_api.ts index e1b8dfa4770ba..a6273fa967baf 100644 --- a/x-pack/plugins/infra/common/http_api/snapshot_api.ts +++ b/x-pack/plugins/infra/common/http_api/snapshot_api.ts @@ -99,7 +99,7 @@ export const SnapshotRequestRT = rt.intersection([ rt.type({ timerange: InfraTimerangeInputRT, metrics: rt.array(SnapshotMetricInputRT), - groupBy: SnapshotGroupByRT, + groupBy: rt.union([SnapshotGroupByRT, rt.null]), nodeType: ItemTypeRT, sourceId: rt.string, }), diff --git a/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts b/x-pack/plugins/infra/common/infra_ml/anomaly_results.ts new file mode 100644 index 0000000000000..f4497dbba5056 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/anomaly_results.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. + */ + +export const ML_SEVERITY_SCORES = { + warning: 3, + minor: 25, + major: 50, + critical: 75, +}; + +export type MLSeverityScoreCategories = keyof typeof ML_SEVERITY_SCORES; + +export const ML_SEVERITY_COLORS = { + critical: 'rgb(228, 72, 72)', + major: 'rgb(229, 113, 0)', + minor: 'rgb(255, 221, 0)', + warning: 'rgb(125, 180, 226)', +}; + +export const getSeverityCategoryForScore = ( + score: number +): MLSeverityScoreCategories | undefined => { + if (score >= ML_SEVERITY_SCORES.critical) { + return 'critical'; + } else if (score >= ML_SEVERITY_SCORES.major) { + return 'major'; + } else if (score >= ML_SEVERITY_SCORES.minor) { + return 'minor'; + } else if (score >= ML_SEVERITY_SCORES.warning) { + return 'warning'; + } else { + // Category is too low to include + return undefined; + } +}; + +export const formatAnomalyScore = (score: number) => { + return Math.round(score); +}; + +export const formatOneDecimalPlace = (number: number) => { + return Math.round(number * 10) / 10; +}; + +export const getFriendlyNameForPartitionId = (partitionId: string) => { + return partitionId !== '' ? partitionId : 'unknown'; +}; + +export const compareDatasetsByMaximumAnomalyScore = < + Dataset extends { maximumAnomalyScore: number } +>( + firstDataset: Dataset, + secondDataset: Dataset +) => firstDataset.maximumAnomalyScore - secondDataset.maximumAnomalyScore; diff --git a/x-pack/plugins/infra/common/infra_ml/index.ts b/x-pack/plugins/infra/common/infra_ml/index.ts new file mode 100644 index 0000000000000..88fbbd4f25045 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './infra_ml'; +export * from './anomaly_results'; +export * from './job_parameters'; +export * from './metrics_hosts_ml'; +export * from './metrics_k8s_ml'; diff --git a/x-pack/plugins/infra/common/infra_ml/infra_ml.ts b/x-pack/plugins/infra/common/infra_ml/infra_ml.ts new file mode 100644 index 0000000000000..680a2a0fef114 --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/infra_ml.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// combines and abstracts job and datafeed status +export type JobStatus = + | 'unknown' + | 'missing' + | 'initializing' + | 'stopped' + | 'started' + | 'finished' + | 'failed'; + +export type SetupStatus = + | { type: 'initializing' } // acquiring job statuses to determine setup status + | { type: 'unknown' } // job status could not be acquired (failed request etc) + | { type: 'required' } // setup required + | { type: 'pending' } // In the process of setting up the module for the first time or retrying, waiting for response + | { type: 'succeeded' } // setup succeeded, notifying user + | { + type: 'failed'; + reasons: string[]; + } // setup failed, notifying user + | { + type: 'skipped'; + newlyCreated?: boolean; + }; // setup is not necessary + +/** + * Maps a job status to the possibility that results have already been produced + * before this state was reached. + */ +export const isJobStatusWithResults = (jobStatus: JobStatus) => + ['started', 'finished', 'stopped', 'failed'].includes(jobStatus); + +export const isHealthyJobStatus = (jobStatus: JobStatus) => + ['started', 'finished'].includes(jobStatus); + +/** + * Maps a setup status to the possibility that results have already been + * produced before this state was reached. + */ +export const isSetupStatusWithResults = (setupStatus: SetupStatus) => + setupStatus.type === 'skipped'; + +const KIBANA_SAMPLE_DATA_INDICES = ['kibana_sample_data_logs*']; + +export const isExampleDataIndex = (indexName: string) => + KIBANA_SAMPLE_DATA_INDICES.includes(indexName); diff --git a/x-pack/plugins/infra/common/infra_ml/job_parameters.ts b/x-pack/plugins/infra/common/infra_ml/job_parameters.ts new file mode 100644 index 0000000000000..8cd1c9ea6e2ba --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/job_parameters.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const bucketSpan = 900000; + +export const categoriesMessageField = 'message'; + +export const partitionField = 'event.dataset'; + +export const getJobIdPrefix = (spaceId: string, sourceId: string) => + `kibana-metrics-ui-${spaceId}-${sourceId}-`; + +export const getJobId = (spaceId: string, sourceId: string, jobType: string) => + `${getJobIdPrefix(spaceId, sourceId)}${jobType}`; + +export const getDatafeedId = (spaceId: string, sourceId: string, jobType: string) => + `datafeed-${getJobId(spaceId, sourceId, jobType)}`; + +export const datasetFilterRT = rt.union([ + rt.strict({ + type: rt.literal('includeAll'), + }), + rt.strict({ + type: rt.literal('includeSome'), + datasets: rt.array(rt.string), + }), +]); + +export type DatasetFilter = rt.TypeOf; + +export const jobSourceConfigurationRT = rt.partial({ + indexPattern: rt.string, + timestampField: rt.string, + bucketSpan: rt.number, + datasetFilter: datasetFilterRT, +}); + +export type JobSourceConfiguration = rt.TypeOf; + +export const jobCustomSettingsRT = rt.partial({ + job_revision: rt.number, + metrics_source_config: jobSourceConfigurationRT, +}); + +export type JobCustomSettings = rt.TypeOf; + +export const combineDatasetFilters = ( + firstFilter: DatasetFilter, + secondFilter: DatasetFilter +): DatasetFilter => { + if (firstFilter.type === 'includeAll' && secondFilter.type === 'includeAll') { + return { + type: 'includeAll', + }; + } + + const includedDatasets = new Set([ + ...(firstFilter.type === 'includeSome' ? firstFilter.datasets : []), + ...(secondFilter.type === 'includeSome' ? secondFilter.datasets : []), + ]); + + return { + type: 'includeSome', + datasets: [...includedDatasets], + }; +}; + +export const filterDatasetFilter = ( + datasetFilter: DatasetFilter, + predicate: (dataset: string) => boolean +): DatasetFilter => { + if (datasetFilter.type === 'includeAll') { + return datasetFilter; + } else { + const newDatasets = datasetFilter.datasets.filter(predicate); + + if (newDatasets.length > 0) { + return { + type: 'includeSome', + datasets: newDatasets, + }; + } else { + return { + type: 'includeAll', + }; + } + } +}; diff --git a/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts b/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts new file mode 100644 index 0000000000000..d09b3be78204f --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/metrics_hosts_ml.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const metricsHostsJobTypeRT = rt.keyof({ + hosts_memory_usage: null, + hosts_network_in: null, + hosts_network_out: null, +}); + +export type MetricsHostsJobType = rt.TypeOf; + +export const metricsHostsJobTypes: MetricsHostsJobType[] = [ + 'hosts_memory_usage', + 'hosts_network_in', + 'hosts_network_out', +]; diff --git a/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts b/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts new file mode 100644 index 0000000000000..3c27dbb61a14a --- /dev/null +++ b/x-pack/plugins/infra/common/infra_ml/metrics_k8s_ml.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const metricK8sJobTypeRT = rt.keyof({ + k8s_memory_usage: null, + k8s_network_in: null, + k8s_network_out: null, +}); + +export type MetricK8sJobType = rt.TypeOf; + +export const metricsK8SJobTypes: MetricK8sJobType[] = [ + 'k8s_memory_usage', + 'k8s_network_in', + 'k8s_network_out', +]; diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts b/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts index c12137f7810d4..6453332be4f50 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/index.ts @@ -31,4 +31,5 @@ export const awsEC2: InventoryModel = { }, requiredMetrics: ['awsEC2CpuUtilization', 'awsEC2NetworkTraffic', 'awsEC2DiskIOBytes'], tooltipMetrics: ['cpu', 'rx', 'tx'], + nodeFilter: [{ term: { 'event.dataset': 'aws.ec2' } }], }; diff --git a/x-pack/plugins/infra/common/inventory_models/intl_strings.ts b/x-pack/plugins/infra/common/inventory_models/intl_strings.ts index 2a885136f4ee7..cd7409100160d 100644 --- a/x-pack/plugins/infra/common/inventory_models/intl_strings.ts +++ b/x-pack/plugins/infra/common/inventory_models/intl_strings.ts @@ -5,36 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { SnapshotMetricType } from './types'; -export const CPUUsage = i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageText', { - defaultMessage: 'CPU usage', -}); - -export const MemoryUsage = i18n.translate('xpack.infra.waffle.metricOptions.memoryUsageText', { - defaultMessage: 'Memory usage', -}); - -export const InboundTraffic = i18n.translate( - 'xpack.infra.waffle.metricOptions.inboundTrafficText', - { - defaultMessage: 'Inbound traffic', - } -); - -export const OutboundTraffic = i18n.translate( - 'xpack.infra.waffle.metricOptions.outboundTrafficText', - { - defaultMessage: 'Outbound traffic', - } -); - -export const LogRate = i18n.translate('xpack.infra.waffle.metricOptions.hostLogRateText', { - defaultMessage: 'Log rate', -}); - -export const Load = i18n.translate('xpack.infra.waffle.metricOptions.loadText', { - defaultMessage: 'Load', -}); +import { toMetricOpt } from '../snapshot_metric_i18n'; +import { SnapshotMetricType, SnapshotMetricTypeKeys } from './types'; interface Lookup { [id: string]: string; @@ -70,80 +42,9 @@ export const fieldToName = (field: string) => { return LOOKUP[field] || field; }; -export const SNAPSHOT_METRIC_TRANSLATIONS = { - cpu: i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageText', { - defaultMessage: 'CPU usage', - }), - - memory: i18n.translate('xpack.infra.waffle.metricOptions.memoryUsageText', { - defaultMessage: 'Memory usage', - }), - - rx: i18n.translate('xpack.infra.waffle.metricOptions.inboundTrafficText', { - defaultMessage: 'Inbound traffic', - }), - - tx: i18n.translate('xpack.infra.waffle.metricOptions.outboundTrafficText', { - defaultMessage: 'Outbound traffic', - }), - - logRate: i18n.translate('xpack.infra.waffle.metricOptions.hostLogRateText', { - defaultMessage: 'Log rate', - }), - - load: i18n.translate('xpack.infra.waffle.metricOptions.loadText', { - defaultMessage: 'Load', - }), - - count: i18n.translate('xpack.infra.waffle.metricOptions.countText', { - defaultMessage: 'Count', - }), - diskIOReadBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOReadBytes', { - defaultMessage: 'Disk Reads', - }), - diskIOWriteBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOWriteBytes', { - defaultMessage: 'Disk Writes', - }), - s3BucketSize: i18n.translate('xpack.infra.waffle.metricOptions.s3BucketSize', { - defaultMessage: 'Bucket Size', - }), - s3TotalRequests: i18n.translate('xpack.infra.waffle.metricOptions.s3TotalRequests', { - defaultMessage: 'Total Requests', - }), - s3NumberOfObjects: i18n.translate('xpack.infra.waffle.metricOptions.s3NumberOfObjects', { - defaultMessage: 'Number of Objects', - }), - s3DownloadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3DownloadBytes', { - defaultMessage: 'Downloads (Bytes)', - }), - s3UploadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3UploadBytes', { - defaultMessage: 'Uploads (Bytes)', - }), - rdsConnections: i18n.translate('xpack.infra.waffle.metricOptions.rdsConnections', { - defaultMessage: 'Connections', - }), - rdsQueriesExecuted: i18n.translate('xpack.infra.waffle.metricOptions.rdsQueriesExecuted', { - defaultMessage: 'Queries Executed', - }), - rdsActiveTransactions: i18n.translate('xpack.infra.waffle.metricOptions.rdsActiveTransactions', { - defaultMessage: 'Active Transactions', - }), - rdsLatency: i18n.translate('xpack.infra.waffle.metricOptions.rdsLatency', { - defaultMessage: 'Latency', - }), - sqsMessagesVisible: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesVisible', { - defaultMessage: 'Messages Available', - }), - sqsMessagesDelayed: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesDelayed', { - defaultMessage: 'Messages Delayed', - }), - sqsMessagesSent: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesSent', { - defaultMessage: 'Messages Added', - }), - sqsMessagesEmpty: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesEmpty', { - defaultMessage: 'Messages Returned Empty', - }), - sqsOldestMessage: i18n.translate('xpack.infra.waffle.metricOptions.sqsOldestMessage', { - defaultMessage: 'Oldest Message', - }), -} as Record; +const snapshotTypeKeys = Object.keys(SnapshotMetricTypeKeys) as SnapshotMetricType[]; +export const SNAPSHOT_METRIC_TRANSLATIONS = snapshotTypeKeys.reduce((result, metric) => { + const text = toMetricOpt(metric)?.text; + if (text) return { ...result, [metric]: text }; + return result; +}, {}) as Record; diff --git a/x-pack/plugins/infra/common/inventory_models/types.ts b/x-pack/plugins/infra/common/inventory_models/types.ts index 851646ef1fa12..5cc788f238365 100644 --- a/x-pack/plugins/infra/common/inventory_models/types.ts +++ b/x-pack/plugins/infra/common/inventory_models/types.ts @@ -314,7 +314,7 @@ export const ESAggregationRT = rt.union([ export const MetricsUIAggregationRT = rt.record(rt.string, ESAggregationRT); export type MetricsUIAggregation = rt.TypeOf; -export const SnapshotMetricTypeRT = rt.keyof({ +export const SnapshotMetricTypeKeys = { count: null, cpu: null, load: null, @@ -339,7 +339,8 @@ export const SnapshotMetricTypeRT = rt.keyof({ sqsMessagesEmpty: null, sqsOldestMessage: null, custom: null, -}); +}; +export const SnapshotMetricTypeRT = rt.keyof(SnapshotMetricTypeKeys); export type SnapshotMetricType = rt.TypeOf; @@ -370,4 +371,5 @@ export interface InventoryModel { metrics: InventoryMetrics; requiredMetrics: InventoryMetric[]; tooltipMetrics: SnapshotMetricType[]; + nodeFilter?: object[]; } diff --git a/x-pack/plugins/infra/common/log_analysis/index.ts b/x-pack/plugins/infra/common/log_analysis/index.ts index 22137e63ab7e7..0b4fa374a5da9 100644 --- a/x-pack/plugins/infra/common/log_analysis/index.ts +++ b/x-pack/plugins/infra/common/log_analysis/index.ts @@ -5,6 +5,7 @@ */ export * from './log_analysis'; +export * from './log_analysis_quality'; export * from './log_analysis_results'; export * from './log_entry_rate_analysis'; export * from './log_entry_categories_analysis'; diff --git a/x-pack/plugins/infra/common/log_analysis/log_analysis_quality.ts b/x-pack/plugins/infra/common/log_analysis/log_analysis_quality.ts new file mode 100644 index 0000000000000..7ffa6c172886b --- /dev/null +++ b/x-pack/plugins/infra/common/log_analysis/log_analysis_quality.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +interface ManyCategoriesWarningReason { + type: 'manyCategories'; + categoriesDocumentRatio: number; +} +interface ManyDeadCategoriesWarningReason { + type: 'manyDeadCategories'; + deadCategoriesRatio: number; +} +interface ManyRareCategoriesWarningReason { + type: 'manyRareCategories'; + rareCategoriesRatio: number; +} +interface NoFrequentCategoriesWarningReason { + type: 'noFrequentCategories'; +} +interface SingleCategoryWarningReason { + type: 'singleCategory'; +} + +export type CategoryQualityWarningReason = + | ManyCategoriesWarningReason + | ManyDeadCategoriesWarningReason + | ManyRareCategoriesWarningReason + | NoFrequentCategoriesWarningReason + | SingleCategoryWarningReason; + +export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type']; + +export interface CategoryQualityWarning { + type: 'categoryQualityWarning'; + jobId: string; + dataset: string; + reasons: CategoryQualityWarningReason[]; +} + +export type QualityWarning = CategoryQualityWarning; diff --git a/x-pack/plugins/infra/common/snapshot_metric_i18n.ts b/x-pack/plugins/infra/common/snapshot_metric_i18n.ts index 412c60fd9a1a7..60454e770584e 100644 --- a/x-pack/plugins/infra/common/snapshot_metric_i18n.ts +++ b/x-pack/plugins/infra/common/snapshot_metric_i18n.ts @@ -4,204 +4,235 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; +import { mapValues } from 'lodash'; import { SnapshotMetricType } from './inventory_models/types'; -const Translations = { +// Lowercase versions of all metrics, for when they need to be used in the middle of a sentence; +// these may need to be translated differently depending on language, e.g. still capitalizing "CPU" +const TranslationsLowercase = { CPUUsage: i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageText', { defaultMessage: 'CPU usage', }), MemoryUsage: i18n.translate('xpack.infra.waffle.metricOptions.memoryUsageText', { - defaultMessage: 'Memory usage', + defaultMessage: 'memory usage', }), InboundTraffic: i18n.translate('xpack.infra.waffle.metricOptions.inboundTrafficText', { - defaultMessage: 'Inbound traffic', + defaultMessage: 'inbound traffic', }), OutboundTraffic: i18n.translate('xpack.infra.waffle.metricOptions.outboundTrafficText', { - defaultMessage: 'Outbound traffic', + defaultMessage: 'outbound traffic', }), LogRate: i18n.translate('xpack.infra.waffle.metricOptions.hostLogRateText', { - defaultMessage: 'Log rate', + defaultMessage: 'log rate', }), Load: i18n.translate('xpack.infra.waffle.metricOptions.loadText', { - defaultMessage: 'Load', + defaultMessage: 'load', }), Count: i18n.translate('xpack.infra.waffle.metricOptions.countText', { - defaultMessage: 'Count', + defaultMessage: 'count', }), DiskIOReadBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOReadBytes', { - defaultMessage: 'Disk Reads', + defaultMessage: 'disk reads', }), DiskIOWriteBytes: i18n.translate('xpack.infra.waffle.metricOptions.diskIOWriteBytes', { - defaultMessage: 'Disk Writes', + defaultMessage: 'disk writes', }), s3BucketSize: i18n.translate('xpack.infra.waffle.metricOptions.s3BucketSize', { - defaultMessage: 'Bucket Size', + defaultMessage: 'bucket size', }), s3TotalRequests: i18n.translate('xpack.infra.waffle.metricOptions.s3TotalRequests', { - defaultMessage: 'Total Requests', + defaultMessage: 'total requests', }), s3NumberOfObjects: i18n.translate('xpack.infra.waffle.metricOptions.s3NumberOfObjects', { - defaultMessage: 'Number of Objects', + defaultMessage: 'number of objects', }), s3DownloadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3DownloadBytes', { - defaultMessage: 'Downloads (Bytes)', + defaultMessage: 'downloads (bytes)', }), s3UploadBytes: i18n.translate('xpack.infra.waffle.metricOptions.s3UploadBytes', { - defaultMessage: 'Uploads (Bytes)', + defaultMessage: 'uploads (bytes)', }), rdsConnections: i18n.translate('xpack.infra.waffle.metricOptions.rdsConnections', { - defaultMessage: 'Connections', + defaultMessage: 'connections', }), rdsQueriesExecuted: i18n.translate('xpack.infra.waffle.metricOptions.rdsQueriesExecuted', { - defaultMessage: 'Queries Executed', + defaultMessage: 'queries executed', }), rdsActiveTransactions: i18n.translate('xpack.infra.waffle.metricOptions.rdsActiveTransactions', { - defaultMessage: 'Active Transactions', + defaultMessage: 'active transactions', }), rdsLatency: i18n.translate('xpack.infra.waffle.metricOptions.rdsLatency', { - defaultMessage: 'Latency', + defaultMessage: 'latency', }), sqsMessagesVisible: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesVisible', { - defaultMessage: 'Messages Available', + defaultMessage: 'messages available', }), sqsMessagesDelayed: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesDelayed', { - defaultMessage: 'Messages Delayed', + defaultMessage: 'messages delayed', }), sqsMessagesSent: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesSent', { - defaultMessage: 'Messages Added', + defaultMessage: 'messages added', }), sqsMessagesEmpty: i18n.translate('xpack.infra.waffle.metricOptions.sqsMessagesEmpty', { - defaultMessage: 'Messages Returned Empty', + defaultMessage: 'messages returned empty', }), sqsOldestMessage: i18n.translate('xpack.infra.waffle.metricOptions.sqsOldestMessage', { - defaultMessage: 'Oldest Message', + defaultMessage: 'oldest message', }), }; +const Translations = mapValues( + TranslationsLowercase, + (translation) => `${translation[0].toUpperCase()}${translation.slice(1)}` +); + export const toMetricOpt = ( metric: SnapshotMetricType -): { text: string; value: SnapshotMetricType } | undefined => { +): { text: string; textLC: string; value: SnapshotMetricType } | undefined => { switch (metric) { case 'cpu': return { text: Translations.CPUUsage, + textLC: TranslationsLowercase.CPUUsage, value: 'cpu', }; case 'memory': return { text: Translations.MemoryUsage, + textLC: TranslationsLowercase.MemoryUsage, value: 'memory', }; case 'rx': return { text: Translations.InboundTraffic, + textLC: TranslationsLowercase.InboundTraffic, value: 'rx', }; case 'tx': return { text: Translations.OutboundTraffic, + textLC: TranslationsLowercase.OutboundTraffic, value: 'tx', }; case 'logRate': return { text: Translations.LogRate, + textLC: TranslationsLowercase.LogRate, value: 'logRate', }; case 'load': return { text: Translations.Load, + textLC: TranslationsLowercase.Load, value: 'load', }; case 'count': return { text: Translations.Count, + textLC: TranslationsLowercase.Count, value: 'count', }; case 'diskIOReadBytes': return { text: Translations.DiskIOReadBytes, + textLC: TranslationsLowercase.DiskIOReadBytes, value: 'diskIOReadBytes', }; case 'diskIOWriteBytes': return { text: Translations.DiskIOWriteBytes, + textLC: TranslationsLowercase.DiskIOWriteBytes, value: 'diskIOWriteBytes', }; case 's3BucketSize': return { text: Translations.s3BucketSize, + textLC: TranslationsLowercase.s3BucketSize, value: 's3BucketSize', }; case 's3TotalRequests': return { text: Translations.s3TotalRequests, + textLC: TranslationsLowercase.s3TotalRequests, value: 's3TotalRequests', }; case 's3NumberOfObjects': return { text: Translations.s3NumberOfObjects, + textLC: TranslationsLowercase.s3NumberOfObjects, value: 's3NumberOfObjects', }; case 's3DownloadBytes': return { text: Translations.s3DownloadBytes, + textLC: TranslationsLowercase.s3DownloadBytes, value: 's3DownloadBytes', }; case 's3UploadBytes': return { text: Translations.s3UploadBytes, + textLC: TranslationsLowercase.s3UploadBytes, value: 's3UploadBytes', }; case 'rdsConnections': return { text: Translations.rdsConnections, + textLC: TranslationsLowercase.rdsConnections, value: 'rdsConnections', }; case 'rdsQueriesExecuted': return { text: Translations.rdsQueriesExecuted, + textLC: TranslationsLowercase.rdsQueriesExecuted, value: 'rdsQueriesExecuted', }; case 'rdsActiveTransactions': return { text: Translations.rdsActiveTransactions, + textLC: TranslationsLowercase.rdsActiveTransactions, value: 'rdsActiveTransactions', }; case 'rdsLatency': return { text: Translations.rdsLatency, + textLC: TranslationsLowercase.rdsLatency, value: 'rdsLatency', }; case 'sqsMessagesVisible': return { text: Translations.sqsMessagesVisible, + textLC: TranslationsLowercase.sqsMessagesVisible, value: 'sqsMessagesVisible', }; case 'sqsMessagesDelayed': return { text: Translations.sqsMessagesDelayed, + textLC: TranslationsLowercase.sqsMessagesDelayed, value: 'sqsMessagesDelayed', }; case 'sqsMessagesSent': return { text: Translations.sqsMessagesSent, + textLC: TranslationsLowercase.sqsMessagesSent, value: 'sqsMessagesSent', }; case 'sqsMessagesEmpty': return { text: Translations.sqsMessagesEmpty, + textLC: TranslationsLowercase.sqsMessagesEmpty, value: 'sqsMessagesEmpty', }; case 'sqsOldestMessage': return { text: Translations.sqsOldestMessage, + textLC: TranslationsLowercase.sqsOldestMessage, value: 'sqsOldestMessage', }; } diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx deleted file mode 100644 index 627ea2bbef429..0000000000000 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criteria.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { EuiFlexItem, EuiFlexGroup, EuiAccordion } from '@elastic/eui'; -import { IFieldType } from 'src/plugins/data/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; -import { Criterion } from './criterion'; -import { - LogDocumentCountAlertParams, - Criterion as CriterionType, -} from '../../../../../common/alerting/logs/types'; -import { AlertsContext } from './editor'; -import { CriterionPreview } from './criterion_preview_chart'; - -interface Props { - fields: IFieldType[]; - criteria?: LogDocumentCountAlertParams['criteria']; - updateCriterion: (idx: number, params: Partial) => void; - removeCriterion: (idx: number) => void; - errors: IErrorObject; - alertParams: Partial; - context: AlertsContext; - sourceId: string; -} - -export const Criteria: React.FC = ({ - fields, - criteria, - updateCriterion, - removeCriterion, - errors, - alertParams, - context, - sourceId, -}) => { - if (!criteria) return null; - return ( - - - {criteria.map((criterion, idx) => { - return ( - 1} - errors={errors[idx.toString()] as IErrorObject} - /> - } - key={idx} - arrowDisplay="right" - > - - - ); - })} - - - ); -}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/document_count.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/document_count.tsx deleted file mode 100644 index ff6a8e7e55fd6..0000000000000 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/document_count.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { - EuiPopoverTitle, - EuiFlexItem, - EuiFlexGroup, - EuiPopover, - EuiSelect, - EuiFieldNumber, - EuiExpression, - EuiFormRow, -} from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; -import { - Comparator, - ComparatorToi18nMap, - LogDocumentCountAlertParams, -} from '../../../../../common/alerting/logs/types'; - -const documentCountPrefix = i18n.translate('xpack.infra.logs.alertFlyout.documentCountPrefix', { - defaultMessage: 'when', -}); - -const getComparatorOptions = (): Array<{ - value: Comparator; - text: string; -}> => { - return [ - { value: Comparator.LT, text: ComparatorToi18nMap[Comparator.LT] }, - { value: Comparator.LT_OR_EQ, text: ComparatorToi18nMap[Comparator.LT_OR_EQ] }, - { value: Comparator.GT, text: ComparatorToi18nMap[Comparator.GT] }, - { value: Comparator.GT_OR_EQ, text: ComparatorToi18nMap[Comparator.GT_OR_EQ] }, - ]; -}; - -interface Props { - comparator?: Comparator; - value?: number; - updateCount: (params: Partial) => void; - errors: IErrorObject; -} - -export const DocumentCount: React.FC = ({ comparator, value, updateCount, errors }) => { - const [isComparatorPopoverOpen, setComparatorPopoverOpenState] = useState(false); - const [isValuePopoverOpen, setIsValuePopoverOpen] = useState(false); - - const documentCountValue = i18n.translate('xpack.infra.logs.alertFlyout.documentCountValue', { - defaultMessage: '{value, plural, one {log entry} other {log entries}}', - values: { value }, - }); - - const documentCountSuffix = i18n.translate('xpack.infra.logs.alertFlyout.documentCountSuffix', { - defaultMessage: '{value, plural, one {occurs} other {occur}}', - values: { value }, - }); - - return ( - - - setComparatorPopoverOpenState(true)} - /> - } - isOpen={isComparatorPopoverOpen} - closePopover={() => setComparatorPopoverOpenState(false)} - ownFocus - panelPaddingSize="s" - anchorPosition="downLeft" - > -
- {documentCountPrefix} - updateCount({ comparator: e.target.value as Comparator })} - options={getComparatorOptions()} - /> -
-
-
- - - setIsValuePopoverOpen(true)} - color={errors.value.length === 0 ? 'secondary' : 'danger'} - /> - } - isOpen={isValuePopoverOpen} - closePopover={() => setIsValuePopoverOpen(false)} - ownFocus - panelPaddingSize="s" - anchorPosition="downLeft" - > -
- {documentCountValue} - 0} error={errors.value}> - { - const number = parseInt(e.target.value, 10); - updateCount({ value: number ? number : undefined }); - }} - /> - -
-
-
- - - - -
- ); -}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx b/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx deleted file mode 100644 index e063b880ab843..0000000000000 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/editor.tsx +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useCallback, useMemo, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiLoadingSpinner, EuiSpacer, EuiButton, EuiCallOut } from '@elastic/eui'; -import { useMount } from 'react-use'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - ForLastExpression, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../triggers_actions_ui/public/common'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../../triggers_actions_ui/public/application/context/alerts_context'; -import { LogDocumentCountAlertParams, Comparator } from '../../../../../common/alerting/logs/types'; -import { DocumentCount } from './document_count'; -import { Criteria } from './criteria'; -import { useSourceId } from '../../../../containers/source_id'; -import { LogSourceProvider, useLogSourceContext } from '../../../../containers/logs/log_source'; -import { GroupByExpression } from '../../shared/group_by_expression/group_by_expression'; - -export interface ExpressionCriteria { - field?: string; - comparator?: Comparator; - value?: string | number; -} - -interface LogsContextMeta { - isInternal?: boolean; -} - -export type AlertsContext = AlertsContextValue; -interface Props { - errors: IErrorObject; - alertParams: Partial; - setAlertParams(key: string, value: any): void; - setAlertProperty(key: string, value: any): void; - alertsContext: AlertsContext; - sourceId: string; -} - -const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; - -const DEFAULT_EXPRESSION = { - count: { - value: 75, - comparator: Comparator.GT, - }, - criteria: [DEFAULT_CRITERIA], - timeSize: 5, - timeUnit: 'm', -}; - -export const ExpressionEditor: React.FC = (props) => { - const isInternal = props.alertsContext.metadata?.isInternal; - const [sourceId] = useSourceId(); - - return ( - <> - {isInternal ? ( - - - - ) : ( - - - - - - )} - - ); -}; - -export const SourceStatusWrapper: React.FC = (props) => { - const { - initialize, - isLoadingSourceStatus, - isUninitialized, - hasFailedLoadingSourceStatus, - loadSourceStatus, - } = useLogSourceContext(); - const { children } = props; - - useMount(() => { - initialize(); - }); - - return ( - <> - {isLoadingSourceStatus || isUninitialized ? ( -
- - - -
- ) : hasFailedLoadingSourceStatus ? ( - - - {i18n.translate('xpack.infra.logs.alertFlyout.sourceStatusErrorTryAgain', { - defaultMessage: 'Try again', - })} - - - ) : ( - children - )} - - ); -}; - -export const Editor: React.FC = (props) => { - const { setAlertParams, alertParams, errors, alertsContext, sourceId } = props; - const [hasSetDefaults, setHasSetDefaults] = useState(false); - const { sourceStatus } = useLogSourceContext(); - useMount(() => { - for (const [key, value] of Object.entries({ ...DEFAULT_EXPRESSION, ...alertParams })) { - setAlertParams(key, value); - setHasSetDefaults(true); - } - }); - - const supportedFields = useMemo(() => { - if (sourceStatus?.logIndexFields) { - return sourceStatus.logIndexFields.filter((field) => { - return (field.type === 'string' || field.type === 'number') && field.searchable; - }); - } else { - return []; - } - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [sourceStatus]); - - const groupByFields = useMemo(() => { - if (sourceStatus?.logIndexFields) { - return sourceStatus.logIndexFields.filter((field) => { - return field.type === 'string' && field.aggregatable; - }); - } else { - return []; - } - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [sourceStatus]); - - const updateCount = useCallback( - (countParams) => { - const nextCountParams = { ...alertParams.count, ...countParams }; - setAlertParams('count', nextCountParams); - }, - [alertParams.count, setAlertParams] - ); - - const updateCriterion = useCallback( - (idx, criterionParams) => { - const nextCriteria = alertParams.criteria?.map((criterion, index) => { - return idx === index ? { ...criterion, ...criterionParams } : criterion; - }); - setAlertParams('criteria', nextCriteria ? nextCriteria : []); - }, - [alertParams, setAlertParams] - ); - - const updateTimeSize = useCallback( - (ts: number | undefined) => { - setAlertParams('timeSize', ts); - }, - [setAlertParams] - ); - - const updateTimeUnit = useCallback( - (tu: string) => { - setAlertParams('timeUnit', tu); - }, - [setAlertParams] - ); - - const updateGroupBy = useCallback( - (groups: string[]) => { - setAlertParams('groupBy', groups); - }, - [setAlertParams] - ); - - const addCriterion = useCallback(() => { - const nextCriteria = alertParams?.criteria - ? [...alertParams.criteria, DEFAULT_CRITERIA] - : [DEFAULT_CRITERIA]; - setAlertParams('criteria', nextCriteria); - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [alertParams, setAlertParams]); - - const removeCriterion = useCallback( - (idx) => { - const nextCriteria = alertParams?.criteria?.filter((criterion, index) => { - return index !== idx; - }); - setAlertParams('criteria', nextCriteria); - }, - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - [alertParams, setAlertParams] - ); - - // Wait until the alert param defaults have been set - if (!hasSetDefaults) return null; - - return ( - <> - - - - - - - - -
- - - -
- - ); -}; - -// required for dynamic import -// eslint-disable-next-line import/no-default-export -export default ExpressionEditor; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx index b8eb73b99f45e..74634bbd5d290 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx @@ -8,7 +8,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { EuiPopover, EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AlertFlyout } from './alert_flyout'; -import { useLinkProps } from '../../../hooks/use_link_props'; +import { useLinkProps } from '../../../../hooks/use_link_props'; export const AlertDropdown = () => { const [popoverOpen, setPopoverOpen] = useState(false); diff --git a/x-pack/plugins/infra/public/components/alerting/logs/alert_flyout.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx similarity index 84% rename from x-pack/plugins/infra/public/components/alerting/logs/alert_flyout.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx index 45e4f8576892c..c6e16dcc9aaef 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx @@ -6,11 +6,10 @@ import React, { useContext } from 'react'; import { ApplicationStart, DocLinksStart, HttpStart, NotificationsStart } from 'src/core/public'; - -import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; -import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../common/alerting/logs/types'; +import { AlertsContextProvider, AlertAdd } from '../../../../../../triggers_actions_ui/public'; +import { TriggerActionsContext } from '../../../../utils/triggers_actions_context'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../../common/alerting/logs/log_threshold/types'; interface Props { visible?: boolean; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx new file mode 100644 index 0000000000000..a607b5ebf9975 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx @@ -0,0 +1,284 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { EuiFlexItem, EuiFlexGroup, EuiButtonEmpty, EuiAccordion, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { IFieldType } from 'src/plugins/data/public'; +import { Criterion } from './criterion'; +import { + AlertParams, + Comparator, + Criteria as CriteriaType, + Criterion as CriterionType, + CountCriteria as CountCriteriaType, + RatioCriteria as RatioCriteriaType, + isRatioAlert, + getNumerator, + getDenominator, +} from '../../../../../../common/alerting/logs/log_threshold/types'; +import { AlertsContext, ExpressionLike } from './editor'; +import { CriterionPreview } from './criterion_preview_chart'; +import { Errors, CriterionErrors } from '../validation'; + +const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; + +const QueryAText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCriteriaQueryAText', { + defaultMessage: 'Query A', +}); + +const QueryBText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCriteriaQueryBText', { + defaultMessage: 'Query B', +}); + +interface SharedProps { + fields: IFieldType[]; + criteria?: AlertParams['criteria']; + errors: Errors['criteria']; + alertParams: Partial; + context: AlertsContext; + sourceId: string; + updateCriteria: (criteria: AlertParams['criteria']) => void; +} + +type CriteriaProps = SharedProps; + +export const Criteria: React.FC = (props) => { + const { criteria, errors } = props; + if (!criteria || criteria.length === 0) return null; + + return !isRatioAlert(criteria) ? ( + + ) : ( + + ); +}; + +interface CriteriaWrapperProps { + alertParams: SharedProps['alertParams']; + fields: SharedProps['fields']; + updateCriterion: (idx: number, params: Partial) => void; + removeCriterion: (idx: number) => void; + addCriterion: () => void; + criteria: CriteriaType; + errors: CriterionErrors; + context: SharedProps['context']; + sourceId: SharedProps['sourceId']; + isRatio?: boolean; +} + +const CriteriaWrapper: React.FC = (props) => { + const { + updateCriterion, + removeCriterion, + addCriterion, + criteria, + fields, + errors, + alertParams, + context, + sourceId, + isRatio = false, + } = props; + + return ( + + + {criteria.map((criterion, idx) => { + return ( + 1} + errors={errors[idx]} + /> + } + key={idx} + arrowDisplay="right" + > + + + ); + })} + + + + ); +}; + +interface RatioCriteriaProps { + alertParams: SharedProps['alertParams']; + fields: SharedProps['fields']; + criteria: RatioCriteriaType; + errors: Errors['criteria']; + context: SharedProps['context']; + sourceId: SharedProps['sourceId']; + updateCriteria: (criteria: AlertParams['criteria']) => void; +} + +const RatioCriteria: React.FC = (props) => { + const { criteria, errors, updateCriteria } = props; + + const handleUpdateNumeratorCriteria = useCallback( + (criteriaParam: CriteriaType) => { + const nextCriteria: RatioCriteriaType = [criteriaParam, getDenominator(criteria)]; + updateCriteria(nextCriteria); + }, + [updateCriteria, criteria] + ); + + const handleUpdateDenominatorCriteria = useCallback( + (criteriaParam: CriteriaType) => { + const nextCriteria: RatioCriteriaType = [getNumerator(criteria), criteriaParam]; + updateCriteria(nextCriteria); + }, + [updateCriteria, criteria] + ); + + const { + updateCriterion: updateNumeratorCriterion, + addCriterion: addNumeratorCriterion, + removeCriterion: removeNumeratorCriterion, + } = useCriteriaState(getNumerator(criteria), handleUpdateNumeratorCriteria); + + const { + updateCriterion: updateDenominatorCriterion, + addCriterion: addDenominatorCriterion, + removeCriterion: removeDenominatorCriterion, + } = useCriteriaState(getDenominator(criteria), handleUpdateDenominatorCriteria); + + return ( + <> + + + + + + + + + + + + + ); +}; + +interface CountCriteriaProps { + alertParams: SharedProps['alertParams']; + fields: SharedProps['fields']; + criteria: CountCriteriaType; + errors: Errors['criteria']; + context: SharedProps['context']; + sourceId: SharedProps['sourceId']; + updateCriteria: (criteria: AlertParams['criteria']) => void; +} + +const CountCriteria: React.FC = (props) => { + const { criteria, updateCriteria, errors } = props; + + const handleUpdateCriteria = useCallback( + (criteriaParam: CriteriaType) => { + updateCriteria(criteriaParam); + }, + [updateCriteria] + ); + + const { updateCriterion, addCriterion, removeCriterion } = useCriteriaState( + criteria, + handleUpdateCriteria + ); + + return ( + + ); +}; + +const useCriteriaState = ( + criteria: CriteriaType, + onUpdateCriteria: (criteria: CriteriaType) => void +) => { + const updateCriterion = useCallback( + (idx, criterionParams) => { + const nextCriteria = criteria.map((criterion, index) => { + return idx === index ? { ...criterion, ...criterionParams } : criterion; + }); + onUpdateCriteria(nextCriteria); + }, + [criteria, onUpdateCriteria] + ); + + const addCriterion = useCallback(() => { + const nextCriteria = criteria ? [...criteria, DEFAULT_CRITERIA] : [DEFAULT_CRITERIA]; + onUpdateCriteria(nextCriteria); + }, [criteria, onUpdateCriteria]); + + const removeCriterion = useCallback( + (idx) => { + const nextCriteria = criteria.filter((criterion, index) => { + return index !== idx; + }); + onUpdateCriteria(nextCriteria); + }, + [criteria, onUpdateCriteria] + ); + + return { updateCriterion, addCriterion, removeCriterion }; +}; + +const AddCriterionButton = ({ addCriterion }: { addCriterion: () => void }) => { + return ( +
+ + + +
+ ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx index 9ee9373bd2c14..8ecd172c08d24 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx @@ -18,14 +18,15 @@ import { EuiFormRow, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { isNumber, isFinite } from 'lodash'; import { IFieldType } from 'src/plugins/data/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; +import { IErrorObject } from '../../../../../../../triggers_actions_ui/public/types'; import { Comparator, Criterion as CriterionType, ComparatorToi18nMap, -} from '../../../../../common/alerting/logs/types'; +} from '../../../../../../common/alerting/logs/log_threshold/types'; const firstCriterionFieldPrefix = i18n.translate( 'xpack.infra.logs.alertFlyout.firstCriterionFieldPrefix', @@ -239,8 +240,10 @@ export const Criterion: React.FC = ({ compressed value={criterion.value as number | undefined} onChange={(e) => { - const number = parseInt(e.target.value, 10); - updateCriterion(idx, { value: number ? number : undefined }); + const number = parseFloat(e.target.value); + updateCriterion(idx, { + value: isNumber(number) && isFinite(number) ? number : undefined, + }); }} /> ) : ( diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx similarity index 87% rename from x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion_preview_chart.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx index 31f9a64015c07..675900499e793 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx @@ -31,28 +31,30 @@ import { getChartTheme, yAxisFormatter, NUM_BUCKETS, -} from '../../shared/criterion_preview_chart/criterion_preview_chart'; +} from '../../../shared/criterion_preview_chart/criterion_preview_chart'; import { - LogDocumentCountAlertParams, + AlertParams, + Threshold, Criterion, Comparator, -} from '../../../../../common/alerting/logs/types'; -import { Color, colorTransformer } from '../../../../../common/color_palette'; +} from '../../../../../../common/alerting/logs/log_threshold/types'; +import { Color, colorTransformer } from '../../../../../../common/color_palette'; import { GetLogAlertsChartPreviewDataAlertParamsSubset, getLogAlertsChartPreviewDataAlertParamsSubsetRT, -} from '../../../../../common/http_api/log_alerts/'; +} from '../../../../../../common/http_api/log_alerts/'; import { AlertsContext } from './editor'; import { useChartPreviewData } from './hooks/use_chart_preview_data'; -import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../../common/runtime_types'; const GROUP_LIMIT = 5; interface Props { - alertParams: Partial; + alertParams: Partial; context: AlertsContext; chartCriterion: Partial; sourceId: string; + showThreshold: boolean; } export const CriterionPreview: React.FC = ({ @@ -60,6 +62,7 @@ export const CriterionPreview: React.FC = ({ context, chartCriterion, sourceId, + showThreshold, }) => { const chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset | null = useMemo(() => { const { field, comparator, value } = chartCriterion; @@ -92,6 +95,7 @@ export const CriterionPreview: React.FC = ({ sourceId={sourceId} threshold={alertParams.count} chartAlertParams={chartAlertParams} + showThreshold={showThreshold} /> ); }; @@ -100,8 +104,9 @@ interface ChartProps { buckets: number; context: AlertsContext; sourceId: string; - threshold?: LogDocumentCountAlertParams['count']; + threshold?: Threshold; chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; + showThreshold: boolean; } const CriterionPreviewChart: React.FC = ({ @@ -110,6 +115,7 @@ const CriterionPreviewChart: React.FC = ({ sourceId, threshold, chartAlertParams, + showThreshold, }) => { const isDarkMode = context.uiSettings?.get('theme:darkMode') || false; @@ -140,17 +146,18 @@ const CriterionPreviewChart: React.FC = ({ const isGrouped = groupBy && groupBy.length > 0 ? true : false; const isAbove = - threshold && threshold.comparator + showThreshold && threshold && threshold.comparator ? [Comparator.GT, Comparator.GT_OR_EQ].includes(threshold.comparator) : false; const isBelow = - threshold && threshold.comparator + showThreshold && threshold && threshold.comparator ? [Comparator.LT, Comparator.LT_OR_EQ].includes(threshold.comparator) : false; // For grouped scenarios we want to limit the groups displayed, for "isAbove" thresholds we'll show // groups with the highest doc counts. And for "isBelow" thresholds we'll show groups with the lowest doc counts. + // Ratio scenarios will just default to max. const filteredSeries = useMemo(() => { if (!isGrouped) { return series; @@ -183,11 +190,14 @@ const CriterionPreviewChart: React.FC = ({ const hasData = series.length > 0; const { yMin, yMax, xMin, xMax } = getDomain(filteredSeries, isStacked); const chartDomain = { - max: threshold && threshold.value ? Math.max(yMax, threshold.value) * 1.1 : yMax * 1.1, // Add 10% headroom. - min: threshold && threshold.value ? Math.min(yMin, threshold.value) : yMin, + max: + showThreshold && threshold && threshold.value + ? Math.max(yMax, threshold.value) * 1.1 + : yMax * 1.1, // Add 10% headroom. + min: showThreshold && threshold && threshold.value ? Math.min(yMin, threshold.value) : yMin, }; - if (threshold && threshold.value && chartDomain.min === threshold.value) { + if (showThreshold && threshold && threshold.value && chartDomain.min === threshold.value) { chartDomain.min = chartDomain.min * 0.9; // Allow some padding so the threshold annotation has better visibility } @@ -229,7 +239,7 @@ const CriterionPreviewChart: React.FC = ({ }} color={!isGrouped ? colorTransformer(Color.color0) : undefined} /> - {threshold && threshold.value ? ( + {showThreshold && threshold && threshold.value ? ( = ({ }} /> ) : null} - {threshold && threshold.value && isBelow ? ( + {showThreshold && threshold && threshold.value && isBelow ? ( = ({ ]} /> ) : null} - {threshold && threshold.value && isAbove ? ( + {showThreshold && threshold && threshold.value && isAbove ? ( ; +interface Props { + errors: Errors; + alertParams: Partial; + setAlertParams(key: string, value: any): void; + setAlertProperty(key: string, value: any): void; + alertsContext: AlertsContext; + sourceId: string; +} + +const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; + +const DEFAULT_BASE_EXPRESSION = { + timeSize: 5, + timeUnit: 'm', +}; + +const DEFAULT_COUNT_EXPRESSION = { + ...DEFAULT_BASE_EXPRESSION, + count: { + value: 75, + comparator: Comparator.GT, + }, + criteria: [DEFAULT_CRITERIA], +}; + +const DEFAULT_RATIO_EXPRESSION = { + ...DEFAULT_BASE_EXPRESSION, + count: { + value: 2, + comparator: Comparator.GT, + }, + criteria: [ + [DEFAULT_CRITERIA], + [{ field: 'log.level', comparator: Comparator.EQ, value: 'warning' }], + ], +}; + +export const ExpressionEditor: React.FC = (props) => { + const isInternal = props.alertsContext.metadata?.isInternal; + const [sourceId] = useSourceId(); + + return ( + <> + {isInternal ? ( + + + + ) : ( + + + + + + )} + + ); +}; + +export const SourceStatusWrapper: React.FC = (props) => { + const { + initialize, + isLoadingSourceStatus, + isUninitialized, + hasFailedLoadingSourceStatus, + loadSourceStatus, + } = useLogSourceContext(); + const { children } = props; + + useMount(() => { + initialize(); + }); + + return ( + <> + {isLoadingSourceStatus || isUninitialized ? ( +
+ + + +
+ ) : hasFailedLoadingSourceStatus ? ( + + + {i18n.translate('xpack.infra.logs.alertFlyout.sourceStatusErrorTryAgain', { + defaultMessage: 'Try again', + })} + + + ) : ( + children + )} + + ); +}; + +export const Editor: React.FC = (props) => { + const { setAlertParams, alertParams, errors, alertsContext, sourceId } = props; + const [hasSetDefaults, setHasSetDefaults] = useState(false); + const { sourceStatus } = useLogSourceContext(); + useMount(() => { + for (const [key, value] of Object.entries({ ...DEFAULT_COUNT_EXPRESSION, ...alertParams })) { + setAlertParams(key, value); + } + setHasSetDefaults(true); + }); + + const supportedFields = useMemo(() => { + if (sourceStatus?.logIndexFields) { + return sourceStatus.logIndexFields.filter((field) => { + return (field.type === 'string' || field.type === 'number') && field.searchable; + }); + } else { + return []; + } + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + }, [sourceStatus]); + + const groupByFields = useMemo(() => { + if (sourceStatus?.logIndexFields) { + return sourceStatus.logIndexFields.filter((field) => { + return field.type === 'string' && field.aggregatable; + }); + } else { + return []; + } + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + }, [sourceStatus]); + + const updateThreshold = useCallback( + (thresholdParams) => { + const nextThresholdParams = { ...alertParams.count, ...thresholdParams }; + setAlertParams('count', nextThresholdParams); + }, + [alertParams.count, setAlertParams] + ); + + const updateCriteria = useCallback( + (criteria: AlertParams['criteria']) => { + setAlertParams('criteria', criteria); + }, + [setAlertParams] + ); + + const updateTimeSize = useCallback( + (ts: number | undefined) => { + setAlertParams('timeSize', ts); + }, + [setAlertParams] + ); + + const updateTimeUnit = useCallback( + (tu: string) => { + setAlertParams('timeUnit', tu); + }, + [setAlertParams] + ); + + const updateGroupBy = useCallback( + (groups: string[]) => { + setAlertParams('groupBy', groups); + }, + [setAlertParams] + ); + + const updateType = useCallback( + (type: ThresholdType) => { + const defaults = type === 'count' ? DEFAULT_COUNT_EXPRESSION : DEFAULT_RATIO_EXPRESSION; + // Reset properties that don't make sense switching from one context to the other + for (const [key, value] of Object.entries({ + criteria: defaults.criteria, + count: defaults.count, + })) { + setAlertParams(key, value); + } + }, + [setAlertParams] + ); + + // Wait until the alert param defaults have been set + if (!hasSetDefaults) return null; + + const criteriaComponent = alertParams.criteria ? ( + + ) : null; + + return ( + <> + + + {alertParams.criteria && !isRatioAlert(alertParams.criteria) && criteriaComponent} + + + + + + + + {alertParams.criteria && isRatioAlert(alertParams.criteria) && criteriaComponent} + + + + ); +}; + +// required for dynamic import +// eslint-disable-next-line import/no-default-export +export default ExpressionEditor; + +// NOTE: Temporary until EUI allow empty values in EuiExpression +// components. +export const ExpressionLike = ({ text }: { text: string }) => { + return ( +
+ {text} +
+ ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/alerting/logs/expression_editor/hooks/use_chart_preview_data.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx index d5ba730026b12..d43e291f900f2 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx @@ -5,15 +5,15 @@ */ import { useState, useMemo } from 'react'; import { AlertsContext } from '../editor'; -import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; +import { useTrackedPromise } from '../../../../../../utils/use_tracked_promise'; import { GetLogAlertsChartPreviewDataSuccessResponsePayload, getLogAlertsChartPreviewDataSuccessResponsePayloadRT, getLogAlertsChartPreviewDataRequestPayloadRT, LOG_ALERTS_CHART_PREVIEW_DATA_PATH, -} from '../../../../../../common/http_api'; -import { decodeOrThrow } from '../../../../../../common/runtime_types'; -import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../common/http_api/log_alerts/'; +} from '../../../../../../../common/http_api'; +import { decodeOrThrow } from '../../../../../../../common/runtime_types'; +import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../../common/http_api/log_alerts/'; interface Options { sourceId: string; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/expression_editor/index.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/index.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/logs/expression_editor/index.tsx rename to x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/index.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx new file mode 100644 index 0000000000000..e2065ca25cb6f --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { isNumber, isFinite } from 'lodash'; +import { + EuiPopoverTitle, + EuiFlexItem, + EuiFlexGroup, + EuiPopover, + EuiSelect, + EuiFieldNumber, + EuiExpression, + EuiFormRow, +} from '@elastic/eui'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IErrorObject } from '../../../../../../../triggers_actions_ui/public/types'; +import { + Comparator, + ComparatorToi18nMap, + AlertParams, +} from '../../../../../../common/alerting/logs/log_threshold/types'; + +const thresholdPrefix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdPrefix', { + defaultMessage: 'is', +}); + +const popoverTitle = i18n.translate('xpack.infra.logs.alertFlyout.thresholdPopoverTitle', { + defaultMessage: 'Threshold', +}); + +const getComparatorOptions = (): Array<{ + value: Comparator; + text: string; +}> => { + return [ + { value: Comparator.LT, text: ComparatorToi18nMap[Comparator.LT] }, + { value: Comparator.LT_OR_EQ, text: ComparatorToi18nMap[Comparator.LT_OR_EQ] }, + { value: Comparator.GT, text: ComparatorToi18nMap[Comparator.GT] }, + { value: Comparator.GT_OR_EQ, text: ComparatorToi18nMap[Comparator.GT_OR_EQ] }, + ]; +}; + +interface Props { + comparator?: Comparator; + value?: number; + updateThreshold: (params: Partial) => void; + errors: IErrorObject; +} + +export const Threshold: React.FC = ({ comparator, value, updateThreshold, errors }) => { + const [isThresholdPopoverOpen, setThresholdPopoverOpenState] = useState(false); + + return ( + + + setThresholdPopoverOpenState(true)} + /> + } + isOpen={isThresholdPopoverOpen} + closePopover={() => setThresholdPopoverOpenState(false)} + ownFocus + panelPaddingSize="s" + anchorPosition="downLeft" + > + <> + {popoverTitle} + + + + updateThreshold({ comparator: e.target.value as Comparator })} + options={getComparatorOptions()} + /> + + + + 0} error={errors.value}> + { + const number = parseFloat(e.target.value); + updateThreshold({ + value: isNumber(number) && isFinite(number) ? number : undefined, + }); + }} + /> + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx new file mode 100644 index 0000000000000..03c895dcd0f2d --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { i18n } from '@kbn/i18n'; +import { EuiFlexItem, EuiFlexGroup, EuiPopover, EuiSelect, EuiExpression } from '@elastic/eui'; +import { + AlertParams, + ThresholdType, + isRatioAlert, +} from '../../../../../../common/alerting/logs/log_threshold/types'; +import { ExpressionLike } from './editor'; + +const typePrefix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypePrefix', { + defaultMessage: 'when the', +}); + +const countSuffix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypeCountSuffix', { + defaultMessage: 'of log entries', +}); + +const ratioSuffix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypeRatioSuffix', { + defaultMessage: 'of Query A to Query B', +}); + +const countI18n = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypeCount', { + defaultMessage: 'count', +}); + +const ratioI18n = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypeRatio', { + defaultMessage: 'ratio', +}); + +const getOptions = (): Array<{ + value: ThresholdType; + text: string; +}> => { + return [ + { value: 'ratio', text: ratioI18n }, + { value: 'count', text: countI18n }, + ]; +}; + +interface Props { + criteria: AlertParams['criteria']; + updateType: (type: ThresholdType) => void; +} + +const getThresholdType = (criteria: AlertParams['criteria']): ThresholdType => { + return isRatioAlert(criteria) ? 'ratio' : 'count'; +}; + +export const TypeSwitcher: React.FC = ({ criteria, updateType }) => { + const [isThresholdTypePopoverOpen, setThresholdTypePopoverOpenState] = useState(false); + const thresholdType = getThresholdType(criteria); + + return ( + + + + setThresholdTypePopoverOpenState(true)} + /> + + + } + isOpen={isThresholdTypePopoverOpen} + closePopover={() => setThresholdTypePopoverOpenState(false)} + ownFocus + panelPaddingSize="s" + anchorPosition="downLeft" + > + + + updateType(thresholdType === 'ratio' ? 'count' : 'ratio')} + options={getOptions()} + /> + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts new file mode 100644 index 0000000000000..15ff5844c1236 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts @@ -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 { i18n } from '@kbn/i18n'; +import React from 'react'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertTypeModel } from '../../../../../../triggers_actions_ui/public/types'; +import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../../common/alerting/logs/log_threshold/types'; +import { validateExpression } from './validation'; + +export function getAlertType(): AlertTypeModel { + return { + id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, + name: i18n.translate('xpack.infra.logs.alertFlyout.alertName', { + defaultMessage: 'Log threshold', + }), + iconClass: 'bell', + alertParamsExpression: React.lazy(() => import('./expression_editor/editor')), + validate: validateExpression, + defaultActionMessage: i18n.translate( + 'xpack.infra.logs.alerting.threshold.defaultActionMessage', + { + defaultMessage: `\\{\\{^context.isRatio\\}\\}\\{\\{#context.group\\}\\}\\{\\{context.group\\}\\} - \\{\\{/context.group\\}\\}\\{\\{context.matchingDocuments\\}\\} log entries have matched the following conditions: \\{\\{context.conditions\\}\\}\\{\\{/context.isRatio\\}\\}\\{\\{#context.isRatio\\}\\}\\{\\{#context.group\\}\\}\\{\\{context.group\\}\\} - \\{\\{/context.group\\}\\} Ratio of the count of log entries matching \\{\\{context.numeratorConditions\\}\\} to the count of log entries matching \\{\\{context.denominatorConditions\\}\\} was \\{\\{context.ratio\\}\\}\\{\\{/context.isRatio\\}\\}`, + } + ), + requiresAppContext: false, + }; +} diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts new file mode 100644 index 0000000000000..a7f773c08d2b3 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { isNumber, isFinite } from 'lodash'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ValidationResult } from '../../../../../../triggers_actions_ui/public/types'; +import { + AlertParams, + Criteria, + RatioCriteria, + isRatioAlert, + getNumerator, + getDenominator, +} from '../../../../../common/alerting/logs/log_threshold/types'; + +export interface CriterionErrors { + [id: string]: { + field: string[]; + comparator: string[]; + value: string[]; + }; +} + +export interface Errors { + threshold: { + value: string[]; + }; + // NOTE: The data structure for criteria errors isn't 100% + // ideal but we need to conform to the interfaces that the alerting + // framework expects. + criteria: { + [id: string]: CriterionErrors; + }; + timeWindowSize: string[]; + timeSizeUnit: string[]; +} + +export function validateExpression({ + count, + criteria, + timeSize, + timeUnit, +}: Partial): ValidationResult { + const validationResult = { errors: {} }; + + // NOTE: In the case of components provided by the Alerting framework the error property names + // must match what they expect. + const errors: Errors = { + threshold: { + value: [], + }, + criteria: {}, + timeSizeUnit: [], + timeWindowSize: [], + }; + + validationResult.errors = errors; + + // Threshold validation + if (!isNumber(count?.value) && !isFinite(count?.value)) { + errors.threshold.value.push( + i18n.translate('xpack.infra.logs.alertFlyout.error.thresholdRequired', { + defaultMessage: 'Numeric threshold value is Required.', + }) + ); + } + + // Time validation + if (!timeSize) { + errors.timeWindowSize.push( + i18n.translate('xpack.infra.logs.alertFlyout.error.timeSizeRequired', { + defaultMessage: 'Time size is Required.', + }) + ); + } + + // Criteria validation + if (criteria && criteria.length > 0) { + const getCriterionErrors = (_criteria: Criteria): CriterionErrors => { + const _errors: CriterionErrors = {}; + + _criteria.forEach((criterion, idx) => { + _errors[idx] = { + field: [], + comparator: [], + value: [], + }; + if (!criterion.field) { + _errors[idx].field.push( + i18n.translate('xpack.infra.logs.alertFlyout.error.criterionFieldRequired', { + defaultMessage: 'Field is required.', + }) + ); + } + if (!criterion.comparator) { + _errors[idx].comparator.push( + i18n.translate('xpack.infra.logs.alertFlyout.error.criterionComparatorRequired', { + defaultMessage: 'Comparator is required.', + }) + ); + } + if (criterion.value === undefined || criterion.value === null) { + _errors[idx].value.push( + i18n.translate('xpack.infra.logs.alertFlyout.error.criterionValueRequired', { + defaultMessage: 'Value is required.', + }) + ); + } + }); + return _errors; + }; + + if (!isRatioAlert(criteria)) { + const criteriaErrors = getCriterionErrors(criteria as Criteria); + errors.criteria[0] = criteriaErrors; + } else { + const numeratorErrors = getCriterionErrors(getNumerator(criteria as RatioCriteria)); + errors.criteria[0] = numeratorErrors; + const denominatorErrors = getCriterionErrors(getDenominator(criteria as RatioCriteria)); + errors.criteria[1] = denominatorErrors; + } + } + + return validationResult; +} diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts deleted file mode 100644 index a26a7328c9103..0000000000000 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold_alert_type.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { i18n } from '@kbn/i18n'; -import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../../triggers_actions_ui/public/types'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../common/alerting/logs/types'; -import { validateExpression } from './validation'; - -export function getAlertType(): AlertTypeModel { - return { - id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, - name: i18n.translate('xpack.infra.logs.alertFlyout.alertName', { - defaultMessage: 'Log threshold', - }), - iconClass: 'bell', - alertParamsExpression: React.lazy(() => import('./expression_editor/editor')), - validate: validateExpression, - defaultActionMessage: i18n.translate( - 'xpack.infra.logs.alerting.threshold.defaultActionMessage', - { - defaultMessage: `\\{\\{#context.group\\}\\}\\{\\{context.group\\}\\} - \\{\\{/context.group\\}\\}\\{\\{context.matchingDocuments\\}\\} log entries have matched the following conditions: \\{\\{context.conditions\\}\\}`, - } - ), - requiresAppContext: false, - }; -} diff --git a/x-pack/plugins/infra/public/components/alerting/logs/validation.ts b/x-pack/plugins/infra/public/components/alerting/logs/validation.ts deleted file mode 100644 index c8c513f57a9d7..0000000000000 --- a/x-pack/plugins/infra/public/components/alerting/logs/validation.ts +++ /dev/null @@ -1,102 +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 { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; -import { LogDocumentCountAlertParams } from '../../../../common/alerting/logs/types'; - -export function validateExpression({ - count, - criteria, - timeSize, - timeUnit, -}: Partial): ValidationResult { - const validationResult = { errors: {} }; - - // NOTE: In the case of components provided by the Alerting framework the error property names - // must match what they expect. - const errors: { - count: { - value: string[]; - }; - criteria: { - [id: string]: { - field: string[]; - comparator: string[]; - value: string[]; - }; - }; - timeWindowSize: string[]; - timeSizeUnit: string[]; - } = { - count: { - value: [], - }, - criteria: {}, - timeSizeUnit: [], - timeWindowSize: [], - }; - - validationResult.errors = errors; - - // Document count validation - if (typeof count?.value !== 'number') { - errors.count.value.push( - i18n.translate('xpack.infra.logs.alertFlyout.error.documentCountRequired', { - defaultMessage: 'Document count is Required.', - }) - ); - } - - // Time validation - if (!timeSize) { - errors.timeWindowSize.push( - i18n.translate('xpack.infra.logs.alertFlyout.error.timeSizeRequired', { - defaultMessage: 'Time size is Required.', - }) - ); - } - - if (criteria && criteria.length > 0) { - // Criteria validation - criteria.forEach((criterion, idx: number) => { - const id = idx.toString(); - - errors.criteria[id] = { - field: [], - comparator: [], - value: [], - }; - - if (!criterion.field) { - errors.criteria[id].field.push( - i18n.translate('xpack.infra.logs.alertFlyout.error.criterionFieldRequired', { - defaultMessage: 'Field is required.', - }) - ); - } - - if (!criterion.comparator) { - errors.criteria[id].comparator.push( - i18n.translate('xpack.infra.logs.alertFlyout.error.criterionComparatorRequired', { - defaultMessage: 'Comparator is required.', - }) - ); - } - - if (!criterion.value) { - errors.criteria[id].value.push( - i18n.translate('xpack.infra.logs.alertFlyout.error.criterionValueRequired', { - defaultMessage: 'Value is required.', - }) - ); - } - }); - } - - return validationResult; -} diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx index 0489bd7d9929a..5b2ce862f7a81 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -31,6 +31,7 @@ export const JobConfigurationOutdatedCallout: React.FC<{ values={{ moduleName, }} + tagName="p" /> ); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx index df9de49ea0445..b9e68b25482b6 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx @@ -31,6 +31,7 @@ export const JobDefinitionOutdatedCallout: React.FC<{ values={{ moduleName, }} + tagName="p" /> ); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/notices_section.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/notices_section.tsx index 2535058322cba..3785d0e8d9423 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/notices_section.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/notices_section.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { QualityWarning } from '../../../containers/logs/log_analysis/log_analysis_module_types'; +import { QualityWarning } from '../../../../common/log_analysis'; import { LogAnalysisJobProblemIndicator } from './log_analysis_job_problem_indicator'; import { CategoryQualityWarnings } from './quality_warning_notices'; @@ -41,6 +41,10 @@ export const CategoryJobNoticesSection: React.FC<{ onRecreateMlJobForReconfiguration={onRecreateMlJobForReconfiguration} onRecreateMlJobForUpdate={onRecreateMlJobForUpdate} /> - + ); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx new file mode 100644 index 0000000000000..7caf75417091a --- /dev/null +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.stories.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { action } from '@storybook/addon-actions'; +import { storiesOf } from '@storybook/react'; +import React from 'react'; +import { EuiThemeProvider } from '../../../../../observability/public'; +import { QualityWarning } from '../../../../common/log_analysis'; +import { CategoryQualityWarnings } from './quality_warning_notices'; + +storiesOf('infra/logAnalysis/CategoryQualityWarnings', module) + .addDecorator((renderStory) => {renderStory()}) + .add('Partitioned warnings', () => { + return ( + + ); + }) + .add('Unpartitioned warnings', () => { + return ( + + ); + }); + +const partitionedQualityWarnings: QualityWarning[] = [ + { + type: 'categoryQualityWarning', + jobId: 'theMlJobId', + dataset: 'first.dataset', + reasons: [ + { type: 'singleCategory' }, + { type: 'manyRareCategories', rareCategoriesRatio: 0.95 }, + { type: 'manyCategories', categoriesDocumentRatio: 0.7 }, + ], + }, + { + type: 'categoryQualityWarning', + jobId: 'theMlJobId', + dataset: 'second.dataset', + reasons: [ + { type: 'noFrequentCategories' }, + { type: 'manyDeadCategories', deadCategoriesRatio: 0.7 }, + ], + }, +]; + +const unpartitionedQualityWarnings: QualityWarning[] = [ + { + type: 'categoryQualityWarning', + jobId: 'theMlJobId', + dataset: '', + reasons: [ + { type: 'singleCategory' }, + { type: 'manyRareCategories', rareCategoriesRatio: 0.95 }, + { type: 'manyCategories', categoriesDocumentRatio: 0.7 }, + ], + }, +]; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx index 0d93ead5a82c6..4bf618923a138 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/quality_warning_notices.tsx @@ -4,43 +4,89 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiCallOut } from '@elastic/eui'; +import { + EuiAccordion, + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, + EuiSpacer, + htmlIdGenerator, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import type { +import { groupBy } from 'lodash'; +import React, { Fragment, useState } from 'react'; +import { euiStyled } from '../../../../../observability/public'; +import { + CategoryQualityWarning, CategoryQualityWarningReason, - QualityWarning, -} from '../../../containers/logs/log_analysis/log_analysis_module_types'; + getFriendlyNameForPartitionId, +} from '../../../../common/log_analysis'; +import { RecreateJobCallout } from './recreate_job_callout'; -export const CategoryQualityWarnings: React.FC<{ qualityWarnings: QualityWarning[] }> = ({ - qualityWarnings, -}) => ( - <> - {qualityWarnings.map((qualityWarning, qualityWarningIndex) => ( - -

+export const CategoryQualityWarnings: React.FC<{ + hasSetupCapabilities: boolean; + onRecreateMlJob: () => void; + qualityWarnings: CategoryQualityWarning[]; +}> = ({ hasSetupCapabilities, onRecreateMlJob, qualityWarnings }) => { + const [detailAccordionId] = useState(htmlIdGenerator()()); + + const categoryQualityWarningsByJob = groupBy(qualityWarnings, 'jobId'); + + return ( + <> + {Object.entries(categoryQualityWarningsByJob).map(([jobId, qualityWarningsForJob]) => ( + -

-
    - {qualityWarning.reasons.map((reason, reasonIndex) => ( -
  • - -
  • - ))} -
-
- ))} - -); + + } + paddingSize="m" + > + + {qualityWarningsForJob.flatMap((qualityWarning) => ( + + + {getFriendlyNameForPartitionId(qualityWarning.dataset)} + + {qualityWarning.reasons.map((reason) => ( + + + + ))} + + ))} + + + + + ))} + + ); +}; + +const QualityWarningReasonDescription = euiStyled(EuiDescriptionListDescription)` + display: list-item; + list-style-type: disc; + margin-left: ${(props) => props.theme.eui.paddingSizes.m}; +`; const categoryQualityWarningCalloutTitle = i18n.translate( 'xpack.infra.logs.logEntryCategories.categoryQUalityWarningCalloutTitle', @@ -49,7 +95,7 @@ const categoryQualityWarningCalloutTitle = i18n.translate( } ); -const CategoryQualityWarningReasonDescription: React.FC<{ +export const CategoryQualityWarningReasonDescription: React.FC<{ reason: CategoryQualityWarningReason; }> = ({ reason }) => { switch (reason.type) { @@ -57,7 +103,7 @@ const CategoryQualityWarningReasonDescription: React.FC<{ return ( ); case 'manyRareCategories': diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx index cdf030a849fa1..2a0337bd99767 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx @@ -14,7 +14,7 @@ export const RecreateJobCallout: React.FC<{ title?: React.ReactNode; }> = ({ children, hasSetupCapabilities, onRecreateMlJob, title }) => ( -

{children}

+ {children} void; + previousQualityWarnings?: QualityWarning[]; validationErrors?: ValidationIndicesError[]; }> = ({ disabled = false, indices, isValidating, onChangeSelectedIndices, + previousQualityWarnings = [], validationErrors = [], }) => { const changeIsIndexSelected = useCallback( @@ -81,6 +84,7 @@ export const AnalysisSetupIndicesForm: React.FunctionComponent<{ key={index.name} onChangeIsSelected={changeIsIndexSelected} onChangeDatasetFilter={changeDatasetFilter} + previousQualityWarnings={previousQualityWarnings} /> ))} diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_dataset_filter.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_dataset_filter.tsx index d3ed8aeaf6155..481cc6071864c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_dataset_filter.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_dataset_filter.tsx @@ -7,6 +7,7 @@ import { EuiFilterButton, EuiFilterGroup, + EuiIconTip, EuiPopover, EuiPopoverTitle, EuiSelectable, @@ -14,11 +15,15 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useCallback, useMemo } from 'react'; -import { DatasetFilter } from '../../../../../common/log_analysis'; +import { DatasetFilter, QualityWarning } from '../../../../../common/log_analysis'; import { useVisibilityState } from '../../../../utils/use_visibility_state'; +import { CategoryQualityWarningReasonDescription } from '../../log_analysis_job_status/quality_warning_notices'; export const IndexSetupDatasetFilter: React.FC<{ - availableDatasets: string[]; + availableDatasets: Array<{ + dataset: string; + warnings: QualityWarning[]; + }>; datasetFilter: DatasetFilter; isDisabled?: boolean; onChangeDatasetFilter: (datasetFilter: DatasetFilter) => void; @@ -40,12 +45,13 @@ export const IndexSetupDatasetFilter: React.FC<{ [onChangeDatasetFilter] ); - const selectableOptions: EuiSelectableOption[] = useMemo( + const selectableOptions = useMemo( () => - availableDatasets.map((datasetName) => ({ - label: datasetName, + availableDatasets.map(({ dataset, warnings }) => ({ + label: dataset, + append: warnings.length > 0 ? : null, checked: - datasetFilter.type === 'includeSome' && datasetFilter.datasets.includes(datasetName) + datasetFilter.type === 'includeSome' && datasetFilter.datasets.includes(dataset) ? 'on' : undefined, })), @@ -86,3 +92,15 @@ export const IndexSetupDatasetFilter: React.FC<{ ); }; + +const DatasetWarningMarker: React.FC<{ warnings: QualityWarning[] }> = ({ warnings }) => { + const warningDescriptions = warnings.flatMap((warning) => + warning.type === 'categoryQualityWarning' + ? warning.reasons.map((reason) => ( + + )) + : [] + ); + + return ; +}; diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_row.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_row.tsx index 92774dbd6838b..b101b9b0cab0c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_row.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/index_setup_row.tsx @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiCheckbox, EuiCode, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiToolTip } from '@elastic/eui'; +import { EuiCheckbox, EuiCode, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useCallback } from 'react'; -import { DatasetFilter } from '../../../../../common/log_analysis'; +import React, { useCallback, useMemo } from 'react'; +import { DatasetFilter, QualityWarning } from '../../../../../common/log_analysis'; import { IndexSetupDatasetFilter } from './index_setup_dataset_filter'; import { AvailableIndex, ValidationUIError } from './validation'; @@ -16,7 +16,14 @@ export const IndexSetupRow: React.FC<{ isDisabled: boolean; onChangeDatasetFilter: (indexName: string, datasetFilter: DatasetFilter) => void; onChangeIsSelected: (indexName: string, isSelected: boolean) => void; -}> = ({ index, isDisabled, onChangeDatasetFilter, onChangeIsSelected }) => { + previousQualityWarnings: QualityWarning[]; +}> = ({ + index, + isDisabled, + onChangeDatasetFilter, + onChangeIsSelected, + previousQualityWarnings, +}) => { const changeIsSelected = useCallback( (event: React.ChangeEvent) => { onChangeIsSelected(index.name, event.currentTarget.checked); @@ -29,6 +36,29 @@ export const IndexSetupRow: React.FC<{ [index.name, onChangeDatasetFilter] ); + const datasets = useMemo( + () => + index.validity === 'valid' + ? index.availableDatasets.map((availableDataset) => ({ + dataset: availableDataset, + warnings: previousQualityWarnings.filter(({ dataset }) => dataset === availableDataset), + })) + : [], + [index, previousQualityWarnings] + ); + + const datasetIndependentQualityWarnings = useMemo( + () => previousQualityWarnings.filter(({ dataset }) => dataset === ''), + [previousQualityWarnings] + ); + + const hasWarnings = useMemo( + () => + datasetIndependentQualityWarnings.length > 0 || + datasets.some(({ warnings }) => warnings.length > 0), + [datasetIndependentQualityWarnings, datasets] + ); + const isSelected = index.validity === 'valid' && index.isSelected; return ( @@ -37,7 +67,23 @@ export const IndexSetupRow: React.FC<{ {index.name}} + label={ + <> + {index.name}{' '} + {index.validity === 'valid' && hasWarnings ? ( + + } + type="alert" + color="warning" + /> + ) : null} + + } onChange={changeIsSelected} checked={isSelected} disabled={isDisabled || index.validity === 'invalid'} @@ -45,12 +91,10 @@ export const IndexSetupRow: React.FC<{ {index.validity === 'invalid' ? ( - - - + ) : index.validity === 'valid' ? ( ( + +
{renderStory()}
+
+ )) + .add('Reconfiguration with partitioned warnings', () => { + return ( + + ); + }) + .add('Reconfiguration with unpartitioned warnings', () => { + return ( + + ); + }); + +const storyActions = actions('setStartTime', 'setEndTime', 'setValidatedIndices'); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx index d4c3c727bd34e..1ea972335d8fc 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/initial_configuration_step/initial_configuration_step.tsx @@ -9,7 +9,7 @@ import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; -import { SetupStatus } from '../../../../../common/log_analysis'; +import { QualityWarning, SetupStatus } from '../../../../../common/log_analysis'; import { AnalysisSetupIndicesForm } from './analysis_setup_indices_form'; import { AnalysisSetupTimerangeForm } from './analysis_setup_timerange_form'; import { @@ -31,6 +31,7 @@ interface InitialConfigurationStepProps { setupStatus: SetupStatus; setValidatedIndices: (selectedIndices: AvailableIndex[]) => void; validationErrors?: ValidationUIError[]; + previousQualityWarnings?: QualityWarning[]; } export const createInitialConfigurationStep = ( @@ -50,6 +51,7 @@ export const InitialConfigurationStep: React.FunctionComponent { const disabled = useMemo(() => !editableFormStatus.includes(setupStatus.type), [setupStatus]); @@ -75,6 +77,7 @@ export const InitialConfigurationStep: React.FunctionComponent diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/log_entry_categories_setup_view.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/log_entry_categories_setup_view.tsx index 2bc5b08a1016a..e7961a11a4d52 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/log_entry_categories_setup_view.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/log_entry_categories_setup_view.tsx @@ -6,6 +6,7 @@ import { EuiSpacer, EuiSteps, EuiText, EuiTitle } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; +import { useMount } from 'react-use'; import { useLogEntryCategoriesSetup } from '../../../../containers/logs/log_analysis/modules/log_entry_categories'; import { createInitialConfigurationStep } from '../initial_configuration_step'; import { createProcessStep } from '../process_step'; @@ -14,8 +15,10 @@ export const LogEntryCategoriesSetupView: React.FC<{ onClose: () => void; }> = ({ onClose }) => { const { + categoryQualityWarnings, cleanUpAndSetUp, endTime, + fetchJobStatus, isValidating, lastSetupErrorMessages, moduleDescriptor, @@ -30,6 +33,10 @@ export const LogEntryCategoriesSetupView: React.FC<{ viewResults, } = useLogEntryCategoriesSetup(); + useMount(() => { + fetchJobStatus(); + }); + const viewResultsAndClose = useCallback(() => { viewResults(); onClose(); @@ -47,6 +54,7 @@ export const LogEntryCategoriesSetupView: React.FC<{ setupStatus, setValidatedIndices, validationErrors, + previousQualityWarnings: categoryQualityWarnings, }), createProcessStep({ cleanUpAndSetUp, @@ -58,6 +66,7 @@ export const LogEntryCategoriesSetupView: React.FC<{ }), ], [ + categoryQualityWarnings, cleanUpAndSetUp, endTime, isValidating, diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout.tsx index 8e00254431438..407c851f2de95 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout.tsx @@ -15,14 +15,16 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { LogEntryRateSetupView } from './log_entry_rate_setup_view'; import { LogEntryCategoriesSetupView } from './log_entry_categories_setup_view'; +import { LogEntryRateSetupView } from './log_entry_rate_setup_view'; import { LogAnalysisModuleList } from './module_list'; -import { useLogAnalysisSetupFlyoutStateContext } from './setup_flyout_state'; +import { ModuleId, moduleIds, useLogAnalysisSetupFlyoutStateContext } from './setup_flyout_state'; const FLYOUT_HEADING_ID = 'logAnalysisSetupFlyoutHeading'; -export const LogAnalysisSetupFlyout: React.FC = () => { +export const LogAnalysisSetupFlyout: React.FC<{ + allowedModules?: ModuleId[]; +}> = ({ allowedModules = moduleIds }) => { const { closeFlyout, flyoutView, @@ -49,32 +51,58 @@ export const LogAnalysisSetupFlyout: React.FC = () => { {flyoutView.view === 'moduleList' ? ( - ) : flyoutView.view === 'moduleSetup' && flyoutView.module === 'logs_ui_analysis' ? ( - - - - ) : flyoutView.view === 'moduleSetup' && flyoutView.module === 'logs_ui_categories' ? ( - - - + ) : flyoutView.view === 'moduleSetup' && allowedModules.includes(flyoutView.module) ? ( + 1 ? showModuleList : undefined} + /> ) : null} ); }; +const ModuleSetupView: React.FC<{ + moduleId: ModuleId; + onClose: () => void; + onViewModuleList?: () => void; +}> = ({ moduleId, onClose, onViewModuleList }) => { + switch (moduleId) { + case 'logs_ui_analysis': + return ( + + + + ); + case 'logs_ui_categories': + return ( + + + + ); + } +}; + const LogAnalysisSetupFlyoutSubPage: React.FC<{ - onViewModuleList: () => void; + onViewModuleList?: () => void; }> = ({ children, onViewModuleList }) => ( - - - - - + {onViewModuleList ? ( + + + + + + ) : null} {children} ); diff --git a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout_state.ts b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout_state.ts index 7a64584df4303..5f131daf952bf 100644 --- a/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout_state.ts +++ b/x-pack/plugins/infra/public/components/logging/log_analysis_setup/setup_flyout/setup_flyout_state.ts @@ -9,6 +9,8 @@ import { useState, useCallback } from 'react'; export type ModuleId = 'logs_ui_analysis' | 'logs_ui_categories'; +export const moduleIds = ['logs_ui_analysis', 'logs_ui_categories'] as const; + type FlyoutView = | { view: 'hidden' } | { view: 'moduleList' } diff --git a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx index 83fe233553351..698b0d3ad0caf 100644 --- a/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx +++ b/x-pack/plugins/infra/public/components/saved_views/toolbar_control.tsx @@ -13,10 +13,9 @@ import { EuiDescriptionListTitle, EuiDescriptionListDescription, } from '@elastic/eui'; -import { EuiPopover } from '@elastic/eui'; +import { EuiPopover, EuiLink } from '@elastic/eui'; import { EuiListGroup, EuiListGroupItem } from '@elastic/eui'; import { EuiFlexItem } from '@elastic/eui'; -import { EuiButtonIcon } from '@elastic/eui'; import { SavedViewCreateModal } from './create_modal'; import { SavedViewUpdateModal } from './update_modal'; import { SavedViewManageViewsFlyout } from './manage_views_flyout'; @@ -151,15 +150,6 @@ export function SavedViewsToolbarControls(props: Props) { - - - (props: Props) { id="xpack.infra.savedView.currentView" /> - - {currentView - ? currentView.name - : i18n.translate('xpack.infra.savedView.unknownView', { - defaultMessage: 'No view selected', - })} - + + + {currentView + ? currentView.name + : i18n.translate('xpack.infra.savedView.unknownView', { + defaultMessage: 'No view selected', + })} + + diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/get_latest_categories_datasets_stats.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/get_latest_categories_datasets_stats.ts new file mode 100644 index 0000000000000..c095c7000f031 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/get_latest_categories_datasets_stats.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { HttpHandler } from 'src/core/public'; +import { + CategorizerStatus, + getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT, + getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT, + LogEntryCategoriesDatasetStats, + LOG_ANALYSIS_GET_LATEST_LOG_ENTRY_CATEGORY_DATASETS_STATS_PATH, +} from '../../../../../common/http_api'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +export { LogEntryCategoriesDatasetStats }; + +export const callGetLatestCategoriesDatasetsStatsAPI = async ( + { + jobIds, + startTime, + endTime, + includeCategorizerStatuses, + }: { + jobIds: string[]; + startTime: number; + endTime: number; + includeCategorizerStatuses: CategorizerStatus[]; + }, + fetch: HttpHandler +) => { + const response = await fetch(LOG_ANALYSIS_GET_LATEST_LOG_ENTRY_CATEGORY_DATASETS_STATS_PATH, { + method: 'POST', + body: JSON.stringify( + getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT.encode({ + data: { + jobIds, + timeRange: { startTime, endTime }, + includeCategorizerStatuses, + }, + }) + ), + }); + + return decodeOrThrow(getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT)(response); +}; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index dbd75a646b532..7441c0ab7d34c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -54,6 +54,17 @@ const jobStateRT = rt.keyof({ opening: null, }); +const jobAnalysisConfigRT = rt.partial({ + per_partition_categorization: rt.intersection([ + rt.type({ + enabled: rt.boolean, + }), + rt.partial({ + stop_on_warn: rt.boolean, + }), + ]), +}); + const jobCategorizationStatusRT = rt.keyof({ ok: null, warn: null, @@ -64,6 +75,7 @@ const jobModelSizeStatsRT = rt.type({ categorized_doc_count: rt.number, dead_category_count: rt.number, frequent_category_count: rt.number, + log_time: rt.number, rare_category_count: rt.number, total_category_count: rt.number, }); @@ -79,6 +91,8 @@ export const jobSummaryRT = rt.intersection([ datafeedIndices: rt.array(rt.string), datafeedState: datafeedStateRT, fullJob: rt.partial({ + analysis_config: jobAnalysisConfigRT, + create_time: rt.number, custom_settings: jobCustomSettingsRT, finished_time: rt.number, model_size_stats: jobModelSizeStatsRT, diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts index 4930c8b478a9c..ba355ad195b11 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts @@ -50,43 +50,3 @@ export interface ModuleSourceConfiguration { spaceId: string; timestampField: string; } - -interface ManyCategoriesWarningReason { - type: 'manyCategories'; - categoriesDocumentRatio: number; -} - -interface ManyDeadCategoriesWarningReason { - type: 'manyDeadCategories'; - deadCategoriesRatio: number; -} - -interface ManyRareCategoriesWarningReason { - type: 'manyRareCategories'; - rareCategoriesRatio: number; -} - -interface NoFrequentCategoriesWarningReason { - type: 'noFrequentCategories'; -} - -interface SingleCategoryWarningReason { - type: 'singleCategory'; -} - -export type CategoryQualityWarningReason = - | ManyCategoriesWarningReason - | ManyDeadCategoriesWarningReason - | ManyRareCategoriesWarningReason - | NoFrequentCategoriesWarningReason - | SingleCategoryWarningReason; - -export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type']; - -export interface CategoryQualityWarning { - type: 'categoryQualityWarning'; - jobId: string; - reasons: CategoryQualityWarningReason[]; -} - -export type QualityWarning = CategoryQualityWarning; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_quality.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_quality.ts index 346281fa94e1b..6bad94ec49f87 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_quality.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_quality.ts @@ -4,43 +4,124 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; +import { useDeepCompareEffect } from 'react-use'; import { - JobModelSizeStats, - JobSummary, - QualityWarning, CategoryQualityWarningReason, -} from '../../log_analysis_module_types'; + QualityWarning, +} from '../../../../../../common/log_analysis'; +import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; +import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; +import { + callGetLatestCategoriesDatasetsStatsAPI, + LogEntryCategoriesDatasetStats, +} from '../../api/get_latest_categories_datasets_stats'; +import { JobModelSizeStats, JobSummary } from '../../log_analysis_module_types'; export const useLogEntryCategoriesQuality = ({ jobSummaries }: { jobSummaries: JobSummary[] }) => { + const { + services: { + http: { fetch }, + }, + } = useKibanaContextForPlugin(); + + const [lastestWarnedDatasetsStats, setLatestWarnedDatasetsStats] = useState< + LogEntryCategoriesDatasetStats[] + >([]); + + const jobSummariesWithCategoryWarnings = useMemo( + () => jobSummaries.filter(isJobWithCategoryWarnings), + [jobSummaries] + ); + + const jobSummariesWithPartitionedCategoryWarnings = useMemo( + () => jobSummariesWithCategoryWarnings.filter(isJobWithPartitionedCategories), + [jobSummariesWithCategoryWarnings] + ); + + const [fetchLatestWarnedDatasetsStatsRequest, fetchLatestWarnedDatasetsStats] = useTrackedPromise( + { + cancelPreviousOn: 'creation', + createPromise: ( + statsIntervals: Array<{ jobId: string; startTime: number; endTime: number }> + ) => + Promise.all( + statsIntervals.map(({ jobId, startTime, endTime }) => + callGetLatestCategoriesDatasetsStatsAPI( + { jobIds: [jobId], startTime, endTime, includeCategorizerStatuses: ['warn'] }, + fetch + ) + ) + ), + onResolve: (results) => { + setLatestWarnedDatasetsStats(results.flatMap(({ data: { datasetStats } }) => datasetStats)); + }, + }, + [] + ); + + useDeepCompareEffect(() => { + fetchLatestWarnedDatasetsStats( + jobSummariesWithPartitionedCategoryWarnings.map((jobSummary) => ({ + jobId: jobSummary.id, + startTime: jobSummary.fullJob?.create_time ?? 0, + endTime: jobSummary.fullJob?.model_size_stats?.log_time ?? Date.now(), + })) + ); + }, [jobSummariesWithPartitionedCategoryWarnings]); + const categoryQualityWarnings: QualityWarning[] = useMemo( - () => - jobSummaries - .filter( - (jobSummary) => jobSummary.fullJob?.model_size_stats?.categorization_status === 'warn' - ) + () => [ + ...jobSummariesWithCategoryWarnings + .filter((jobSummary) => !isJobWithPartitionedCategories(jobSummary)) .map((jobSummary) => ({ - type: 'categoryQualityWarning', + type: 'categoryQualityWarning' as const, jobId: jobSummary.id, + dataset: '', reasons: jobSummary.fullJob?.model_size_stats ? getCategoryQualityWarningReasons(jobSummary.fullJob.model_size_stats) : [], })), - [jobSummaries] + ...lastestWarnedDatasetsStats.map((datasetStats) => ({ + type: 'categoryQualityWarning' as const, + jobId: datasetStats.job_id, + dataset: datasetStats.dataset, + reasons: getCategoryQualityWarningReasons(datasetStats), + })), + ], + [jobSummariesWithCategoryWarnings, lastestWarnedDatasetsStats] ); return { categoryQualityWarnings, + lastLatestWarnedDatasetsStatsRequestErrors: + fetchLatestWarnedDatasetsStatsRequest.state === 'rejected' + ? fetchLatestWarnedDatasetsStatsRequest.value + : null, + isLoadingCategoryQualityWarnings: fetchLatestWarnedDatasetsStatsRequest.state === 'pending', }; }; +const isJobWithCategoryWarnings = (jobSummary: JobSummary) => + jobSummary.fullJob?.model_size_stats?.categorization_status === 'warn'; + +const isJobWithPartitionedCategories = (jobSummary: JobSummary) => + jobSummary.fullJob?.analysis_config?.per_partition_categorization ?? false; + const getCategoryQualityWarningReasons = ({ categorized_doc_count: categorizedDocCount, dead_category_count: deadCategoryCount, frequent_category_count: frequentCategoryCount, rare_category_count: rareCategoryCount, total_category_count: totalCategoryCount, -}: JobModelSizeStats): CategoryQualityWarningReason[] => { +}: Pick< + JobModelSizeStats, + | 'categorized_doc_count' + | 'dead_category_count' + | 'frequent_category_count' + | 'rare_category_count' + | 'total_category_count' +>): CategoryQualityWarningReason[] => { const rareCategoriesRatio = rareCategoryCount / totalCategoryCount; const categoriesDocumentRatio = totalCategoryCount / categorizedDocCount; const deadCategoriesRatio = deadCategoryCount / totalCategoryCount; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_setup.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_setup.tsx index 399c30cf47e71..269b64c6f4076 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_setup.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_setup.tsx @@ -9,7 +9,9 @@ import { useLogEntryCategoriesModuleContext } from './use_log_entry_categories_m export const useLogEntryCategoriesSetup = () => { const { + categoryQualityWarnings, cleanUpAndSetUpModule, + fetchJobStatus, lastSetupErrorMessages, moduleDescriptor, setUpModule, @@ -37,8 +39,10 @@ export const useLogEntryCategoriesSetup = () => { }); return { + categoryQualityWarnings, cleanUpAndSetUp, endTime, + fetchJobStatus, isValidating, lastSetupErrorMessages, moduleDescriptor, diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts new file mode 100644 index 0000000000000..ee70edc31d49b --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_api_types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; + +export const getMlCapabilitiesResponsePayloadRT = rt.type({ + capabilities: rt.type({ + canGetJobs: rt.boolean, + canCreateJob: rt.boolean, + canDeleteJob: rt.boolean, + canOpenJob: rt.boolean, + canCloseJob: rt.boolean, + canForecastJob: rt.boolean, + canGetDatafeeds: rt.boolean, + canStartStopDatafeed: rt.boolean, + canUpdateJob: rt.boolean, + canUpdateDatafeed: rt.boolean, + canPreviewDatafeed: rt.boolean, + }), + isPlatinumOrTrialLicense: rt.boolean, + mlFeatureEnabledInSpace: rt.boolean, + upgradeInProgress: rt.boolean, +}); + +export type GetMlCapabilitiesResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts new file mode 100644 index 0000000000000..23fa338e74f14 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { npStart } from '../../../legacy_singletons'; + +import { getDatafeedId, getJobId } from '../../../../common/infra_ml'; +import { throwErrors, createPlainError } from '../../../../common/runtime_types'; + +export const callDeleteJobs = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time + const deleteJobsResponse = await npStart.http.fetch('/api/ml/jobs/delete_jobs', { + method: 'POST', + body: JSON.stringify( + deleteJobsRequestPayloadRT.encode({ + jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + }) + ), + }); + + return pipe( + deleteJobsResponsePayloadRT.decode(deleteJobsResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const callGetJobDeletionTasks = async () => { + const jobDeletionTasksResponse = await npStart.http.fetch('/api/ml/jobs/deleting_jobs_tasks'); + + return pipe( + getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const callStopDatafeeds = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 + const stopDatafeedResponse = await npStart.http.fetch('/api/ml/jobs/stop_datafeeds', { + method: 'POST', + body: JSON.stringify( + stopDatafeedsRequestPayloadRT.encode({ + datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, sourceId, jobType)), + }) + ), + }); + + return pipe( + stopDatafeedsResponsePayloadRT.decode(stopDatafeedResponse), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const deleteJobsRequestPayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export type DeleteJobsRequestPayload = rt.TypeOf; + +export const deleteJobsResponsePayloadRT = rt.record( + rt.string, + rt.type({ + deleted: rt.boolean, + }) +); + +export type DeleteJobsResponsePayload = rt.TypeOf; + +export const getJobDeletionTasksResponsePayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export const stopDatafeedsRequestPayloadRT = rt.type({ + datafeedIds: rt.array(rt.string), +}); + +export const stopDatafeedsResponsePayloadRT = rt.record( + rt.string, + rt.type({ + stopped: rt.boolean, + }) +); diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts new file mode 100644 index 0000000000000..3fddb63f69791 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { getJobId, jobCustomSettingsRT } from '../../../../common/infra_ml'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callJobsSummaryAPI = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const response = await npStart.http.fetch('/api/ml/jobs/jobs_summary', { + method: 'POST', + body: JSON.stringify( + fetchJobStatusRequestPayloadRT.encode({ + jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + }) + ), + }); + return pipe( + fetchJobStatusResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +export const fetchJobStatusRequestPayloadRT = rt.type({ + jobIds: rt.array(rt.string), +}); + +export type FetchJobStatusRequestPayload = rt.TypeOf; + +const datafeedStateRT = rt.keyof({ + started: null, + stopped: null, + stopping: null, + '': null, +}); + +const jobStateRT = rt.keyof({ + closed: null, + closing: null, + deleting: null, + failed: null, + opened: null, + opening: null, +}); + +const jobCategorizationStatusRT = rt.keyof({ + ok: null, + warn: null, +}); + +const jobModelSizeStatsRT = rt.type({ + categorization_status: jobCategorizationStatusRT, + categorized_doc_count: rt.number, + dead_category_count: rt.number, + frequent_category_count: rt.number, + rare_category_count: rt.number, + total_category_count: rt.number, +}); + +export type JobModelSizeStats = rt.TypeOf; + +export const jobSummaryRT = rt.intersection([ + rt.type({ + id: rt.string, + jobState: jobStateRT, + }), + rt.partial({ + datafeedIndices: rt.array(rt.string), + datafeedState: datafeedStateRT, + fullJob: rt.partial({ + custom_settings: jobCustomSettingsRT, + finished_time: rt.number, + model_size_stats: jobModelSizeStatsRT, + }), + }), +]); + +export type JobSummary = rt.TypeOf; + +export const fetchJobStatusResponsePayloadRT = rt.array(jobSummaryRT); + +export type FetchJobStatusResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts new file mode 100644 index 0000000000000..d492522c120a1 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { jobCustomSettingsRT } from '../../../../common/log_analysis'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callGetMlModuleAPI = async (moduleId: string) => { + const response = await npStart.http.fetch(`/api/ml/modules/get_module/${moduleId}`, { + method: 'GET', + }); + + return pipe( + getMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const jobDefinitionRT = rt.type({ + id: rt.string, + config: rt.type({ + custom_settings: jobCustomSettingsRT, + }), +}); + +export type JobDefinition = rt.TypeOf; + +const getMlModuleResponsePayloadRT = rt.type({ + id: rt.string, + jobs: rt.array(jobDefinitionRT), +}); + +export type GetMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts new file mode 100644 index 0000000000000..06b0e075387b0 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as rt from 'io-ts'; +import { npStart } from '../../../legacy_singletons'; + +import { getJobIdPrefix, jobCustomSettingsRT } from '../../../../common/infra_ml'; +import { createPlainError, throwErrors } from '../../../../common/runtime_types'; + +export const callSetupMlModuleAPI = async ( + moduleId: string, + start: number | undefined, + end: number | undefined, + spaceId: string, + sourceId: string, + indexPattern: string, + jobOverrides: SetupMlModuleJobOverrides[] = [], + datafeedOverrides: SetupMlModuleDatafeedOverrides[] = [], + query?: object +) => { + const response = await npStart.http.fetch(`/api/ml/modules/setup/${moduleId}`, { + method: 'POST', + body: JSON.stringify( + setupMlModuleRequestPayloadRT.encode({ + start, + end, + indexPatternName: indexPattern, + prefix: getJobIdPrefix(spaceId, sourceId), + startDatafeed: true, + jobOverrides, + datafeedOverrides, + query, + }) + ), + }); + + return pipe( + setupMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const setupMlModuleTimeParamsRT = rt.partial({ + start: rt.number, + end: rt.number, +}); + +const setupMlModuleJobOverridesRT = rt.type({ + job_id: rt.string, + custom_settings: jobCustomSettingsRT, +}); + +export type SetupMlModuleJobOverrides = rt.TypeOf; + +const setupMlModuleDatafeedOverridesRT = rt.object; + +export type SetupMlModuleDatafeedOverrides = rt.TypeOf; + +const setupMlModuleRequestParamsRT = rt.intersection([ + rt.strict({ + indexPatternName: rt.string, + prefix: rt.string, + startDatafeed: rt.boolean, + jobOverrides: rt.array(setupMlModuleJobOverridesRT), + datafeedOverrides: rt.array(setupMlModuleDatafeedOverridesRT), + }), + rt.exact( + rt.partial({ + query: rt.object, + }) + ), +]); + +const setupMlModuleRequestPayloadRT = rt.intersection([ + setupMlModuleTimeParamsRT, + setupMlModuleRequestParamsRT, +]); + +const setupErrorResponseRT = rt.type({ + msg: rt.string, +}); + +const datafeedSetupResponseRT = rt.intersection([ + rt.type({ + id: rt.string, + started: rt.boolean, + success: rt.boolean, + }), + rt.partial({ + error: setupErrorResponseRT, + }), +]); + +const jobSetupResponseRT = rt.intersection([ + rt.type({ + id: rt.string, + success: rt.boolean, + }), + rt.partial({ + error: setupErrorResponseRT, + }), +]); + +const setupMlModuleResponsePayloadRT = rt.type({ + datafeeds: rt.array(datafeedSetupResponseRT), + jobs: rt.array(jobSetupResponseRT), +}); + +export type SetupMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx new file mode 100644 index 0000000000000..f4c90a459af6a --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import createContainer from 'constate'; +import { useMemo, useState, useEffect } from 'react'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { npStart } from '../../legacy_singletons'; +import { + getMlCapabilitiesResponsePayloadRT, + GetMlCapabilitiesResponsePayload, +} from './api/ml_api_types'; +import { throwErrors, createPlainError } from '../../../common/runtime_types'; + +export const useInfraMLCapabilities = () => { + const [mlCapabilities, setMlCapabilities] = useState( + initialMlCapabilities + ); + + const [fetchMlCapabilitiesRequest, fetchMlCapabilities] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + const rawResponse = await npStart.http.fetch('/api/ml/ml_capabilities'); + + return pipe( + getMlCapabilitiesResponsePayloadRT.decode(rawResponse), + fold(throwErrors(createPlainError), identity) + ); + }, + onResolve: (response) => { + setMlCapabilities(response); + }, + }, + [] + ); + + useEffect(() => { + fetchMlCapabilities(); + }, [fetchMlCapabilities]); + + const isLoading = useMemo(() => fetchMlCapabilitiesRequest.state === 'pending', [ + fetchMlCapabilitiesRequest.state, + ]); + + const hasInfraMLSetupCapabilities = mlCapabilities.capabilities.canCreateJob; + const hasInfraMLReadCapabilities = mlCapabilities.capabilities.canGetJobs; + const hasInfraMLCapabilites = + mlCapabilities.isPlatinumOrTrialLicense && mlCapabilities.mlFeatureEnabledInSpace; + + return { + hasInfraMLCapabilites, + hasInfraMLReadCapabilities, + hasInfraMLSetupCapabilities, + isLoading, + }; +}; + +export const [InfraMLCapabilitiesProvider, useInfraMLCapabilitiesContext] = createContainer( + useInfraMLCapabilities +); + +const initialMlCapabilities = { + capabilities: { + canGetJobs: false, + canCreateJob: false, + canDeleteJob: false, + canOpenJob: false, + canCloseJob: false, + canForecastJob: false, + canGetDatafeeds: false, + canStartStopDatafeed: false, + canUpdateJob: false, + canUpdateDatafeed: false, + canPreviewDatafeed: false, + canGetCalendars: false, + canCreateCalendar: false, + canDeleteCalendar: false, + canGetFilters: false, + canCreateFilter: false, + canDeleteFilter: false, + canFindFileStructure: false, + canGetDataFrameJobs: false, + canDeleteDataFrameJob: false, + canPreviewDataFrameJob: false, + canCreateDataFrameJob: false, + canStartStopDataFrameJob: false, + }, + isPlatinumOrTrialLicense: false, + mlFeatureEnabledInSpace: false, + upgradeInProgress: false, +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx new file mode 100644 index 0000000000000..736982c8043b1 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.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 { getJobId } from '../../../common/infra_ml'; +import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './api/ml_cleanup'; + +export const cleanUpJobsAndDatafeeds = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + try { + await callStopDatafeeds(spaceId, sourceId, jobTypes); + } catch (err) { + // Proceed only if datafeed has been deleted or didn't exist in the first place + if (err?.res?.status !== 404) { + throw err; + } + } + + return await deleteJobs(spaceId, sourceId, jobTypes); +}; + +const deleteJobs = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const deleteJobsResponse = await callDeleteJobs(spaceId, sourceId, jobTypes); + await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes); + return deleteJobsResponse; +}; + +const waitUntilJobsAreDeleted = async ( + spaceId: string, + sourceId: string, + jobTypes: JobType[] +) => { + const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); + while (true) { + const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(); + const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); + + if (needToWait) { + await timeout(1000); + } else { + return true; + } + } +}; + +const timeout = (ms: number) => new Promise((res) => setTimeout(res, ms)); diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx new file mode 100644 index 0000000000000..349541d108f5e --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { useCallback, useMemo } from 'react'; +import { DatasetFilter } from '../../../common/infra_ml'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { useModuleStatus } from './infra_ml_module_status'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModule = ({ + sourceConfiguration, + moduleDescriptor, +}: { + sourceConfiguration: ModuleSourceConfiguration; + moduleDescriptor: ModuleDescriptor; +}) => { + const { spaceId, sourceId, timestampField } = sourceConfiguration; + const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); + + const [, fetchJobStatus] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + dispatchModuleStatus({ type: 'fetchingJobStatuses' }); + return await moduleDescriptor.getJobSummary(spaceId, sourceId); + }, + onResolve: (jobResponse) => { + dispatchModuleStatus({ + type: 'fetchedJobStatuses', + payload: jobResponse, + spaceId, + sourceId, + }); + }, + onReject: () => { + dispatchModuleStatus({ type: 'failedFetchingJobStatuses' }); + }, + }, + [spaceId, sourceId] + ); + + const [, setUpModule] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async ( + selectedIndices: string[], + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + partitionField?: string + ) => { + dispatchModuleStatus({ type: 'startedSetup' }); + const setupResult = await moduleDescriptor.setUpModule( + start, + end, + datasetFilter, + { + indices: selectedIndices, + sourceId, + spaceId, + timestampField, + }, + partitionField + ); + const jobSummaries = await moduleDescriptor.getJobSummary(spaceId, sourceId); + return { setupResult, jobSummaries }; + }, + onResolve: ({ setupResult: { datafeeds, jobs }, jobSummaries }) => { + dispatchModuleStatus({ + type: 'finishedSetup', + datafeedSetupResults: datafeeds, + jobSetupResults: jobs, + jobSummaries, + spaceId, + sourceId, + }); + }, + onReject: () => { + dispatchModuleStatus({ type: 'failedSetup' }); + }, + }, + [moduleDescriptor.setUpModule, spaceId, sourceId, timestampField] + ); + + const [cleanUpModuleRequest, cleanUpModule] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await moduleDescriptor.cleanUpModule(spaceId, sourceId); + }, + }, + [spaceId, sourceId] + ); + + const isCleaningUp = useMemo(() => cleanUpModuleRequest.state === 'pending', [ + cleanUpModuleRequest.state, + ]); + + const cleanUpAndSetUpModule = useCallback( + ( + selectedIndices: string[], + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + partitionField?: string + ) => { + dispatchModuleStatus({ type: 'startedSetup' }); + cleanUpModule() + .then(() => { + setUpModule(selectedIndices, start, end, datasetFilter, partitionField); + }) + .catch(() => { + dispatchModuleStatus({ type: 'failedSetup' }); + }); + }, + [cleanUpModule, dispatchModuleStatus, setUpModule] + ); + + const viewResults = useCallback(() => { + dispatchModuleStatus({ type: 'viewedResults' }); + }, [dispatchModuleStatus]); + + const jobIds = useMemo(() => moduleDescriptor.getJobIds(spaceId, sourceId), [ + moduleDescriptor, + spaceId, + sourceId, + ]); + + return { + cleanUpAndSetUpModule, + cleanUpModule, + fetchJobStatus, + isCleaningUp, + jobIds, + jobStatus: moduleStatus.jobStatus, + jobSummaries: moduleStatus.jobSummaries, + lastSetupErrorMessages: moduleStatus.lastSetupErrorMessages, + moduleDescriptor, + setUpModule, + setupStatus: moduleStatus.setupStatus, + sourceConfiguration, + viewResults, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts new file mode 100644 index 0000000000000..2d90f5d531010 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_configuration.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useMemo } from 'react'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModuleConfiguration = ({ + moduleDescriptor, + sourceConfiguration, +}: { + moduleDescriptor: ModuleDescriptor; + sourceConfiguration: ModuleSourceConfiguration; +}) => { + const getIsJobConfigurationOutdated = useMemo( + () => isJobConfigurationOutdated(moduleDescriptor, sourceConfiguration), + [sourceConfiguration, moduleDescriptor] + ); + + return { + getIsJobConfigurationOutdated, + }; +}; + +export const isJobConfigurationOutdated = ( + { bucketSpan }: ModuleDescriptor, + currentSourceConfiguration: ModuleSourceConfiguration +) => (jobSummary: JobSummary): boolean => { + if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { + return false; + } + + const jobConfiguration = jobSummary.fullJob.custom_settings.metrics_source_config; + + return !( + jobConfiguration && + jobConfiguration.bucketSpan === bucketSpan && + jobConfiguration.indexPattern && + isSubset( + new Set(jobConfiguration.indexPattern.split(',')), + new Set(currentSourceConfiguration.indices) + ) && + jobConfiguration.timestampField === currentSourceConfiguration.timestampField + ); +}; + +const isSubset = (subset: Set, superset: Set) => { + return Array.from(subset).every((subsetElement) => superset.has(subsetElement)); +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx new file mode 100644 index 0000000000000..3c7ffcfd4a4e2 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.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 { useCallback, useMemo, useState } from 'react'; +import { getJobId } from '../../../common/log_analysis'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { JobSummary } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +export const useInfraMLModuleDefinition = ({ + sourceConfiguration: { spaceId, sourceId }, + moduleDescriptor, +}: { + sourceConfiguration: ModuleSourceConfiguration; + moduleDescriptor: ModuleDescriptor; +}) => { + const [moduleDefinition, setModuleDefinition] = useState< + GetMlModuleResponsePayload | undefined + >(); + + const jobDefinitionByJobId = useMemo( + () => + moduleDefinition + ? moduleDefinition.jobs.reduce>( + (accumulatedJobDefinitions, jobDefinition) => ({ + ...accumulatedJobDefinitions, + [getJobId(spaceId, sourceId, jobDefinition.id)]: jobDefinition, + }), + {} + ) + : {}, + [moduleDefinition, sourceId, spaceId] + ); + + const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await moduleDescriptor.getModuleDefinition(); + }, + onResolve: (response) => { + setModuleDefinition(response); + }, + onReject: () => { + setModuleDefinition(undefined); + }, + }, + [moduleDescriptor.getModuleDefinition, spaceId, sourceId] + ); + + const getIsJobDefinitionOutdated = useCallback( + (jobSummary: JobSummary): boolean => { + const jobDefinition: JobDefinition | undefined = jobDefinitionByJobId[jobSummary.id]; + + if (jobDefinition == null) { + return false; + } + + const currentRevision = jobDefinition?.config.custom_settings.job_revision; + return (jobSummary.fullJob?.custom_settings?.job_revision ?? 0) < (currentRevision ?? 0); + }, + [jobDefinitionByJobId] + ); + + return { + fetchModuleDefinition, + fetchModuleDefinitionRequestState: fetchModuleDefinitionRequest.state, + getIsJobDefinitionOutdated, + jobDefinitionByJobId, + moduleDefinition, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx new file mode 100644 index 0000000000000..63d479546b44f --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_status.tsx @@ -0,0 +1,268 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { + JobStatus, + getDatafeedId, + getJobId, + isJobStatusWithResults, + SetupStatus, +} from '../../../common/infra_ml'; +import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; +import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; +import { MandatoryProperty } from '../../../common/utility_types'; + +interface StatusReducerState { + jobStatus: Record; + jobSummaries: JobSummary[]; + lastSetupErrorMessages: string[]; + setupStatus: SetupStatus; +} + +type StatusReducerAction = + | { type: 'startedSetup' } + | { + type: 'finishedSetup'; + sourceId: string; + spaceId: string; + jobSetupResults: SetupMlModuleResponsePayload['jobs']; + jobSummaries: FetchJobStatusResponsePayload; + datafeedSetupResults: SetupMlModuleResponsePayload['datafeeds']; + } + | { type: 'failedSetup' } + | { type: 'fetchingJobStatuses' } + | { + type: 'fetchedJobStatuses'; + spaceId: string; + sourceId: string; + payload: FetchJobStatusResponsePayload; + } + | { type: 'failedFetchingJobStatuses' } + | { type: 'viewedResults' }; + +const createInitialState = ({ + jobTypes, +}: { + jobTypes: JobType[]; +}): StatusReducerState => ({ + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'unknown', + }), + {} as Record + ), + jobSummaries: [], + lastSetupErrorMessages: [], + setupStatus: { type: 'initializing' }, +}); + +const createStatusReducer = (jobTypes: JobType[]) => ( + state: StatusReducerState, + action: StatusReducerAction +): StatusReducerState => { + switch (action.type) { + case 'startedSetup': { + return { + ...state, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'initializing', + }), + {} as Record + ), + setupStatus: { type: 'pending' }, + }; + } + case 'finishedSetup': { + const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, sourceId } = action; + const nextJobStatus = jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: + hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobSetupResults) && + hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))( + datafeedSetupResults + ) + ? 'started' + : 'failed', + }), + {} as Record + ); + const nextSetupStatus: SetupStatus = Object.values(nextJobStatus).every( + (jobState) => jobState === 'started' + ) + ? { type: 'succeeded' } + : { + type: 'failed', + reasons: [ + ...Object.values(datafeedSetupResults) + .filter(hasError) + .map((datafeed) => datafeed.error.msg), + ...Object.values(jobSetupResults) + .filter(hasError) + .map((job) => job.error.msg), + ], + }; + + return { + ...state, + jobStatus: nextJobStatus, + jobSummaries, + setupStatus: nextSetupStatus, + }; + } + case 'failedSetup': { + return { + ...state, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'failed', + }), + {} as Record + ), + setupStatus: { type: 'failed', reasons: ['unknown'] }, + }; + } + case 'fetchingJobStatuses': { + return { + ...state, + setupStatus: + state.setupStatus.type === 'unknown' ? { type: 'initializing' } : state.setupStatus, + }; + } + case 'fetchedJobStatuses': { + const { payload: jobSummaries, spaceId, sourceId } = action; + const { setupStatus } = state; + const nextJobStatus = jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: getJobStatus(getJobId(spaceId, sourceId, jobType))(jobSummaries), + }), + {} as Record + ); + const nextSetupStatus = getSetupStatus(nextJobStatus)(setupStatus); + + return { + ...state, + jobSummaries, + jobStatus: nextJobStatus, + setupStatus: nextSetupStatus, + }; + } + case 'failedFetchingJobStatuses': { + return { + ...state, + setupStatus: { type: 'unknown' }, + jobStatus: jobTypes.reduce( + (accumulatedJobStatus, jobType) => ({ + ...accumulatedJobStatus, + [jobType]: 'unknown', + }), + {} as Record + ), + }; + } + case 'viewedResults': { + return { + ...state, + setupStatus: { type: 'skipped', newlyCreated: true }, + }; + } + default: { + return state; + } + } +}; + +const hasSuccessfullyCreatedJob = (jobId: string) => ( + jobSetupResponses: SetupMlModuleResponsePayload['jobs'] +) => + jobSetupResponses.filter( + (jobSetupResponse) => + jobSetupResponse.id === jobId && jobSetupResponse.success && !jobSetupResponse.error + ).length > 0; + +const hasSuccessfullyStartedDatafeed = (datafeedId: string) => ( + datafeedSetupResponses: SetupMlModuleResponsePayload['datafeeds'] +) => + datafeedSetupResponses.filter( + (datafeedSetupResponse) => + datafeedSetupResponse.id === datafeedId && + datafeedSetupResponse.success && + datafeedSetupResponse.started && + !datafeedSetupResponse.error + ).length > 0; + +const getJobStatus = (jobId: string) => ( + jobSummaries: FetchJobStatusResponsePayload +): JobStatus => { + return ( + jobSummaries + .filter((jobSummary) => jobSummary.id === jobId) + .map( + (jobSummary): JobStatus => { + if (jobSummary.jobState === 'failed' || jobSummary.datafeedState === '') { + return 'failed'; + } else if ( + jobSummary.jobState === 'closed' && + jobSummary.datafeedState === 'stopped' && + jobSummary.fullJob && + jobSummary.fullJob.finished_time != null + ) { + return 'finished'; + } else if ( + jobSummary.jobState === 'closed' || + jobSummary.jobState === 'closing' || + jobSummary.datafeedState === 'stopped' + ) { + return 'stopped'; + } else if (jobSummary.jobState === 'opening') { + return 'initializing'; + } else if (jobSummary.jobState === 'opened' && jobSummary.datafeedState === 'started') { + return 'started'; + } + + return 'unknown'; + } + )[0] || 'missing' + ); +}; + +const getSetupStatus = (everyJobStatus: Record) => ( + previousSetupStatus: SetupStatus +): SetupStatus => { + return Object.entries(everyJobStatus).reduce( + (setupStatus, [, jobStatus]) => { + if (jobStatus === 'missing') { + return { type: 'required' }; + } else if (setupStatus.type === 'required' || setupStatus.type === 'succeeded') { + return setupStatus; + } else if (setupStatus.type === 'skipped' || isJobStatusWithResults(jobStatus)) { + return { + type: 'skipped', + // preserve newlyCreated status + newlyCreated: setupStatus.type === 'skipped' && setupStatus.newlyCreated, + }; + } + + return setupStatus; + }, + previousSetupStatus + ); +}; + +const hasError = ( + value: Value +): value is MandatoryProperty => value.error != null; + +export const useModuleStatus = (jobTypes: JobType[]) => { + return useReducer(createStatusReducer(jobTypes), { jobTypes }, createInitialState); +}; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts new file mode 100644 index 0000000000000..a9f2671de8259 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { + ValidateLogEntryDatasetsResponsePayload, + ValidationIndicesResponsePayload, +} from '../../../common/http_api/log_analysis'; +import { DatasetFilter } from '../../../common/infra_ml'; +import { DeleteJobsResponsePayload } from './api/ml_cleanup'; +import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload } from './api/ml_get_module'; +import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; + +export { JobModelSizeStats, JobSummary } from './api/ml_get_jobs_summary_api'; + +export interface ModuleDescriptor { + moduleId: string; + moduleName: string; + moduleDescription: string; + jobTypes: JobType[]; + bucketSpan: number; + getJobIds: (spaceId: string, sourceId: string) => Record; + getJobSummary: (spaceId: string, sourceId: string) => Promise; + getModuleDefinition: () => Promise; + setUpModule: ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + sourceConfiguration: ModuleSourceConfiguration, + partitionField?: string + ) => Promise; + cleanUpModule: (spaceId: string, sourceId: string) => Promise; + validateSetupIndices: ( + indices: string[], + timestampField: string + ) => Promise; + validateSetupDatasets: ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number + ) => Promise; +} + +export interface ModuleSourceConfiguration { + indices: string[]; + sourceId: string; + spaceId: string; + timestampField: string; +} + +interface ManyCategoriesWarningReason { + type: 'manyCategories'; + categoriesDocumentRatio: number; +} + +interface ManyDeadCategoriesWarningReason { + type: 'manyDeadCategories'; + deadCategoriesRatio: number; +} + +interface ManyRareCategoriesWarningReason { + type: 'manyRareCategories'; + rareCategoriesRatio: number; +} + +interface NoFrequentCategoriesWarningReason { + type: 'noFrequentCategories'; +} + +interface SingleCategoryWarningReason { + type: 'singleCategory'; +} + +export type CategoryQualityWarningReason = + | ManyCategoriesWarningReason + | ManyDeadCategoriesWarningReason + | ManyRareCategoriesWarningReason + | NoFrequentCategoriesWarningReason + | SingleCategoryWarningReason; + +export type CategoryQualityWarningReasonType = CategoryQualityWarningReason['type']; + +export interface CategoryQualityWarning { + type: 'categoryQualityWarning'; + jobId: string; + reasons: CategoryQualityWarningReason[]; +} + +export type QualityWarning = CategoryQualityWarning; diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts new file mode 100644 index 0000000000000..0dfe3b301f240 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts @@ -0,0 +1,289 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { isEqual } from 'lodash'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { usePrevious } from 'react-use'; +import { + combineDatasetFilters, + DatasetFilter, + filterDatasetFilter, + isExampleDataIndex, +} from '../../../common/infra_ml'; +import { + AvailableIndex, + ValidationIndicesError, + ValidationUIError, +} from '../../components/logging/log_analysis_setup/initial_configuration_step'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +type SetupHandler = ( + indices: string[], + startTime: number | undefined, + endTime: number | undefined, + datasetFilter: DatasetFilter +) => void; + +interface AnalysisSetupStateArguments { + cleanUpAndSetUpModule: SetupHandler; + moduleDescriptor: ModuleDescriptor; + setUpModule: SetupHandler; + sourceConfiguration: ModuleSourceConfiguration; +} + +const fourWeeksInMs = 86400000 * 7 * 4; + +export const useAnalysisSetupState = ({ + cleanUpAndSetUpModule, + moduleDescriptor: { validateSetupDatasets, validateSetupIndices }, + setUpModule, + sourceConfiguration, +}: AnalysisSetupStateArguments) => { + const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs); + const [endTime, setEndTime] = useState(undefined); + + const isTimeRangeValid = useMemo( + () => (startTime != null && endTime != null ? startTime < endTime : true), + [endTime, startTime] + ); + + const [validatedIndices, setValidatedIndices] = useState( + sourceConfiguration.indices.map((indexName) => ({ + name: indexName, + validity: 'unknown' as const, + })) + ); + + const updateIndicesWithValidationErrors = useCallback( + (validationErrors: ValidationIndicesError[]) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + const indexValiationErrors = validationErrors.filter( + ({ index }) => index === previousAvailableIndex.name + ); + + if (indexValiationErrors.length > 0) { + return { + validity: 'invalid', + name: previousAvailableIndex.name, + errors: indexValiationErrors, + }; + } else if (previousAvailableIndex.validity === 'valid') { + return { + ...previousAvailableIndex, + validity: 'valid', + errors: [], + }; + } else { + return { + validity: 'valid', + name: previousAvailableIndex.name, + isSelected: !isExampleDataIndex(previousAvailableIndex.name), + availableDatasets: [], + datasetFilter: { + type: 'includeAll' as const, + }, + }; + } + }) + ), + [] + ); + + const updateIndicesWithAvailableDatasets = useCallback( + (availableDatasets: Array<{ indexName: string; datasets: string[] }>) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + if (previousAvailableIndex.validity !== 'valid') { + return previousAvailableIndex; + } + + const availableDatasetsForIndex = availableDatasets.filter( + ({ indexName }) => indexName === previousAvailableIndex.name + ); + const newAvailableDatasets = availableDatasetsForIndex.flatMap( + ({ datasets }) => datasets + ); + + // filter out datasets that have disappeared if this index' datasets were updated + const newDatasetFilter: DatasetFilter = + availableDatasetsForIndex.length > 0 + ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) => + newAvailableDatasets.includes(dataset) + ) + : previousAvailableIndex.datasetFilter; + + return { + ...previousAvailableIndex, + availableDatasets: newAvailableDatasets, + datasetFilter: newDatasetFilter, + }; + }) + ), + [] + ); + + const validIndexNames = useMemo( + () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name), + [validatedIndices] + ); + + const selectedIndexNames = useMemo( + () => + validatedIndices + .filter((index) => index.validity === 'valid' && index.isSelected) + .map((i) => i.name), + [validatedIndices] + ); + + const datasetFilter = useMemo( + () => + validatedIndices + .flatMap((validatedIndex) => + validatedIndex.validity === 'valid' + ? validatedIndex.datasetFilter + : { type: 'includeAll' as const } + ) + .reduce(combineDatasetFilters, { type: 'includeAll' as const }), + [validatedIndices] + ); + + const [validateIndicesRequest, validateIndices] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await validateSetupIndices( + sourceConfiguration.indices, + sourceConfiguration.timestampField + ); + }, + onResolve: ({ data: { errors } }) => { + updateIndicesWithValidationErrors(errors); + }, + onReject: () => { + setValidatedIndices([]); + }, + }, + [sourceConfiguration.indices, sourceConfiguration.timestampField] + ); + + const [validateDatasetsRequest, validateDatasets] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + if (validIndexNames.length === 0) { + return { data: { datasets: [] } }; + } + + return await validateSetupDatasets( + validIndexNames, + sourceConfiguration.timestampField, + startTime ?? 0, + endTime ?? Date.now() + ); + }, + onResolve: ({ data: { datasets } }) => { + updateIndicesWithAvailableDatasets(datasets); + }, + }, + [validIndexNames, sourceConfiguration.timestampField, startTime, endTime] + ); + + const setUp = useCallback(() => { + return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const cleanUpAndSetUp = useCallback(() => { + return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const isValidating = useMemo( + () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending', + [validateDatasetsRequest.state, validateIndicesRequest.state] + ); + + const validationErrors = useMemo(() => { + if (isValidating) { + return []; + } + + return [ + // validate request status + ...(validateIndicesRequest.state === 'rejected' || + validateDatasetsRequest.state === 'rejected' + ? [{ error: 'NETWORK_ERROR' as const }] + : []), + // validation request results + ...validatedIndices.reduce((errors, index) => { + return index.validity === 'invalid' && selectedIndexNames.includes(index.name) + ? [...errors, ...index.errors] + : errors; + }, []), + // index count + ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []), + // time range + ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []), + ]; + }, [ + isValidating, + validateIndicesRequest.state, + validateDatasetsRequest.state, + validatedIndices, + selectedIndexNames, + isTimeRangeValid, + ]); + + const prevStartTime = usePrevious(startTime); + const prevEndTime = usePrevious(endTime); + const prevValidIndexNames = usePrevious(validIndexNames); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + validateIndices(); + }, [isTimeRangeValid, validateIndices]); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + if ( + startTime !== prevStartTime || + endTime !== prevEndTime || + !isEqual(validIndexNames, prevValidIndexNames) + ) { + validateDatasets(); + } + }, [ + endTime, + isTimeRangeValid, + prevEndTime, + prevStartTime, + prevValidIndexNames, + startTime, + validIndexNames, + validateDatasets, + ]); + + return { + cleanUpAndSetUp, + datasetFilter, + endTime, + isValidating, + selectedIndexNames, + setEndTime, + setStartTime, + setUp, + startTime, + validatedIndices, + setValidatedIndices, + validationErrors, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx new file mode 100644 index 0000000000000..9c065f3e91bc4 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 createContainer from 'constate'; +import { useMemo } from 'react'; +import { useInfraMLModule } from '../../infra_ml_module'; +import { useInfraMLModuleConfiguration } from '../../infra_ml_module_configuration'; +import { useInfraMLModuleDefinition } from '../../infra_ml_module_definition'; +import { ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { metricHostsModule } from './module_descriptor'; + +export const useMetricHostsModule = ({ + indexPattern, + sourceId, + spaceId, + timestampField, +}: { + indexPattern: string; + sourceId: string; + spaceId: string; + timestampField: string; +}) => { + const sourceConfiguration: ModuleSourceConfiguration = useMemo( + () => ({ + indices: indexPattern.split(','), + sourceId, + spaceId, + timestampField, + }), + [indexPattern, sourceId, spaceId, timestampField] + ); + + const infraMLModule = useInfraMLModule({ + moduleDescriptor: metricHostsModule, + sourceConfiguration, + }); + + const { getIsJobConfigurationOutdated } = useInfraMLModuleConfiguration({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const { fetchModuleDefinition, getIsJobDefinitionOutdated } = useInfraMLModuleDefinition({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const hasOutdatedJobConfigurations = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, infraMLModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, infraMLModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(infraMLModule.jobStatus).some( + (currentJobStatus) => currentJobStatus === 'stopped' + ), + [infraMLModule.jobStatus] + ); + + return { + ...infraMLModule, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; +}; + +export const [MetricHostsModuleProvider, useMetricHostsModuleContext] = createContainer( + useMetricHostsModule +); diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts new file mode 100644 index 0000000000000..cec87fb1144e3 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; +import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; +import { callGetMlModuleAPI } from '../../api/ml_get_module'; +import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; +import { + metricsHostsJobTypes, + getJobId, + MetricsHostsJobType, + DatasetFilter, + bucketSpan, + partitionField, +} from '../../../../../common/infra_ml'; + +const moduleId = 'metrics_ui_hosts'; +const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { + defaultMessage: 'Metrics anomanly detection', +}); +const moduleDescription = i18n.translate('xpack.infra.ml.metricsHostModuleDescription', { + defaultMessage: 'Use Machine Learning to automatically detect anomalous log entry rates.', +}); + +const getJobIds = (spaceId: string, sourceId: string) => + metricsHostsJobTypes.reduce( + (accumulatedJobIds, jobType) => ({ + ...accumulatedJobIds, + [jobType]: getJobId(spaceId, sourceId, jobType), + }), + {} as Record + ); + +const getJobSummary = async (spaceId: string, sourceId: string) => { + const response = await callJobsSummaryAPI(spaceId, sourceId, metricsHostsJobTypes); + const jobIds = Object.values(getJobIds(spaceId, sourceId)); + + return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); +}; + +const getModuleDefinition = async () => { + return await callGetMlModuleAPI(moduleId); +}; + +const setUpModule = async ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + pField?: string +) => { + const indexNamePattern = indices.join(','); + const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + })); + + return callSetupMlModuleAPI( + moduleId, + start, + end, + spaceId, + sourceId, + indexNamePattern, + jobOverrides, + [] + ); +}; + +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); +}; + +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + +export const metricHostsModule: ModuleDescriptor = { + moduleId, + moduleName, + moduleDescription, + jobTypes: metricsHostsJobTypes, + bucketSpan, + getJobIds, + getJobSummary, + getModuleDefinition, + setUpModule, + cleanUpModule, + validateSetupDatasets, + validateSetupIndices, +}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx new file mode 100644 index 0000000000000..07c8ab02f17ee --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 createContainer from 'constate'; +import { useMemo } from 'react'; +import { useInfraMLModule } from '../../infra_ml_module'; +import { useInfraMLModuleConfiguration } from '../../infra_ml_module_configuration'; +import { useInfraMLModuleDefinition } from '../../infra_ml_module_definition'; +import { ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { metricHostsModule } from './module_descriptor'; + +export const useMetricK8sModule = ({ + indexPattern, + sourceId, + spaceId, + timestampField, +}: { + indexPattern: string; + sourceId: string; + spaceId: string; + timestampField: string; +}) => { + const sourceConfiguration: ModuleSourceConfiguration = useMemo( + () => ({ + indices: indexPattern.split(','), + sourceId, + spaceId, + timestampField, + }), + [indexPattern, sourceId, spaceId, timestampField] + ); + + const infraMLModule = useInfraMLModule({ + moduleDescriptor: metricHostsModule, + sourceConfiguration, + }); + + const { getIsJobConfigurationOutdated } = useInfraMLModuleConfiguration({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const { fetchModuleDefinition, getIsJobDefinitionOutdated } = useInfraMLModuleDefinition({ + sourceConfiguration, + moduleDescriptor: metricHostsModule, + }); + + const hasOutdatedJobConfigurations = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobConfigurationOutdated), + [getIsJobConfigurationOutdated, infraMLModule.jobSummaries] + ); + + const hasOutdatedJobDefinitions = useMemo( + () => infraMLModule.jobSummaries.some(getIsJobDefinitionOutdated), + [getIsJobDefinitionOutdated, infraMLModule.jobSummaries] + ); + + const hasStoppedJobs = useMemo( + () => + Object.values(infraMLModule.jobStatus).some( + (currentJobStatus) => currentJobStatus === 'stopped' + ), + [infraMLModule.jobStatus] + ); + + return { + ...infraMLModule, + fetchModuleDefinition, + hasOutdatedJobConfigurations, + hasOutdatedJobDefinitions, + hasStoppedJobs, + }; +}; + +export const [MetricK8sModuleProvider, useMetricK8sModuleContext] = createContainer( + useMetricK8sModule +); diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts new file mode 100644 index 0000000000000..cbcff1c307af6 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; +import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; +import { callGetMlModuleAPI } from '../../api/ml_get_module'; +import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; +import { + metricsK8SJobTypes, + getJobId, + MetricK8sJobType, + DatasetFilter, + bucketSpan, + partitionField, +} from '../../../../../common/infra_ml'; + +const moduleId = 'metrics_ui_k8s'; +const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { + defaultMessage: 'Metrics anomanly detection', +}); +const moduleDescription = i18n.translate('xpack.infra.ml.metricsHostModuleDescription', { + defaultMessage: 'Use Machine Learning to automatically detect anomalous log entry rates.', +}); + +const getJobIds = (spaceId: string, sourceId: string) => + metricsK8SJobTypes.reduce( + (accumulatedJobIds, jobType) => ({ + ...accumulatedJobIds, + [jobType]: getJobId(spaceId, sourceId, jobType), + }), + {} as Record + ); + +const getJobSummary = async (spaceId: string, sourceId: string) => { + const response = await callJobsSummaryAPI(spaceId, sourceId, metricsK8SJobTypes); + const jobIds = Object.values(getJobIds(spaceId, sourceId)); + + return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); +}; + +const getModuleDefinition = async () => { + return await callGetMlModuleAPI(moduleId); +}; + +const setUpModule = async ( + start: number | undefined, + end: number | undefined, + datasetFilter: DatasetFilter, + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + pField?: string +) => { + const indexNamePattern = indices.join(','); + const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + analysis_config: { + bucket_span: `${bucketSpan}ms`, + }, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + })); + + return callSetupMlModuleAPI( + moduleId, + start, + end, + spaceId, + sourceId, + indexNamePattern, + jobOverrides, + [] + ); +}; + +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); +}; + +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + +export const metricHostsModule: ModuleDescriptor = { + moduleId, + moduleName, + moduleDescription, + jobTypes: metricsK8SJobTypes, + bucketSpan, + getJobIds, + getJobSummary, + getModuleDefinition, + setUpModule, + cleanUpModule, + validateSetupDatasets, + validateSetupIndices, +}; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx index 0556955e47f66..e1b294c8383e3 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx @@ -19,7 +19,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -33,7 +33,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index a32e1e68141b5..0541b811508ee 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -5,6 +5,8 @@ */ import { i18n } from '@kbn/i18n'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import flowRight from 'lodash/flowRight'; import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx index 2880b1b794443..b5765942e9f10 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_content.tsx @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { isJobStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { @@ -14,6 +14,10 @@ import { MissingSetupPrivilegesPrompt, SubscriptionSplashContent, } from '../../../components/logging/log_analysis_setup'; +import { + LogAnalysisSetupFlyout, + useLogAnalysisSetupFlyoutStateContext, +} from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceErrorPage } from '../../../components/source_error_page'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useLogAnalysisCapabilitiesContext } from '../../../containers/logs/log_analysis'; @@ -21,7 +25,6 @@ import { useLogEntryCategoriesModuleContext } from '../../../containers/logs/log import { useLogSourceContext } from '../../../containers/logs/log_source'; import { LogEntryCategoriesResultsContent } from './page_results_content'; import { LogEntryCategoriesSetupContent } from './page_setup_content'; -import { LogEntryCategoriesSetupFlyout } from './setup_flyout'; export const LogEntryCategoriesPageContent = () => { const { @@ -40,9 +43,10 @@ export const LogEntryCategoriesPageContent = () => { const { fetchJobStatus, setupStatus, jobStatus } = useLogEntryCategoriesModuleContext(); - const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); - const openFlyout = useCallback(() => setIsFlyoutOpen(true), []); - const closeFlyout = useCallback(() => setIsFlyoutOpen(false), []); + const { showModuleSetup } = useLogAnalysisSetupFlyoutStateContext(); + const showCategoriesModuleSetup = useCallback(() => showModuleSetup('logs_ui_categories'), [ + showModuleSetup, + ]); useEffect(() => { if (hasLogAnalysisReadCapabilities) { @@ -71,8 +75,8 @@ export const LogEntryCategoriesPageContent = () => { } else if (isJobStatusWithResults(jobStatus['log-entry-categories-count'])) { return ( <> - - + + ); } else if (!hasLogAnalysisSetupCapabilities) { @@ -80,9 +84,11 @@ export const LogEntryCategoriesPageContent = () => { } else { return ( <> - - + + ); } }; + +const allowedSetupModules = ['logs_ui_categories' as const]; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx index 723d833799e29..7d2f1d5418bc5 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx @@ -5,6 +5,7 @@ */ import React from 'react'; +import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; import { useLogSourceContext } from '../../../containers/logs/log_source'; import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; @@ -27,7 +28,7 @@ export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ child spaceId={space.id} timestampField={sourceConfiguration.configuration.fields.timestamp} > - {children} + {children} ); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/setup_flyout.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/setup_flyout.tsx deleted file mode 100644 index a038765de2bf3..0000000000000 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/setup_flyout.tsx +++ /dev/null @@ -1,128 +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 { - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiSpacer, - EuiSteps, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useCallback, useMemo } from 'react'; -import { - createInitialConfigurationStep, - createProcessStep, -} from '../../../components/logging/log_analysis_setup'; -import { useLogEntryCategoriesSetup } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; - -interface LogEntryCategoriesSetupFlyoutProps { - isOpen: boolean; - onClose: () => void; -} - -export const LogEntryCategoriesSetupFlyout: React.FC = ({ - isOpen, - onClose, -}) => { - const { - cleanUpAndSetUp, - endTime, - isValidating, - lastSetupErrorMessages, - setEndTime, - setStartTime, - setValidatedIndices, - setUp, - setupStatus, - startTime, - validatedIndices, - validationErrors, - viewResults, - } = useLogEntryCategoriesSetup(); - - const viewResultsAndClose = useCallback(() => { - viewResults(); - onClose(); - }, [viewResults, onClose]); - - const steps = useMemo( - () => [ - createInitialConfigurationStep({ - setStartTime, - setEndTime, - startTime, - endTime, - isValidating, - validatedIndices, - setupStatus, - setValidatedIndices, - validationErrors, - }), - createProcessStep({ - cleanUpAndSetUp, - errorMessages: lastSetupErrorMessages, - isConfigurationValid: validationErrors.length <= 0 && !isValidating, - setUp, - setupStatus, - viewResults: viewResultsAndClose, - }), - ], - [ - cleanUpAndSetUp, - endTime, - isValidating, - lastSetupErrorMessages, - setEndTime, - setStartTime, - setUp, - setValidatedIndices, - setupStatus, - startTime, - validatedIndices, - validationErrors, - viewResultsAndClose, - ] - ); - - if (!isOpen) { - return null; - } - return ( - - - -

- -

-
-
- - -

- -

-
- - - - - -
-
- ); -}; diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index 426ae8e9d05a8..973037af499e5 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -23,7 +23,7 @@ import { LogEntryCategoriesPage } from './log_entry_categories'; import { LogEntryRatePage } from './log_entry_rate'; import { LogsSettingsPage } from './settings'; import { StreamPage } from './stream'; -import { AlertDropdown } from '../../components/alerting/logs/alert_dropdown'; +import { AlertDropdown } from '../../components/alerting/logs/log_threshold/alert_dropdown'; export const LogsPageContent: React.FunctionComponent = () => { const uiCapabilities = useKibana().services.application?.capabilities; diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 3b3ed80f9e731..ac2c87248ae77 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -38,6 +38,8 @@ import { MetricsAlertDropdown } from '../../alerting/metric_threshold/components import { SavedView } from '../../containers/saved_view/saved_view'; import { SourceConfigurationFields } from '../../graphql/types'; import { AlertPrefillProvider } from '../../alerting/use_alert_prefill'; +import { InfraMLCapabilitiesProvider } from '../../containers/ml/infra_ml_capabilities'; +import { AnomalyDetectionFlyout } from './inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout'; const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLabel', { defaultMessage: 'Add data', @@ -55,110 +57,118 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { - - + + + - + -
+ - - - - - - - - - - - - {ADD_DATA_LABEL} - - - - - - - - ( - - {({ configuration, createDerivedIndexPattern }) => ( - - - {configuration ? ( - - ) : ( - - )} - - )} - + } )} - /> - - - + > + + + + + + + + + + + + + + {ADD_DATA_LABEL} + + + + + + + + ( + + {({ configuration, createDerivedIndexPattern }) => ( + + + {configuration ? ( + + ) : ( + + )} + + )} + + )} + /> + + + + diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx new file mode 100644 index 0000000000000..9cb84c7fff438 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/bottom_drawer.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui'; + +import { euiStyled, useUiTracker } from '../../../../../../observability/public'; +import { InfraFormatter } from '../../../../lib/lib'; +import { Timeline } from './timeline/timeline'; + +const showHistory = i18n.translate('xpack.infra.showHistory', { + defaultMessage: 'Show history', +}); +const hideHistory = i18n.translate('xpack.infra.hideHistory', { + defaultMessage: 'Hide history', +}); + +const TRANSITION_MS = 300; + +export const BottomDrawer: React.FC<{ + measureRef: (instance: HTMLElement | null) => void; + interval: string; + formatter: InfraFormatter; +}> = ({ measureRef, interval, formatter, children }) => { + const [isOpen, setIsOpen] = useState(false); + + const trackDrawerOpen = useUiTracker({ app: 'infra_metrics' }); + const onClick = useCallback(() => { + if (!isOpen) trackDrawerOpen({ metric: 'open_timeline_drawer__inventory' }); + setIsOpen(!isOpen); + }, [isOpen, trackDrawerOpen]); + + return ( + + + + + {isOpen ? hideHistory : showHistory} + + + + {children} + + + + + + + + + + ); +}; + +const BottomActionContainer = euiStyled.div<{ isOpen: boolean }>` + padding: ${(props) => props.theme.eui.paddingSizes.m} 0; + position: fixed; + left: 0; + bottom: 0; + right: 0; + transition: transform ${TRANSITION_MS}ms; + transform: translateY(${(props) => (props.isOpen ? 0 : '224px')}) +`; + +const BottomActionTopBar = euiStyled(EuiFlexGroup).attrs({ + justifyContent: 'spaceBetween', + alignItems: 'center', +})` + margin-bottom: 0; + height: 48px; +`; + +const ShowHideButton = euiStyled(EuiButtonEmpty).attrs({ size: 's' })` + width: 140px; +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 47616c7f4f7fd..712578be7dffd 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useEffect } from 'react'; import { useInterval } from 'react-use'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { AutoSizer } from '../../../../components/auto_sizer'; import { convertIntervalToString } from '../../../../utils/convert_interval_to_string'; import { NodesOverview } from './nodes_overview'; @@ -23,12 +23,13 @@ import { euiStyled } from '../../../../../../observability/public'; import { Toolbar } from './toolbars/toolbar'; import { ViewSwitcher } from './waffle/view_switcher'; import { IntervalLabel } from './waffle/interval_label'; -import { Legend } from './waffle/legend'; import { createInventoryMetricFormatter } from '../lib/create_inventory_metric_formatter'; import { createLegend } from '../lib/create_legend'; import { useSavedViewContext } from '../../../../containers/saved_view/saved_view'; import { useWaffleViewState } from '../hooks/use_waffle_view_state'; import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control'; +import { BottomDrawer } from './bottom_drawer'; +import { Legend } from './waffle/legend'; export const Layout = () => { const { sourceId, source } = useSourceContext(); @@ -104,12 +105,19 @@ export const Layout = () => { - + + + + + + + + {({ measureRef, bounds: { height = 0 } }) => ( @@ -128,24 +136,14 @@ export const Layout = () => { formatter={formatter} bottomMargin={height} /> - - - - - - - - - - - - - + + + )} @@ -164,12 +162,8 @@ const TopActionContainer = euiStyled.div` padding: ${(props) => `12px ${props.theme.eui.paddingSizes.m}`}; `; -const BottomActionContainer = euiStyled.div` - background-color: ${(props) => props.theme.eui.euiPageBackgroundColor}; - padding: ${(props) => props.theme.eui.paddingSizes.m} ${(props) => - props.theme.eui.paddingSizes.m}; - position: fixed; - left: 0; - bottom: 0; - right: 0; +const SavedViewContainer = euiStyled.div` + position: relative; + z-index: 1; + padding-left: ${(props) => props.theme.eui.paddingSizes.m}; `; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx new file mode 100644 index 0000000000000..b063713fa2c97 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, useCallback } from 'react'; +import { EuiButtonEmpty, EuiFlyout } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { FlyoutHome } from './flyout_home'; +import { JobSetupScreen } from './job_setup_screen'; +import { useInfraMLCapabilities } from '../../../../../../containers/ml/infra_ml_capabilities'; +import { MetricHostsModuleProvider } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { MetricK8sModuleProvider } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http'; +import { useActiveKibanaSpace } from '../../../../../../hooks/use_kibana_space'; + +export const AnomalyDetectionFlyout = () => { + const { hasInfraMLSetupCapabilities } = useInfraMLCapabilities(); + const [showFlyout, setShowFlyout] = useState(false); + const [screenName, setScreenName] = useState<'home' | 'setup'>('home'); + const [screenParams, setScreenParams] = useState(null); + const { source } = useSourceViaHttp({ + sourceId: 'default', + type: 'metrics', + }); + + const { space } = useActiveKibanaSpace(); + + const openFlyout = useCallback(() => { + setScreenName('home'); + setShowFlyout(true); + }, []); + + const openJobSetup = useCallback( + (jobType: 'hosts' | 'kubernetes') => { + setScreenName('setup'); + setScreenParams({ jobType }); + }, + [setScreenName] + ); + + const closeFlyout = useCallback(() => { + setShowFlyout(false); + }, []); + + if (source?.configuration.metricAlias == null || space == null) { + return null; + } + + return ( + <> + + + + {showFlyout && ( + + + + {screenName === 'home' && ( + + )} + {screenName === 'setup' && ( + + )} + + + + )} + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx new file mode 100644 index 0000000000000..801dff9c4a17a --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx @@ -0,0 +1,333 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, useCallback, useEffect } from 'react'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; +import moment from 'moment'; +import { useInfraMLCapabilitiesContext } from '../../../../../../containers/ml/infra_ml_capabilities'; +import { SubscriptionSplashContent } from './subscription_splash_content'; +import { + MissingResultsPrivilegesPrompt, + MissingSetupPrivilegesPrompt, +} from '../../../../../../components/logging/log_analysis_setup'; +import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { LoadingPage } from '../../../../../../components/loading_page'; +import { useLinkProps } from '../../../../../../hooks/use_link_props'; + +interface Props { + hasSetupCapabilities: boolean; + goToSetup(type: 'hosts' | 'kubernetes'): void; +} + +export const FlyoutHome = (props: Props) => { + const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs'); + const { goToSetup } = props; + const { + fetchJobStatus: fetchHostJobStatus, + setupStatus: hostSetupStatus, + jobSummaries: hostJobSummaries, + } = useMetricHostsModuleContext(); + const { + fetchJobStatus: fetchK8sJobStatus, + setupStatus: k8sSetupStatus, + jobSummaries: k8sJobSummaries, + } = useMetricK8sModuleContext(); + const { + hasInfraMLCapabilites, + hasInfraMLReadCapabilities, + hasInfraMLSetupCapabilities, + } = useInfraMLCapabilitiesContext(); + + const createHosts = useCallback(() => { + goToSetup('hosts'); + }, [goToSetup]); + + const createK8s = useCallback(() => { + goToSetup('kubernetes'); + }, [goToSetup]); + + const goToJobs = useCallback(() => { + setTab('jobs'); + }, []); + + const jobIds = [ + ...(k8sJobSummaries || []).map((k) => k.id), + ...(hostJobSummaries || []).map((h) => h.id), + ]; + const anomaliesUrl = useLinkProps({ + app: 'ml', + pathname: `/explorer?_g=${createResultsUrl(jobIds)}`, + }); + + useEffect(() => { + if (hasInfraMLReadCapabilities) { + fetchHostJobStatus(); + fetchK8sJobStatus(); + } + }, [fetchK8sJobStatus, fetchHostJobStatus, hasInfraMLReadCapabilities]); + + if (!hasInfraMLCapabilites) { + return ; + } else if (!hasInfraMLReadCapabilities) { + return ; + } else if (hostSetupStatus.type === 'initializing' || k8sSetupStatus.type === 'initializing') { + return ( + + ); + } else if (!hasInfraMLSetupCapabilities) { + return ; + } else { + return ( + <> + + +

+ +

+
+
+ + + + + + + + + + + + {hostJobSummaries.length > 0 && ( + <> + 0} + hasK8sJobs={k8sJobSummaries.length > 0} + /> + + + )} + {tab === 'jobs' && ( + 0} + hasK8sJobs={k8sJobSummaries.length > 0} + hasSetupCapabilities={props.hasSetupCapabilities} + createHosts={createHosts} + createK8s={createK8s} + /> + )} + + + ); + } +}; + +interface CalloutProps { + hasHostJobs: boolean; + hasK8sJobs: boolean; +} +const JobsEnabledCallout = (props: CalloutProps) => { + let target = ''; + if (props.hasHostJobs && props.hasK8sJobs) { + target = `${i18n.translate('xpack.infra.ml.anomalyFlyout.create.hostTitle', { + defaultMessage: 'Hosts', + })} and ${i18n.translate('xpack.infra.ml.anomalyFlyout.create.k8sSuccessTitle', { + defaultMessage: 'Kubernetes', + })}`; + } else if (props.hasHostJobs) { + target = i18n.translate('xpack.infra.ml.anomalyFlyout.create.hostSuccessTitle', { + defaultMessage: 'Hosts', + }); + } else if (props.hasK8sJobs) { + target = i18n.translate('xpack.infra.ml.anomalyFlyout.create.k8sSuccessTitle', { + defaultMessage: 'Kubernetes', + }); + } + + const manageJobsLinkProps = useLinkProps({ + app: 'ml', + pathname: '/jobs', + }); + + return ( + <> + + } + iconType="check" + /> + + + + + + ); +}; + +interface CreateJobTab { + hasSetupCapabilities: boolean; + hasHostJobs: boolean; + hasK8sJobs: boolean; + createHosts(): void; + createK8s(): void; +} + +const CreateJobTab = (props: CreateJobTab) => { + return ( + <> +
+ +

+ +

+
+ +

+ +

+
+
+ + + + + } + // title="Hosts" + title={ + + } + description={ + + } + footer={ + <> + {props.hasHostJobs && ( + + + + )} + {!props.hasHostJobs && ( + + + + )} + + } + /> + + + } + title={ + + } + description={ + + } + footer={ + <> + {props.hasK8sJobs && ( + + + + )} + {!props.hasK8sJobs && ( + + + + )} + + } + /> + + + + ); +}; + +function createResultsUrl(jobIds: string[], mode = 'absolute') { + const idString = jobIds.map((j) => `'${j}'`).join(','); + let path = ''; + + const from = moment().subtract(4, 'weeks').toISOString(); + const to = moment().toISOString(); + + path += `(ml:(jobIds:!(${idString}))`; + path += `,refreshInterval:(display:Off,pause:!f,value:0),time:(from:'${from}'`; + path += `,to:'${to}'`; + if (mode === 'invalid') { + path += `,mode:invalid`; + } + path += "))&_a=(query:(query_string:(analyze_wildcard:!t,query:'*')))"; + + return path; +} diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx new file mode 100644 index 0000000000000..428c002da6383 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -0,0 +1,277 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, useCallback, useMemo, useEffect } from 'react'; +import { EuiForm, EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; +import { EuiText, EuiSpacer } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlyoutFooter } from '@elastic/eui'; +import { EuiButton } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; +import moment, { Moment } from 'moment'; +import { EuiComboBox } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { useSourceViaHttp } from '../../../../../../containers/source/use_source_via_http'; +import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; +import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; +import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; + +interface Props { + jobType: 'hosts' | 'kubernetes'; + closeFlyout(): void; + goHome(): void; +} + +export const JobSetupScreen = (props: Props) => { + const [now] = useState(() => moment()); + const { goHome } = props; + const [startDate, setStartDate] = useState(now.clone().subtract(4, 'weeks')); + const [partitionField, setPartitionField] = useState(null); + const h = useMetricHostsModuleContext(); + const k = useMetricK8sModuleContext(); + const { createDerivedIndexPattern } = useSourceViaHttp({ + sourceId: 'default', + type: 'metrics', + }); + + const indicies = h.sourceConfiguration.indices; + + const setupStatus = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.setupStatus; + } else { + return h.setupStatus; + } + }, [props.jobType, k.setupStatus, h.setupStatus]); + + const cleanUpAndSetUpModule = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.cleanUpAndSetUpModule; + } else { + return h.cleanUpAndSetUpModule; + } + }, [props.jobType, k.cleanUpAndSetUpModule, h.cleanUpAndSetUpModule]); + + const setUpModule = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.setUpModule; + } else { + return h.setUpModule; + } + }, [props.jobType, k.setUpModule, h.setUpModule]); + + const hasSummaries = useMemo(() => { + if (props.jobType === 'kubernetes') { + return k.jobSummaries.length > 0; + } else { + return h.jobSummaries.length > 0; + } + }, [props.jobType, k.jobSummaries, h.jobSummaries]); + + const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [ + createDerivedIndexPattern, + ]); + + const updateStart = useCallback((date: Moment) => { + setStartDate(date); + }, []); + + const createJobs = useCallback(() => { + if (hasSummaries) { + cleanUpAndSetUpModule( + indicies, + moment(startDate).toDate().getTime(), + undefined, + { type: 'includeAll' }, + partitionField ? partitionField[0] : undefined + ); + } else { + setUpModule( + indicies, + moment(startDate).toDate().getTime(), + undefined, + { type: 'includeAll' }, + partitionField ? partitionField[0] : undefined + ); + } + }, [cleanUpAndSetUpModule, setUpModule, hasSummaries, indicies, partitionField, startDate]); + + const onPartitionFieldChange = useCallback((value: Array<{ label: string }>) => { + setPartitionField(value.map((v) => v.label)); + }, []); + + useEffect(() => { + if (props.jobType === 'kubernetes') { + setPartitionField(['kubernetes.namespace']); + } + }, [props.jobType]); + + useEffect(() => { + if (setupStatus.type === 'succeeded') { + goHome(); + } + }, [setupStatus, goHome]); + + return ( + <> + + +

+ +

+
+
+ + {setupStatus.type === 'pending' ? ( + + + + + + + + + ) : setupStatus.type === 'failed' ? ( + <> + + + + + + + ) : ( + <> + +

+ +

+
+ + + + + + + } + description={ + + } + > + + } + > + + + + + + + + } + description={ + + } + > + + } + compressed + > + ({ label: p })) : undefined + } + options={derivedIndexPattern.fields + .filter((f) => f.aggregatable && f.type === 'string') + .map((f) => ({ label: f.name }))} + onChange={onPartitionFieldChange} + isClearable={true} + /> + + + + + )} +
+ + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.tsx new file mode 100644 index 0000000000000..f07c37f5e7ea2 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/subscription_splash_content.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, { useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + EuiText, + EuiButton, + EuiButtonEmpty, + EuiImage, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { LoadingPage } from '../../../../../../components/loading_page'; +import { useTrialStatus } from '../../../../../../hooks/use_trial_status'; +import { useKibana } from '../../../../../../../../../../src/plugins/kibana_react/public'; +import { euiStyled } from '../../../../../../../../observability/public'; +import { HttpStart } from '../../../../../../../../../../src/core/public'; + +export const SubscriptionSplashContent: React.FC = () => { + const { services } = useKibana<{ http: HttpStart }>(); + const { loadState, isTrialAvailable, checkTrialAvailability } = useTrialStatus(); + + useEffect(() => { + checkTrialAvailability(); + }, [checkTrialAvailability]); + + if (loadState === 'pending') { + return ( + + ); + } + + const canStartTrial = isTrialAvailable && loadState === 'resolved'; + + let title; + let description; + let cta; + + if (canStartTrial) { + title = ( + + ); + + description = ( + + ); + + cta = ( + + + + ); + } else { + title = ( + + ); + + description = ( + + ); + + cta = ( + + + + ); + } + + return ( + + + + + + +

{title}

+
+ + +

{description}

+
+ +
{cta}
+
+ + + +
+ + +

+ +

+
+ + + +
+
+
+
+ ); +}; + +const SubscriptionPage = euiStyled(EuiPage)` + height: 100% +`; + +const SubscriptionPageContent = euiStyled(EuiPageContent)` + max-width: 768px !important; +`; + +const SubscriptionPageFooter = euiStyled.div` + background: ${(props) => props.theme.eui.euiColorLightestShade}; + margin: 0 -${(props) => props.theme.eui.paddingSizes.l} -${(props) => + props.theme.eui.paddingSizes.l}; + padding: ${(props) => props.theme.eui.paddingSizes.l}; +`; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx new file mode 100644 index 0000000000000..a3b02b858385e --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx @@ -0,0 +1,354 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, { useMemo, useCallback, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import moment from 'moment'; +import { first, last } from 'lodash'; +import { EuiLoadingChart, EuiText, EuiEmptyPrompt, EuiButton } from '@elastic/eui'; +import { + Axis, + Chart, + Settings, + Position, + TooltipValue, + niceTimeFormatter, + ElementClickListener, + RectAnnotation, + RectAnnotationDatum, +} from '@elastic/charts'; +import { EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; +import { EuiIcon } from '@elastic/eui'; +import { useUiSetting } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { toMetricOpt } from '../../../../../../common/snapshot_metric_i18n'; +import { MetricsExplorerAggregation } from '../../../../../../common/http_api'; +import { Color } from '../../../../../../common/color_palette'; +import { useSourceContext } from '../../../../../containers/source'; +import { useTimeline } from '../../hooks/use_timeline'; +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; +import { useWaffleTimeContext } from '../../hooks/use_waffle_time'; +import { useWaffleFiltersContext } from '../../hooks/use_waffle_filters'; +import { MetricExplorerSeriesChart } from '../../../metrics_explorer/components/series_chart'; +import { MetricsExplorerChartType } from '../../../metrics_explorer/hooks/use_metrics_explorer_options'; +import { getTimelineChartTheme } from '../../../metrics_explorer/components/helpers/get_chart_theme'; +import { calculateDomain } from '../../../metrics_explorer/components/helpers/calculate_domain'; + +import { euiStyled } from '../../../../../../../observability/public'; +import { InfraFormatter } from '../../../../../lib/lib'; +import { useMetricsHostsAnomaliesResults } from '../../hooks/use_metrics_hosts_anomalies'; +import { useMetricsK8sAnomaliesResults } from '../../hooks/use_metrics_k8s_anomalies'; + +interface Props { + interval: string; + yAxisFormatter: InfraFormatter; + isVisible: boolean; +} + +export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible }) => { + const { sourceId } = useSourceContext(); + const { metric, nodeType, accountId, region } = useWaffleOptionsContext(); + const { currentTime, jumpToTime, stopAutoReload } = useWaffleTimeContext(); + const { filterQueryAsJson } = useWaffleFiltersContext(); + + const { loading, error, startTime, endTime, timeseries, reload } = useTimeline( + filterQueryAsJson, + [metric], + nodeType, + sourceId, + currentTime, + accountId, + region, + interval, + isVisible + ); + + const anomalyParams = { + sourceId: 'default', + startTime, + endTime, + defaultSortOptions: { + direction: 'desc' as const, + field: 'anomalyScore' as const, + }, + defaultPaginationOptions: { pageSize: 100 }, + }; + + const { metricsHostsAnomalies, getMetricsHostsAnomalies } = useMetricsHostsAnomaliesResults( + anomalyParams + ); + const { metricsK8sAnomalies, getMetricsK8sAnomalies } = useMetricsK8sAnomaliesResults( + anomalyParams + ); + + const getAnomalies = useMemo(() => { + if (nodeType === 'host') { + return getMetricsHostsAnomalies; + } else if (nodeType === 'pod') { + return getMetricsK8sAnomalies; + } + }, [nodeType, getMetricsK8sAnomalies, getMetricsHostsAnomalies]); + + const anomalies = useMemo(() => { + if (nodeType === 'host') { + return metricsHostsAnomalies; + } else if (nodeType === 'pod') { + return metricsK8sAnomalies; + } + }, [nodeType, metricsHostsAnomalies, metricsK8sAnomalies]); + + const metricLabel = toMetricOpt(metric.type)?.textLC; + + const chartMetric = { + color: Color.color0, + aggregation: 'avg' as MetricsExplorerAggregation, + label: metricLabel, + }; + + const dateFormatter = useMemo(() => { + if (!timeseries) return () => ''; + const firstTimestamp = first(timeseries.rows)?.timestamp; + const lastTimestamp = last(timeseries.rows)?.timestamp; + + if (firstTimestamp == null || lastTimestamp == null) { + return (value: number) => `${value}`; + } + + return niceTimeFormatter([firstTimestamp, lastTimestamp]); + }, [timeseries]); + + const isDarkMode = useUiSetting('theme:darkMode'); + const tooltipProps = { + headerFormatter: (tooltipValue: TooltipValue) => + moment(tooltipValue.value).format('Y-MM-DD HH:mm:ss.SSS'), + }; + + const dataDomain = timeseries ? calculateDomain(timeseries, [chartMetric], false) : null; + const domain = dataDomain + ? { + max: dataDomain.max * 1.1, // add 10% headroom. + min: dataDomain.min, + } + : { max: 0, min: 0 }; + + const onClickPoint: ElementClickListener = useCallback( + ([[geometryValue]]) => { + if (!Array.isArray(geometryValue)) { + const { x: timestamp } = geometryValue; + jumpToTime(timestamp); + stopAutoReload(); + } + }, + [jumpToTime, stopAutoReload] + ); + + const anomalyMetricName = useMemo(() => { + const metricType = metric.type; + if (metricType === 'memory') { + return 'memory_usage'; + } + if (metricType === 'rx') { + return 'network_in'; + } + if (metricType === 'tx') { + return 'network_out'; + } + }, [metric]); + + useEffect(() => { + if (getAnomalies && anomalyMetricName) { + getAnomalies(anomalyMetricName); + } + }, [getAnomalies, anomalyMetricName]); + + if (loading) { + return ( + + + + + + ); + } + + if (!loading && (error || !timeseries)) { + return ( + + {error ? errorTitle : noHistoryDataTitle}} + actions={ + + {error ? retryButtonLabel : checkNewDataButtonLabel} + + } + /> + + ); + } + + function generateAnnotationData(results: Array<[number, string[]]>): RectAnnotationDatum[] { + return results.map((anomaly) => { + const [val, influencers] = anomaly; + return { + coordinates: { + x0: val, + x1: moment(val).add(15, 'minutes').valueOf(), + y0: dataDomain?.min, + y1: dataDomain?.max, + }, + details: influencers.join(','), + }; + }); + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {anomalies && ( + [a.startTime, a.influencers]) + )} + style={{ fill: '#D36086' }} + /> + )} + + + + + + + + ); +}; + +const TimelineContainer = euiStyled.div` + background-color: ${(props) => props.theme.eui.euiPageBackgroundColor}; + border-top: 1px solid ${(props) => props.theme.eui.euiColorLightShade}; + height: 220px; + width: 100%; + padding: ${(props) => props.theme.eui.paddingSizes.s} ${(props) => + props.theme.eui.paddingSizes.m}; + display: flex; + flex-direction: column; +`; + +const TimelineHeader = euiStyled.div` + display: flex; + width: 100%; + padding: ${(props) => props.theme.eui.paddingSizes.s} ${(props) => + props.theme.eui.paddingSizes.m}; +`; + +const TimelineChartContainer = euiStyled.div` + padding-left: ${(props) => props.theme.eui.paddingSizes.xs}; + width: 100%; + height: 100%; +`; + +const TimelineLoadingContainer = euiStyled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; +`; + +const noHistoryDataTitle = i18n.translate('xpack.infra.inventoryTimeline.noHistoryDataTitle', { + defaultMessage: 'There is no history data to display.', +}); + +const errorTitle = i18n.translate('xpack.infra.inventoryTimeline.errorTitle', { + defaultMessage: 'Unable to display history data.', +}); + +const checkNewDataButtonLabel = i18n.translate( + 'xpack.infra.inventoryTimeline.checkNewDataButtonLabel', + { + defaultMessage: 'Check for new data', + } +); + +const retryButtonLabel = i18n.translate('xpack.infra.inventoryTimeline.retryButtonLabel', { + defaultMessage: 'Try again', +}); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx index a785cb31c3cf4..262d94d8f3674 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx @@ -27,10 +27,7 @@ import { SNAPSHOT_CUSTOM_AGGREGATIONS, SnapshotCustomAggregationRT, } from '../../../../../../../common/http_api/snapshot_api'; -import { - EuiTheme, - withTheme, -} from '../../../../../../../../../legacy/common/eui_styled_components'; +import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; interface SelectedOption { label: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx index e75885ccbc917..831a0cde49cfb 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx @@ -8,10 +8,7 @@ import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getCustomMetricLabel } from '../../../../../../../common/formatters/get_custom_metric_label'; import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; -import { - EuiTheme, - withTheme, -} from '../../../../../../../../../legacy/common/eui_styled_components'; +import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; interface Props { theme: EuiTheme | undefined; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx index d1abcade5d660..956241545e8be 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx @@ -9,10 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { CustomMetricMode } from './types'; import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; -import { - EuiTheme, - withTheme, -} from '../../../../../../../../../legacy/common/eui_styled_components'; +import { EuiTheme, withTheme } from '../../../../../../../../xpack_legacy/common'; interface Props { theme: EuiTheme | undefined; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts new file mode 100644 index 0000000000000..f33e3ea16b389 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts @@ -0,0 +1,318 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { + INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + Metric, + Sort, + Pagination, + PaginationCursor, + getMetricsHostsAnomaliesRequestPayloadRT, + MetricsHostsAnomaly, + getMetricsHostsAnomaliesSuccessReponsePayloadRT, +} from '../../../../../common/http_api/infra_ml'; +import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; +import { npStart } from '../../../../legacy_singletons'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +export type SortOptions = Sort; +export type PaginationOptions = Pick; +export type Page = number; +export type FetchNextPage = () => void; +export type FetchPreviousPage = () => void; +export type ChangeSortOptions = (sortOptions: Sort) => void; +export type ChangePaginationOptions = (paginationOptions: PaginationOptions) => void; +export type MetricsHostsAnomalies = MetricsHostsAnomaly[]; +interface PaginationCursors { + previousPageCursor: PaginationCursor; + nextPageCursor: PaginationCursor; +} + +interface ReducerState { + page: number; + lastReceivedCursors: PaginationCursors | undefined; + paginationCursor: Pagination['cursor'] | undefined; + hasNextPage: boolean; + paginationOptions: PaginationOptions; + sortOptions: Sort; + timeRange: { + start: number; + end: number; + }; + filteredDatasets?: string[]; +} + +type ReducerStateDefaults = Pick< + ReducerState, + 'page' | 'lastReceivedCursors' | 'paginationCursor' | 'hasNextPage' +>; + +type ReducerAction = + | { type: 'changePaginationOptions'; payload: { paginationOptions: PaginationOptions } } + | { type: 'changeSortOptions'; payload: { sortOptions: Sort } } + | { type: 'fetchNextPage' } + | { type: 'fetchPreviousPage' } + | { type: 'changeHasNextPage'; payload: { hasNextPage: boolean } } + | { type: 'changeLastReceivedCursors'; payload: { lastReceivedCursors: PaginationCursors } } + | { type: 'changeTimeRange'; payload: { timeRange: { start: number; end: number } } } + | { type: 'changeFilteredDatasets'; payload: { filteredDatasets?: string[] } }; + +const stateReducer = (state: ReducerState, action: ReducerAction): ReducerState => { + const resetPagination = { + page: 1, + paginationCursor: undefined, + }; + switch (action.type) { + case 'changePaginationOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeSortOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeHasNextPage': + return { + ...state, + ...action.payload, + }; + case 'changeLastReceivedCursors': + return { + ...state, + ...action.payload, + }; + case 'fetchNextPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page + 1, + paginationCursor: { searchAfter: state.lastReceivedCursors.nextPageCursor }, + } + : state; + case 'fetchPreviousPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page - 1, + paginationCursor: { searchBefore: state.lastReceivedCursors.previousPageCursor }, + } + : state; + case 'changeTimeRange': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeFilteredDatasets': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + default: + return state; + } +}; + +const STATE_DEFAULTS: ReducerStateDefaults = { + // NOTE: This piece of state is purely for the client side, it could be extracted out of the hook. + page: 1, + // Cursor from the last request + lastReceivedCursors: undefined, + // Cursor to use for the next request. For the first request, and therefore not paging, this will be undefined. + paginationCursor: undefined, + hasNextPage: false, +}; + +export const useMetricsHostsAnomaliesResults = ({ + endTime, + startTime, + sourceId, + defaultSortOptions, + defaultPaginationOptions, + onGetMetricsHostsAnomaliesDatasetsError, + filteredDatasets, +}: { + endTime: number; + startTime: number; + sourceId: string; + defaultSortOptions: Sort; + defaultPaginationOptions: Pick; + onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; + filteredDatasets?: string[]; +}) => { + const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { + return { + ...stateDefaults, + paginationOptions: defaultPaginationOptions, + sortOptions: defaultSortOptions, + filteredDatasets, + timeRange: { + start: startTime, + end: endTime, + }, + }; + }; + + const [reducerState, dispatch] = useReducer(stateReducer, STATE_DEFAULTS, initStateReducer); + + const [metricsHostsAnomalies, setMetricsHostsAnomalies] = useState([]); + + const [getMetricsHostsAnomaliesRequest, getMetricsHostsAnomalies] = useTrackedPromise( + { + cancelPreviousOn: 'creation', + createPromise: async (metric: Metric) => { + const { + timeRange: { start: queryStartTime, end: queryEndTime }, + sortOptions, + paginationOptions, + paginationCursor, + } = reducerState; + return await callGetMetricHostsAnomaliesAPI( + sourceId, + queryStartTime, + queryEndTime, + metric, + sortOptions, + { + ...paginationOptions, + cursor: paginationCursor, + } + ); + }, + onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { + const { paginationCursor } = reducerState; + if (requestCursors) { + dispatch({ + type: 'changeLastReceivedCursors', + payload: { lastReceivedCursors: requestCursors }, + }); + } + // Check if we have more "next" entries. "Page" covers the "previous" scenario, + // since we need to know the page we're on anyway. + if (!paginationCursor || (paginationCursor && 'searchAfter' in paginationCursor)) { + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: hasMoreEntries } }); + } else if (paginationCursor && 'searchBefore' in paginationCursor) { + // We've requested a previous page, therefore there is a next page. + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: true } }); + } + setMetricsHostsAnomalies(anomalies); + }, + }, + [ + sourceId, + dispatch, + reducerState.timeRange, + reducerState.sortOptions, + reducerState.paginationOptions, + reducerState.paginationCursor, + reducerState.filteredDatasets, + ] + ); + + const changeSortOptions = useCallback( + (nextSortOptions: Sort) => { + dispatch({ type: 'changeSortOptions', payload: { sortOptions: nextSortOptions } }); + }, + [dispatch] + ); + + const changePaginationOptions = useCallback( + (nextPaginationOptions: PaginationOptions) => { + dispatch({ + type: 'changePaginationOptions', + payload: { paginationOptions: nextPaginationOptions }, + }); + }, + [dispatch] + ); + + // Time range has changed + useEffect(() => { + dispatch({ + type: 'changeTimeRange', + payload: { timeRange: { start: startTime, end: endTime } }, + }); + }, [startTime, endTime]); + + // Selected datasets have changed + useEffect(() => { + dispatch({ + type: 'changeFilteredDatasets', + payload: { filteredDatasets }, + }); + }, [filteredDatasets]); + + const handleFetchNextPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchNextPage' }); + } + }, [dispatch, reducerState]); + + const handleFetchPreviousPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchPreviousPage' }); + } + }, [dispatch, reducerState]); + + const isLoadingMetricsHostsAnomalies = useMemo( + () => getMetricsHostsAnomaliesRequest.state === 'pending', + [getMetricsHostsAnomaliesRequest.state] + ); + + const hasFailedLoadingMetricsHostsAnomalies = useMemo( + () => getMetricsHostsAnomaliesRequest.state === 'rejected', + [getMetricsHostsAnomaliesRequest.state] + ); + + return { + metricsHostsAnomalies, + getMetricsHostsAnomalies, + isLoadingMetricsHostsAnomalies, + hasFailedLoadingMetricsHostsAnomalies, + changeSortOptions, + sortOptions: reducerState.sortOptions, + changePaginationOptions, + paginationOptions: reducerState.paginationOptions, + fetchPreviousPage: reducerState.page > 1 ? handleFetchPreviousPage : undefined, + fetchNextPage: reducerState.hasNextPage ? handleFetchNextPage : undefined, + page: reducerState.page, + }; +}; + +export const callGetMetricHostsAnomaliesAPI = async ( + sourceId: string, + startTime: number, + endTime: number, + metric: Metric, + sort: Sort, + pagination: Pagination +) => { + const response = await npStart.http.fetch(INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, { + method: 'POST', + body: JSON.stringify( + getMetricsHostsAnomaliesRequestPayloadRT.encode({ + data: { + sourceId, + timeRange: { + startTime, + endTime, + }, + metric, + sort, + pagination, + }, + }) + ), + }); + + return decodeOrThrow(getMetricsHostsAnomaliesSuccessReponsePayloadRT)(response); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts new file mode 100644 index 0000000000000..89e70c4c5c4c7 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts @@ -0,0 +1,322 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { + Sort, + Pagination, + PaginationCursor, + INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + getMetricsK8sAnomaliesSuccessReponsePayloadRT, + getMetricsK8sAnomaliesRequestPayloadRT, + MetricsK8sAnomaly, + Metric, +} from '../../../../../common/http_api/infra_ml'; +import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; +import { npStart } from '../../../../legacy_singletons'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +export type SortOptions = Sort; +export type PaginationOptions = Pick; +export type Page = number; +export type FetchNextPage = () => void; +export type FetchPreviousPage = () => void; +export type ChangeSortOptions = (sortOptions: Sort) => void; +export type ChangePaginationOptions = (paginationOptions: PaginationOptions) => void; +export type MetricsK8sAnomalies = MetricsK8sAnomaly[]; +interface PaginationCursors { + previousPageCursor: PaginationCursor; + nextPageCursor: PaginationCursor; +} + +interface ReducerState { + page: number; + lastReceivedCursors: PaginationCursors | undefined; + paginationCursor: Pagination['cursor'] | undefined; + hasNextPage: boolean; + paginationOptions: PaginationOptions; + sortOptions: Sort; + timeRange: { + start: number; + end: number; + }; + filteredDatasets?: string[]; +} + +type ReducerStateDefaults = Pick< + ReducerState, + 'page' | 'lastReceivedCursors' | 'paginationCursor' | 'hasNextPage' +>; + +type ReducerAction = + | { type: 'changePaginationOptions'; payload: { paginationOptions: PaginationOptions } } + | { type: 'changeSortOptions'; payload: { sortOptions: Sort } } + | { type: 'fetchNextPage' } + | { type: 'fetchPreviousPage' } + | { type: 'changeHasNextPage'; payload: { hasNextPage: boolean } } + | { type: 'changeLastReceivedCursors'; payload: { lastReceivedCursors: PaginationCursors } } + | { type: 'changeTimeRange'; payload: { timeRange: { start: number; end: number } } } + | { type: 'changeFilteredDatasets'; payload: { filteredDatasets?: string[] } }; + +const stateReducer = (state: ReducerState, action: ReducerAction): ReducerState => { + const resetPagination = { + page: 1, + paginationCursor: undefined, + }; + switch (action.type) { + case 'changePaginationOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeSortOptions': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeHasNextPage': + return { + ...state, + ...action.payload, + }; + case 'changeLastReceivedCursors': + return { + ...state, + ...action.payload, + }; + case 'fetchNextPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page + 1, + paginationCursor: { searchAfter: state.lastReceivedCursors.nextPageCursor }, + } + : state; + case 'fetchPreviousPage': + return state.lastReceivedCursors + ? { + ...state, + page: state.page - 1, + paginationCursor: { searchBefore: state.lastReceivedCursors.previousPageCursor }, + } + : state; + case 'changeTimeRange': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + case 'changeFilteredDatasets': + return { + ...state, + ...resetPagination, + ...action.payload, + }; + default: + return state; + } +}; + +const STATE_DEFAULTS: ReducerStateDefaults = { + // NOTE: This piece of state is purely for the client side, it could be extracted out of the hook. + page: 1, + // Cursor from the last request + lastReceivedCursors: undefined, + // Cursor to use for the next request. For the first request, and therefore not paging, this will be undefined. + paginationCursor: undefined, + hasNextPage: false, +}; + +export const useMetricsK8sAnomaliesResults = ({ + endTime, + startTime, + sourceId, + defaultSortOptions, + defaultPaginationOptions, + onGetMetricsHostsAnomaliesDatasetsError, + filteredDatasets, +}: { + endTime: number; + startTime: number; + sourceId: string; + defaultSortOptions: Sort; + defaultPaginationOptions: Pick; + onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; + filteredDatasets?: string[]; +}) => { + const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { + return { + ...stateDefaults, + paginationOptions: defaultPaginationOptions, + sortOptions: defaultSortOptions, + filteredDatasets, + timeRange: { + start: startTime, + end: endTime, + }, + }; + }; + + const [reducerState, dispatch] = useReducer(stateReducer, STATE_DEFAULTS, initStateReducer); + + const [metricsK8sAnomalies, setMetricsK8sAnomalies] = useState([]); + + const [getMetricsK8sAnomaliesRequest, getMetricsK8sAnomalies] = useTrackedPromise( + { + cancelPreviousOn: 'creation', + createPromise: async (metric: Metric) => { + const { + timeRange: { start: queryStartTime, end: queryEndTime }, + sortOptions, + paginationOptions, + paginationCursor, + filteredDatasets: queryFilteredDatasets, + } = reducerState; + return await callGetMetricsK8sAnomaliesAPI( + sourceId, + queryStartTime, + queryEndTime, + metric, + sortOptions, + { + ...paginationOptions, + cursor: paginationCursor, + }, + queryFilteredDatasets + ); + }, + onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { + const { paginationCursor } = reducerState; + if (requestCursors) { + dispatch({ + type: 'changeLastReceivedCursors', + payload: { lastReceivedCursors: requestCursors }, + }); + } + // Check if we have more "next" entries. "Page" covers the "previous" scenario, + // since we need to know the page we're on anyway. + if (!paginationCursor || (paginationCursor && 'searchAfter' in paginationCursor)) { + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: hasMoreEntries } }); + } else if (paginationCursor && 'searchBefore' in paginationCursor) { + // We've requested a previous page, therefore there is a next page. + dispatch({ type: 'changeHasNextPage', payload: { hasNextPage: true } }); + } + setMetricsK8sAnomalies(anomalies); + }, + }, + [ + sourceId, + dispatch, + reducerState.timeRange, + reducerState.sortOptions, + reducerState.paginationOptions, + reducerState.paginationCursor, + reducerState.filteredDatasets, + ] + ); + + const changeSortOptions = useCallback( + (nextSortOptions: Sort) => { + dispatch({ type: 'changeSortOptions', payload: { sortOptions: nextSortOptions } }); + }, + [dispatch] + ); + + const changePaginationOptions = useCallback( + (nextPaginationOptions: PaginationOptions) => { + dispatch({ + type: 'changePaginationOptions', + payload: { paginationOptions: nextPaginationOptions }, + }); + }, + [dispatch] + ); + + // Time range has changed + useEffect(() => { + dispatch({ + type: 'changeTimeRange', + payload: { timeRange: { start: startTime, end: endTime } }, + }); + }, [startTime, endTime]); + + // Selected datasets have changed + useEffect(() => { + dispatch({ + type: 'changeFilteredDatasets', + payload: { filteredDatasets }, + }); + }, [filteredDatasets]); + + const handleFetchNextPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchNextPage' }); + } + }, [dispatch, reducerState]); + + const handleFetchPreviousPage = useCallback(() => { + if (reducerState.lastReceivedCursors) { + dispatch({ type: 'fetchPreviousPage' }); + } + }, [dispatch, reducerState]); + + const isLoadingMetricsK8sAnomalies = useMemo( + () => getMetricsK8sAnomaliesRequest.state === 'pending', + [getMetricsK8sAnomaliesRequest.state] + ); + + const hasFailedLoadingMetricsK8sAnomalies = useMemo( + () => getMetricsK8sAnomaliesRequest.state === 'rejected', + [getMetricsK8sAnomaliesRequest.state] + ); + + return { + metricsK8sAnomalies, + getMetricsK8sAnomalies, + isLoadingMetricsK8sAnomalies, + hasFailedLoadingMetricsK8sAnomalies, + changeSortOptions, + sortOptions: reducerState.sortOptions, + changePaginationOptions, + paginationOptions: reducerState.paginationOptions, + fetchPreviousPage: reducerState.page > 1 ? handleFetchPreviousPage : undefined, + fetchNextPage: reducerState.hasNextPage ? handleFetchNextPage : undefined, + page: reducerState.page, + }; +}; + +export const callGetMetricsK8sAnomaliesAPI = async ( + sourceId: string, + startTime: number, + endTime: number, + metric: Metric, + sort: Sort, + pagination: Pagination, + datasets?: string[] +) => { + const response = await npStart.http.fetch(INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, { + method: 'POST', + body: JSON.stringify( + getMetricsK8sAnomaliesRequestPayloadRT.encode({ + data: { + sourceId, + timeRange: { + startTime, + endTime, + }, + metric, + sort, + pagination, + datasets, + }, + }) + ), + }); + + return decodeOrThrow(getMetricsK8sAnomaliesSuccessReponsePayloadRT)(response); +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts index 06b53d531f53c..702213516c123 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts @@ -43,7 +43,7 @@ export function useSnapshot( const timerange: InfraTimerangeInput = { interval: '1m', to: currentTime, - from: currentTime - 360 * 1000, + from: currentTime - 1200 * 1000, lookbackSize: 20, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts new file mode 100644 index 0000000000000..597c268180819 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_timeline.ts @@ -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 { fold } from 'fp-ts/lib/Either'; +import { identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { first } from 'lodash'; +import { useEffect, useMemo, useCallback } from 'react'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getIntervalInSeconds } from '../../../../../server/utils/get_interval_in_seconds'; +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { useHTTPRequest } from '../../../../hooks/use_http_request'; +import { + SnapshotNodeResponseRT, + SnapshotNodeResponse, + SnapshotRequest, + InfraTimerangeInput, +} from '../../../../../common/http_api/snapshot_api'; +import { + InventoryItemType, + SnapshotMetricType, +} from '../../../../../common/inventory_models/types'; + +const ONE_MINUTE = 60; +const ONE_HOUR = ONE_MINUTE * 60; +const ONE_DAY = ONE_HOUR * 24; +const ONE_WEEK = ONE_DAY * 7; +const ONE_MONTH = ONE_DAY * 30; + +const getDisplayInterval = (interval: string | undefined) => { + if (interval) { + const intervalInSeconds = getIntervalInSeconds(interval); + if (intervalInSeconds < 300) return '5m'; + } + return interval; +}; + +const getTimeLengthFromInterval = (interval: string | undefined) => { + if (interval) { + const intervalInSeconds = getIntervalInSeconds(interval); + // Get up to 288 datapoints based on interval + const timeLength = + intervalInSeconds <= ONE_MINUTE * 15 + ? ONE_DAY + : intervalInSeconds <= ONE_MINUTE * 35 + ? ONE_DAY * 3 + : intervalInSeconds <= ONE_HOUR * 2.5 + ? ONE_WEEK + : ONE_MONTH; + return { timeLength, intervalInSeconds }; + } else { + return { timeLength: 0, intervalInSeconds: 0 }; + } +}; + +export function useTimeline( + filterQuery: string | null | undefined, + metrics: Array<{ type: SnapshotMetricType }>, + nodeType: InventoryItemType, + sourceId: string, + currentTime: number, + accountId: string, + region: string, + interval: string | undefined, + shouldReload: boolean +) { + const decodeResponse = (response: any) => { + return pipe( + SnapshotNodeResponseRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); + }; + + const displayInterval = useMemo(() => getDisplayInterval(interval), [interval]); + + const timeLengthResult = useMemo(() => getTimeLengthFromInterval(displayInterval), [ + displayInterval, + ]); + const { timeLength, intervalInSeconds } = timeLengthResult; + + const endTime = currentTime + intervalInSeconds * 1000; + const startTime = currentTime - timeLength * 1000; + const timerange: InfraTimerangeInput = { + interval: displayInterval ?? '', + to: endTime, + from: startTime, + ignoreLookback: true, + forceInterval: true, + }; + + const { error, loading, response, makeRequest } = useHTTPRequest( + '/api/metrics/snapshot', + 'POST', + JSON.stringify({ + metrics, + groupBy: null, + nodeType, + timerange, + filterQuery, + sourceId, + accountId, + region, + includeTimeseries: true, + } as SnapshotRequest), + decodeResponse + ); + + const loadData = useCallback(() => { + if (shouldReload) return makeRequest(); + return Promise.resolve(); + }, [makeRequest, shouldReload]); + + useEffect(() => { + (async () => { + if (timeLength) { + await loadData(); + } + })(); + }, [loadData, timeLength]); + + const timeseries = response + ? first(response.nodes.map((node) => first(node.metrics)?.timeseries)) + : null; + + return { + error: (error && error.message) || null, + loading: !interval ? true : loading, + timeseries, + startTime, + endTime, + reload: makeRequest, + }; +} diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts index 42469ffb5ee9a..bb6a70f65bb97 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts @@ -4,8 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts'; +import { + Theme, + PartialTheme, + LIGHT_THEME, + DARK_THEME, + mergeWithDefaultTheme, +} from '@elastic/charts'; export function getChartTheme(isDarkMode: boolean): Theme { return isDarkMode ? DARK_THEME : LIGHT_THEME; } + +export function getTimelineChartTheme(isDarkMode: boolean): Theme { + return isDarkMode ? DARK_THEME : mergeWithDefaultTheme(TIMELINE_LIGHT_THEME, LIGHT_THEME); +} + +const TIMELINE_LIGHT_THEME: PartialTheme = { + crosshair: { + band: { + fill: '#D3DAE6', + }, + }, + axes: { + gridLine: { + horizontal: { + stroke: '#eaeaea', + }, + }, + }, +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx index b33fe5c232f01..f566e5253c615 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx @@ -18,6 +18,10 @@ import { resp, createSeries, } from '../../../../utils/fixtures/metrics_explorer'; +import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options'; +import { SourceQuery } from '../../../../../common/graphql/types'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/public'; +import { HttpHandler } from 'kibana/public'; const mockedFetch = jest.fn(); @@ -31,7 +35,16 @@ const renderUseMetricsExplorerDataHook = () => { return {children}; }; return renderHook( - (props) => + (props: { + options: MetricsExplorerOptions; + source: SourceQuery.Query['source']['configuration'] | undefined; + derivedIndexPattern: IIndexPattern; + timeRange: MetricsExplorerTimeOptions; + afterKey: string | null | Record; + signal: any; + fetch?: HttpHandler; + shouldLoadImmediately?: boolean; + }) => useMetricsExplorerData( props.options, props.source, diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 66715b3fee28b..b409c32603ffc 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -8,7 +8,7 @@ import { AppMountParameters, PluginInitializerContext } from 'kibana/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { createMetricThresholdAlertType } from './alerting/metric_threshold'; import { createInventoryMetricAlertType } from './alerting/inventory'; -import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type'; +import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold/log_threshold_alert_type'; import { registerStartSingleton } from './legacy_singletons'; import { registerFeatures } from './register_feature'; import { diff --git a/x-pack/plugins/infra/public/utils/url_state.tsx b/x-pack/plugins/infra/public/utils/url_state.tsx index bf4cfbaf05965..5abd35afb7525 100644 --- a/x-pack/plugins/infra/public/utils/url_state.tsx +++ b/x-pack/plugins/infra/public/utils/url_state.tsx @@ -156,16 +156,14 @@ export const replaceStateKeyInQueryString = ( urlState: UrlState | undefined ) => (queryString: string) => { const previousQueryValues = parse(queryString, { sort: false }); - const encodedUrlState = - typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - - return stringify( - url.encodeQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }), - { sort: false, encode: false } - ); + const newValue = + typeof urlState === 'undefined' + ? previousQueryValues + : { + ...previousQueryValues, + [stateKey]: encodeRisonUrlState(urlState), + }; + return stringify(url.encodeQuery(newValue), { sort: false, encode: false }); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/plugins/infra/public/utils/use_url_state.ts b/x-pack/plugins/infra/public/utils/use_url_state.ts index ab0ca1311194f..dd1cc9aeef9e4 100644 --- a/x-pack/plugins/infra/public/utils/use_url_state.ts +++ b/x-pack/plugins/infra/public/utils/use_url_state.ts @@ -111,16 +111,15 @@ export const replaceStateKeyInQueryString = ( urlState: UrlState | undefined ) => (queryString: string) => { const previousQueryValues = parse(queryString, { sort: false }); - const encodedUrlState = - typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - - return stringify( - url.encodeQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }), - { sort: false, encode: false } - ); + const newValue = + typeof urlState === 'undefined' + ? previousQueryValues + : { + ...previousQueryValues, + [stateKey]: encodeRisonUrlState(urlState), + }; + + return stringify(url.encodeQuery(newValue), { sort: false, encode: false }); }; const replaceQueryStringInLocation = (location: Location, queryString: string): Location => { diff --git a/x-pack/plugins/infra/scripts/storybook.js b/x-pack/plugins/infra/scripts/storybook.js deleted file mode 100644 index 05d5daedf58f2..0000000000000 --- a/x-pack/plugins/infra/scripts/storybook.js +++ /dev/null @@ -1,13 +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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'infra', - storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.stories.tsx')], -}); diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index 12ac57eb90186..444530c4d79f0 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../common/alerting/logs/types'; +import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../common/alerting/logs/log_threshold/types'; import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/inventory_metric_threshold/types'; import { METRIC_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/metric_threshold/types'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; diff --git a/x-pack/plugins/infra/server/infra_server.ts b/x-pack/plugins/infra/server/infra_server.ts index a72e40e25b479..1d89b7be43296 100644 --- a/x-pack/plugins/infra/server/infra_server.ts +++ b/x-pack/plugins/infra/server/infra_server.ts @@ -13,6 +13,7 @@ import { InfraBackendLibs } from './lib/infra_types'; import { initGetLogEntryCategoriesRoute, initGetLogEntryCategoryDatasetsRoute, + initGetLogEntryCategoryDatasetsStatsRoute, initGetLogEntryCategoryExamplesRoute, initGetLogEntryRateRoute, initGetLogEntryExamplesRoute, @@ -21,6 +22,8 @@ import { initGetLogEntryAnomaliesRoute, initGetLogEntryAnomaliesDatasetsRoute, } from './routes/log_analysis'; +import { initGetK8sAnomaliesRoute } from './routes/infra_ml'; +import { initGetHostsAnomaliesRoute } from './routes/infra_ml'; import { initMetricExplorerRoute } from './routes/metrics_explorer'; import { initMetadataRoute } from './routes/metadata'; import { initSnapshotRoute } from './routes/snapshot'; @@ -52,10 +55,13 @@ export const initInfraServer = (libs: InfraBackendLibs) => { initIpToHostName(libs); initGetLogEntryCategoriesRoute(libs); initGetLogEntryCategoryDatasetsRoute(libs); + initGetLogEntryCategoryDatasetsStatsRoute(libs); initGetLogEntryCategoryExamplesRoute(libs); initGetLogEntryRateRoute(libs); initGetLogEntryAnomaliesRoute(libs); initGetLogEntryAnomaliesDatasetsRoute(libs); + initGetK8sAnomaliesRoute(libs); + initGetHostsAnomaliesRoute(libs); initSnapshotRoute(libs); initNodeDetailsRoute(libs); initSourceRoute(libs); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts index 71115ad3a5745..e1657968b3f92 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts @@ -23,7 +23,7 @@ import { UngroupedSearchQueryResponse, GroupedSearchQueryResponse, GroupedSearchQueryResponseRT, -} from '../../../../common/alerting/logs/types'; +} from '../../../../common/alerting/logs/log_threshold/types'; import { decodeOrThrow } from '../../../../common/runtime_types'; const COMPOSITE_GROUP_SIZE = 40; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts index f730513991a78..e04fe338f3436 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts @@ -17,11 +17,11 @@ import { import { Comparator, AlertStates, - LogDocumentCountAlertParams, + AlertParams, Criterion, UngroupedSearchQueryResponse, GroupedSearchQueryResponse, -} from '../../../../common/alerting/logs/types'; +} from '../../../../common/alerting/logs/log_threshold/types'; import { alertsMock } from '../../../../../alerts/server/mocks'; // Mocks // @@ -56,7 +56,7 @@ const negativeCriteria: Criterion[] = [ { ...textField, comparator: Comparator.NOT_MATCH_PHRASE }, ]; -const baseAlertParams: Pick = { +const baseAlertParams: Pick = { count: { comparator: Comparator.GT, value: 5, @@ -85,7 +85,7 @@ describe('Log threshold executor', () => { }); describe('Criteria filter building', () => { test('Handles positive criteria', () => { - const alertParams: LogDocumentCountAlertParams = { + const alertParams: AlertParams = { ...baseAlertParams, criteria: positiveCriteria, }; @@ -140,7 +140,7 @@ describe('Log threshold executor', () => { }); test('Handles negative criteria', () => { - const alertParams: LogDocumentCountAlertParams = { + const alertParams: AlertParams = { ...baseAlertParams, criteria: negativeCriteria, }; @@ -168,7 +168,7 @@ describe('Log threshold executor', () => { }); test('Handles time range', () => { - const alertParams: LogDocumentCountAlertParams = { ...baseAlertParams, criteria: [] }; + const alertParams: AlertParams = { ...baseAlertParams, criteria: [] }; const filters = buildFiltersFromCriteria(alertParams, TIMESTAMP_FIELD); expect(typeof filters.rangeFilter.range[TIMESTAMP_FIELD].gte).toBe('number'); expect(typeof filters.rangeFilter.range[TIMESTAMP_FIELD].lte).toBe('number'); @@ -183,7 +183,7 @@ describe('Log threshold executor', () => { describe('ES queries', () => { describe('Query generation', () => { test('Correctly generates ungrouped queries', () => { - const alertParams: LogDocumentCountAlertParams = { + const alertParams: AlertParams = { ...baseAlertParams, criteria: [...positiveCriteria, ...negativeCriteria], }; @@ -279,7 +279,7 @@ describe('Log threshold executor', () => { }); test('Correctly generates grouped queries', () => { - const alertParams: LogDocumentCountAlertParams = { + const alertParams: AlertParams = { ...baseAlertParams, groupBy: ['host.name'], criteria: [...positiveCriteria, ...negativeCriteria], @@ -303,25 +303,6 @@ describe('Log threshold executor', () => { }, }, ], - must_not: [ - { - term: { - keywordField: { - value: 'error', - }, - }, - }, - { - match: { - textField: 'Something went wrong', - }, - }, - { - match_phrase: { - textField: 'Something went wrong', - }, - }, - ], }, }, aggregations: { @@ -398,6 +379,25 @@ describe('Log threshold executor', () => { }, }, ], + must_not: [ + { + term: { + keywordField: { + value: 'error', + }, + }, + }, + { + match: { + textField: 'Something went wrong', + }, + }, + { + match_phrase: { + textField: 'Something went wrong', + }, + }, + ], }, }, }, @@ -467,6 +467,7 @@ describe('Log threshold executor', () => { conditions: ' numericField more than 10', group: null, matchingDocuments: 10, + isRatio: false, }, }, ]); @@ -593,6 +594,7 @@ describe('Log threshold executor', () => { conditions: ' numericField more than 10', group: 'i-am-a-host-name-1, i-am-a-dataset-1', matchingDocuments: 10, + isRatio: false, }, }, ]); @@ -612,6 +614,7 @@ describe('Log threshold executor', () => { conditions: ' numericField more than 10', group: 'i-am-a-host-name-3, i-am-a-dataset-3', matchingDocuments: 20, + isRatio: false, }, }, ]); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index 224b898141c36..0ea65f94c9400 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -14,14 +14,21 @@ import { import { AlertStates, Comparator, - LogDocumentCountAlertParams, + AlertParams, Criterion, GroupedSearchQueryResponseRT, UngroupedSearchQueryResponseRT, UngroupedSearchQueryResponse, GroupedSearchQueryResponse, - LogDocumentCountAlertParamsRT, -} from '../../../../common/alerting/logs/types'; + AlertParamsRT, + isRatioAlertParams, + hasGroupBy, + getNumerator, + getDenominator, + Criteria, + CountAlertParams, + RatioAlertParams, +} from '../../../../common/alerting/logs/log_threshold/types'; import { InfraBackendLibs } from '../../infra_types'; import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { decodeOrThrow } from '../../../../common/runtime_types'; @@ -42,7 +49,6 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => async function ({ services, params }: AlertExecutorOptions) { const { alertInstanceFactory, savedObjectsClient, callCluster } = services; const { sources } = libs; - const { groupBy } = params; const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default'); const indexPattern = sourceConfiguration.configuration.logAlias; @@ -50,30 +56,23 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); try { - const validatedParams = decodeOrThrow(LogDocumentCountAlertParamsRT)(params); + const validatedParams = decodeOrThrow(AlertParamsRT)(params); - const query = - groupBy && groupBy.length > 0 - ? getGroupedESQuery(validatedParams, timestampField, indexPattern) - : getUngroupedESQuery(validatedParams, timestampField, indexPattern); - - if (!query) { - throw new Error('ES query could not be built from the provided alert params'); - } - - if (groupBy && groupBy.length > 0) { - processGroupByResults( - await getGroupedResults(query, callCluster), + if (!isRatioAlertParams(validatedParams)) { + await executeAlert( validatedParams, - alertInstanceFactory, - updateAlertInstance + timestampField, + indexPattern, + callCluster, + alertInstanceFactory ); } else { - processUngroupedResults( - await getUngroupedResults(query, callCluster), + await executeRatioAlert( validatedParams, - alertInstanceFactory, - updateAlertInstance + timestampField, + indexPattern, + callCluster, + alertInstanceFactory ); } } catch (e) { @@ -85,9 +84,97 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => } }; +async function executeAlert( + alertParams: CountAlertParams, + timestampField: string, + indexPattern: string, + callCluster: AlertServices['callCluster'], + alertInstanceFactory: AlertServices['alertInstanceFactory'] +) { + const query = getESQuery(alertParams, timestampField, indexPattern); + + if (!query) { + throw new Error('ES query could not be built from the provided alert params'); + } + + if (hasGroupBy(alertParams)) { + processGroupByResults( + await getGroupedResults(query, callCluster), + alertParams, + alertInstanceFactory, + updateAlertInstance + ); + } else { + processUngroupedResults( + await getUngroupedResults(query, callCluster), + alertParams, + alertInstanceFactory, + updateAlertInstance + ); + } +} + +async function executeRatioAlert( + alertParams: RatioAlertParams, + timestampField: string, + indexPattern: string, + callCluster: AlertServices['callCluster'], + alertInstanceFactory: AlertServices['alertInstanceFactory'] +) { + // Ratio alert params are separated out into two standard sets of alert params + const numeratorParams: AlertParams = { + ...alertParams, + criteria: getNumerator(alertParams.criteria), + }; + + const denominatorParams: AlertParams = { + ...alertParams, + criteria: getDenominator(alertParams.criteria), + }; + + const numeratorQuery = getESQuery(numeratorParams, timestampField, indexPattern); + const denominatorQuery = getESQuery(denominatorParams, timestampField, indexPattern); + + if (!numeratorQuery || !denominatorQuery) { + throw new Error('ES query could not be built from the provided ratio alert params'); + } + + if (hasGroupBy(alertParams)) { + const numeratorGroupedResults = await getGroupedResults(numeratorQuery, callCluster); + const denominatorGroupedResults = await getGroupedResults(denominatorQuery, callCluster); + processGroupByRatioResults( + numeratorGroupedResults, + denominatorGroupedResults, + alertParams, + alertInstanceFactory, + updateAlertInstance + ); + } else { + const numeratorUngroupedResults = await getUngroupedResults(numeratorQuery, callCluster); + const denominatorUngroupedResults = await getUngroupedResults(denominatorQuery, callCluster); + processUngroupedRatioResults( + numeratorUngroupedResults, + denominatorUngroupedResults, + alertParams, + alertInstanceFactory, + updateAlertInstance + ); + } +} + +const getESQuery = ( + alertParams: Omit & { criteria: Criteria }, + timestampField: string, + indexPattern: string +) => { + return hasGroupBy(alertParams) + ? getGroupedESQuery(alertParams, timestampField, indexPattern) + : getUngroupedESQuery(alertParams, timestampField, indexPattern); +}; + export const processUngroupedResults = ( results: UngroupedSearchQueryResponse, - params: LogDocumentCountAlertParams, + params: CountAlertParams, alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { @@ -102,8 +189,41 @@ export const processUngroupedResults = ( actionGroup: FIRED_ACTIONS.id, context: { matchingDocuments: documentCount, - conditions: createConditionsMessage(criteria), + conditions: createConditionsMessageForCriteria(criteria), + group: null, + isRatio: false, + }, + }, + ]); + } else { + alertInstaceUpdater(alertInstance, AlertStates.OK); + } +}; + +export const processUngroupedRatioResults = ( + numeratorResults: UngroupedSearchQueryResponse, + denominatorResults: UngroupedSearchQueryResponse, + params: RatioAlertParams, + alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstaceUpdater: AlertInstanceUpdater +) => { + const { count, criteria } = params; + + const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); + const numeratorCount = numeratorResults.hits.total.value; + const denominatorCount = denominatorResults.hits.total.value; + const ratio = getRatio(numeratorCount, denominatorCount); + + if (ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value)) { + alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ + { + actionGroup: FIRED_ACTIONS.id, + context: { + ratio, + numeratorConditions: createConditionsMessageForCriteria(getNumerator(criteria)), + denominatorConditions: createConditionsMessageForCriteria(getDenominator(criteria)), group: null, + isRatio: true, }, }, ]); @@ -112,24 +232,39 @@ export const processUngroupedResults = ( } }; -interface ReducedGroupByResults { +const getRatio = (numerator: number, denominator: number) => { + // We follow the mathematics principle that dividing by 0 isn't possible, + // and a ratio is therefore undefined (or indeterminate). + if (numerator === 0 || denominator === 0) return undefined; + return numerator / denominator; +}; + +interface ReducedGroupByResult { name: string; documentCount: number; } +type ReducedGroupByResults = ReducedGroupByResult[]; + +const getReducedGroupByResults = ( + results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'] +): ReducedGroupByResults => { + return results.reduce((acc, groupBucket) => { + const groupName = Object.values(groupBucket.key).join(', '); + const groupResult = { name: groupName, documentCount: groupBucket.filtered_results.doc_count }; + return [...acc, groupResult]; + }, []); +}; + export const processGroupByResults = ( results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], - params: LogDocumentCountAlertParams, + params: CountAlertParams, alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; - const groupResults = results.reduce((acc, groupBucket) => { - const groupName = Object.values(groupBucket.key).join(', '); - const groupResult = { name: groupName, documentCount: groupBucket.filtered_results.doc_count }; - return [...acc, groupResult]; - }, []); + const groupResults = getReducedGroupByResults(results); groupResults.forEach((group) => { const alertInstance = alertInstanceFactory(group.name); @@ -141,8 +276,53 @@ export const processGroupByResults = ( actionGroup: FIRED_ACTIONS.id, context: { matchingDocuments: documentCount, - conditions: createConditionsMessage(criteria), + conditions: createConditionsMessageForCriteria(criteria), group: group.name, + isRatio: false, + }, + }, + ]); + } else { + alertInstaceUpdater(alertInstance, AlertStates.OK); + } + }); +}; + +export const processGroupByRatioResults = ( + numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], + denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], + params: RatioAlertParams, + alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstaceUpdater: AlertInstanceUpdater +) => { + const { count, criteria } = params; + + const numeratorGroupResults = getReducedGroupByResults(numeratorResults); + const denominatorGroupResults = getReducedGroupByResults(denominatorResults); + + numeratorGroupResults.forEach((numeratorGroup) => { + const alertInstance = alertInstanceFactory(numeratorGroup.name); + const numeratorDocumentCount = numeratorGroup.documentCount; + const denominatorGroup = denominatorGroupResults.find( + (_group) => _group.name === numeratorGroup.name + ); + // If there is no matching group, a ratio cannot be determined, and is therefore undefined. + const ratio = denominatorGroup + ? getRatio(numeratorDocumentCount, denominatorGroup.documentCount) + : undefined; + if ( + ratio !== undefined && + checkValueAgainstComparatorMap[count.comparator](ratio, count.value) + ) { + alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ + { + actionGroup: FIRED_ACTIONS.id, + context: { + ratio, + numeratorConditions: createConditionsMessageForCriteria(getNumerator(criteria)), + denominatorConditions: createConditionsMessageForCriteria(getDenominator(criteria)), + group: numeratorGroup.name, + isRatio: true, }, }, ]); @@ -172,7 +352,7 @@ export const updateAlertInstance: AlertInstanceUpdater = (alertInstance, state, }; export const buildFiltersFromCriteria = ( - params: Omit, + params: Pick & { criteria: Criteria }, timestampField: string ) => { const { timeSize, timeUnit, criteria } = params; @@ -223,7 +403,7 @@ export const buildFiltersFromCriteria = ( }; export const getGroupedESQuery = ( - params: Omit, + params: Pick & { criteria: Criteria }, timestampField: string, index: string ): object | undefined => { @@ -254,6 +434,7 @@ export const getGroupedESQuery = ( bool: { // Scope the inner filtering back to the unpadded range filter: [rangeFilter, ...mustFilters], + ...(mustNotFilters.length > 0 && { must_not: mustNotFilters }), }, }, }, @@ -265,7 +446,6 @@ export const getGroupedESQuery = ( query: { bool: { filter: [groupedRangeFilter], - ...(mustNotFilters.length > 0 && { must_not: mustNotFilters }), }, }, aggregations, @@ -281,7 +461,7 @@ export const getGroupedESQuery = ( }; export const getUngroupedESQuery = ( - params: Omit, + params: Pick & { criteria: Criteria }, timestampField: string, index: string ): object => { @@ -315,7 +495,7 @@ type Filter = { [key in SupportedESQueryTypes]?: object; }; -const buildFiltersForCriteria = (criteria: LogDocumentCountAlertParams['criteria']) => { +const buildFiltersForCriteria = (criteria: Criteria) => { let filters: Filter[] = []; criteria.forEach((criterion) => { @@ -443,7 +623,7 @@ const getGroupedResults = async (query: object, callCluster: AlertServices['call return compositeGroupBuckets; }; -const createConditionsMessage = (criteria: LogDocumentCountAlertParams['criteria']) => { +const createConditionsMessageForCriteria = (criteria: Criteria) => { const parts = criteria.map((criterion, index) => { const { field, comparator, value } = criterion; return `${index === 0 ? '' : 'and'} ${field} ${comparator} ${value}`; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index ab55601f4c475..2c1d7e0976607 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { schema } from '@kbn/config-schema'; import { PluginSetupContract } from '../../../../../alerts/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, - Comparator, -} from '../../../../common/alerting/logs/types'; + AlertParamsRT, +} from '../../../../common/alerting/logs/log_threshold/types'; import { InfraBackendLibs } from '../../infra_types'; +import { decodeOrThrow } from '../../../../common/runtime_types'; const documentCountActionVariableDescription = i18n.translate( 'xpack.infra.logs.alerting.threshold.documentCountActionVariableDescription', @@ -34,33 +34,33 @@ const groupByActionVariableDescription = i18n.translate( } ); -const countSchema = schema.object({ - value: schema.number(), - comparator: schema.oneOf([ - schema.literal(Comparator.GT), - schema.literal(Comparator.LT), - schema.literal(Comparator.GT_OR_EQ), - schema.literal(Comparator.LT_OR_EQ), - schema.literal(Comparator.EQ), - ]), -}); +const isRatioActionVariableDescription = i18n.translate( + 'xpack.infra.logs.alerting.threshold.isRatioActionVariableDescription', + { + defaultMessage: 'Denotes whether this alert was configured with a ratio', + } +); + +const ratioActionVariableDescription = i18n.translate( + 'xpack.infra.logs.alerting.threshold.ratioActionVariableDescription', + { + defaultMessage: 'The ratio value of the two sets of criteria', + } +); -const criteriaSchema = schema.object({ - field: schema.string(), - comparator: schema.oneOf([ - schema.literal(Comparator.GT), - schema.literal(Comparator.LT), - schema.literal(Comparator.GT_OR_EQ), - schema.literal(Comparator.LT_OR_EQ), - schema.literal(Comparator.EQ), - schema.literal(Comparator.NOT_EQ), - schema.literal(Comparator.MATCH), - schema.literal(Comparator.NOT_MATCH), - schema.literal(Comparator.MATCH_PHRASE), - schema.literal(Comparator.NOT_MATCH_PHRASE), - ]), - value: schema.oneOf([schema.number(), schema.string()]), -}); +const numeratorConditionsActionVariableDescription = i18n.translate( + 'xpack.infra.logs.alerting.threshold.numeratorConditionsActionVariableDescription', + { + defaultMessage: 'The conditions that the numerator of the ratio needed to fulfill', + } +); + +const denominatorConditionsActionVariableDescription = i18n.translate( + 'xpack.infra.logs.alerting.threshold.denominatorConditionsActionVariableDescription', + { + defaultMessage: 'The conditions that the denominator of the ratio needed to fulfill', + } +); export async function registerLogThresholdAlertType( alertingPlugin: PluginSetupContract, @@ -76,13 +76,9 @@ export async function registerLogThresholdAlertType( id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, name: 'Log threshold', validate: { - params: schema.object({ - count: countSchema, - criteria: schema.arrayOf(criteriaSchema), - timeUnit: schema.string(), - timeSize: schema.number(), - groupBy: schema.maybe(schema.arrayOf(schema.string())), - }), + params: { + validate: (params) => decodeOrThrow(AlertParamsRT)(params), + }, }, defaultActionGroupId: FIRED_ACTIONS.id, actionGroups: [FIRED_ACTIONS], @@ -92,6 +88,14 @@ export async function registerLogThresholdAlertType( { name: 'matchingDocuments', description: documentCountActionVariableDescription }, { name: 'conditions', description: conditionsActionVariableDescription }, { name: 'group', description: groupByActionVariableDescription }, + // Ratio alerts + { name: 'isRatio', description: isRatioActionVariableDescription }, + { name: 'ratio', description: ratioActionVariableDescription }, + { name: 'numeratorConditions', description: numeratorConditionsActionVariableDescription }, + { + name: 'denominatorConditions', + description: denominatorConditionsActionVariableDescription, + }, ], }, producer: 'logs', diff --git a/x-pack/plugins/infra/server/lib/infra_ml/common.ts b/x-pack/plugins/infra/server/lib/infra_ml/common.ts new file mode 100644 index 0000000000000..4d2be94c7cd62 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/common.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import type { MlAnomalyDetectors, MlSystem } from '../../types'; +import { NoLogAnalysisMlJobError } from './errors'; + +import { + CompositeDatasetKey, + createLogEntryDatasetsQuery, + LogEntryDatasetBucket, + logEntryDatasetsResponseRT, +} from './queries/log_entry_data_sets'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { startTracingSpan, TracingSpan } from '../../../common/performance_tracing'; + +export async function fetchMlJob(mlAnomalyDetectors: MlAnomalyDetectors, jobId: string) { + const finalizeMlGetJobSpan = startTracingSpan('Fetch ml job from ES'); + const { + jobs: [mlJob], + } = await mlAnomalyDetectors.jobs(jobId); + + const mlGetJobSpan = finalizeMlGetJobSpan(); + + if (mlJob == null) { + throw new NoLogAnalysisMlJobError(`Failed to find ml job ${jobId}.`); + } + + return { + mlJob, + timing: { + spans: [mlGetJobSpan], + }, + }; +} + +const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; + +// Finds datasets related to ML job ids +export async function getLogEntryDatasets( + mlSystem: MlSystem, + startTime: number, + endTime: number, + jobIds: string[] +) { + const finalizeLogEntryDatasetsSpan = startTracingSpan('get data sets'); + + let logEntryDatasetBuckets: LogEntryDatasetBucket[] = []; + let afterLatestBatchKey: CompositeDatasetKey | undefined; + let esSearchSpans: TracingSpan[] = []; + + while (true) { + const finalizeEsSearchSpan = startTracingSpan('fetch log entry dataset batch from ES'); + + const logEntryDatasetsResponse = decodeOrThrow(logEntryDatasetsResponseRT)( + await mlSystem.mlAnomalySearch( + createLogEntryDatasetsQuery( + jobIds, + startTime, + endTime, + COMPOSITE_AGGREGATION_BATCH_SIZE, + afterLatestBatchKey + ) + ) + ); + + const { after_key: afterKey, buckets: latestBatchBuckets = [] } = + logEntryDatasetsResponse.aggregations?.dataset_buckets ?? {}; + + logEntryDatasetBuckets = [...logEntryDatasetBuckets, ...latestBatchBuckets]; + afterLatestBatchKey = afterKey; + esSearchSpans = [...esSearchSpans, finalizeEsSearchSpan()]; + + if (latestBatchBuckets.length < COMPOSITE_AGGREGATION_BATCH_SIZE) { + break; + } + } + + const logEntryDatasetsSpan = finalizeLogEntryDatasetsSpan(); + + return { + data: logEntryDatasetBuckets.map((logEntryDatasetBucket) => logEntryDatasetBucket.key.dataset), + timing: { + spans: [logEntryDatasetsSpan, ...esSearchSpans], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/errors.ts b/x-pack/plugins/infra/server/lib/infra_ml/errors.ts new file mode 100644 index 0000000000000..ad46ebf710266 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/errors.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable max-classes-per-file */ + +import { + UnknownMLCapabilitiesError, + InsufficientMLCapabilities, + MLPrivilegesUninitialized, +} from '../../../../ml/server'; + +export class NoLogAnalysisMlJobError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class InsufficientLogAnalysisMlJobConfigurationError extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class UnknownCategoryError extends Error { + constructor(categoryId: number) { + super(`Unknown ml category ${categoryId}`); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export class InsufficientAnomalyMlJobsConfigured extends Error { + constructor(message?: string) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export const isMlPrivilegesError = (error: any) => { + return ( + error instanceof UnknownMLCapabilitiesError || + error instanceof InsufficientMLCapabilities || + error instanceof MLPrivilegesUninitialized + ); +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/index.ts b/x-pack/plugins/infra/server/lib/infra_ml/index.ts new file mode 100644 index 0000000000000..536f0a44d5890 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/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 * from './errors'; +export * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts new file mode 100644 index 0000000000000..a3a0f91afaab8 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext } from 'src/core/server'; +import { InfraRequestHandlerContext } from '../../types'; +import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; +import { fetchMlJob } from './common'; +import { getJobId, metricsHostsJobTypes } from '../../../common/infra_ml'; +import { Sort, Pagination } from '../../../common/http_api/infra_ml'; +import type { MlSystem, MlAnomalyDetectors } from '../../types'; +import { InsufficientAnomalyMlJobsConfigured, isMlPrivilegesError } from './errors'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { + metricsHostsAnomaliesResponseRT, + createMetricsHostsAnomaliesQuery, +} from './queries/metrics_hosts_anomalies'; + +interface MappedAnomalyHit { + id: string; + anomalyScore: number; + typical: number; + actual: number; + jobId: string; + startTime: number; + duration: number; + influencers: string[]; + categoryId?: string; +} + +async function getCompatibleAnomaliesJobIds( + spaceId: string, + sourceId: string, + metric: 'memory_usage' | 'network_in' | 'network_out' | undefined, + mlAnomalyDetectors: MlAnomalyDetectors +) { + let metricsHostsJobIds = metricsHostsJobTypes; + + if (metric) { + metricsHostsJobIds = metricsHostsJobIds.filter((jt) => jt === `hosts_${metric}`); + } + + const jobIds: string[] = []; + let jobSpans: TracingSpan[] = []; + + try { + await Promise.all( + metricsHostsJobIds + .map((jt) => getJobId(spaceId, sourceId, jt)) + .map((id) => { + return (async () => { + const { + timing: { spans }, + } = await fetchMlJob(mlAnomalyDetectors, id); + jobIds.push(id); + jobSpans = [...jobSpans, ...spans]; + })(); + }) + ); + } catch (e) { + if (isMlPrivilegesError(e)) { + throw e; + } + // An error is also thrown when no jobs are found + } + + return { + jobIds, + timing: { spans: jobSpans }, + }; +} + +export async function getMetricsHostsAnomalies( + context: RequestHandlerContext & { infra: Required }, + sourceId: string, + startTime: number, + endTime: number, + metric: 'memory_usage' | 'network_in' | 'network_out' | undefined, + sort: Sort, + pagination: Pagination +) { + const finalizeMetricsHostsAnomaliesSpan = startTracingSpan('get metrics hosts entry anomalies'); + + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + metric, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Metrics Hosts ML jobs need to be configured to search anomalies' + ); + } + + try { + const { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { spans: fetchLogEntryAnomaliesSpans }, + } = await fetchMetricsHostsAnomalies( + context.infra.mlSystem, + jobIds, + startTime, + endTime, + sort, + pagination + ); + + const data = anomalies.map((anomaly) => { + const { jobId } = anomaly; + + return parseAnomalyResult(anomaly, jobId); + }); + + const metricsHostsAnomaliesSpan = finalizeMetricsHostsAnomaliesSpan(); + + return { + data, + paginationCursors, + hasMoreEntries, + timing: { + spans: [metricsHostsAnomaliesSpan, ...jobSpans, ...fetchLogEntryAnomaliesSpans], + }, + }; + } catch (e) { + throw new Error(e); + } +} + +const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { + const { + id, + anomalyScore, + typical, + actual, + duration, + influencers, + startTime: anomalyStartTime, + } = anomaly; + + return { + id, + anomalyScore, + typical, + actual, + duration, + influencers, + startTime: anomalyStartTime, + type: 'metrics_hosts' as const, + jobId, + }; +}; + +async function fetchMetricsHostsAnomalies( + mlSystem: MlSystem, + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + // We'll request 1 extra entry on top of our pageSize to determine if there are + // more entries to be fetched. This avoids scenarios where the client side can't + // determine if entries.length === pageSize actually means there are more entries / next page + // or not. + const expandedPagination = { ...pagination, pageSize: pagination.pageSize + 1 }; + + const finalizeFetchLogEntryAnomaliesSpan = startTracingSpan('fetch metrics hosts anomalies'); + + const results = decodeOrThrow(metricsHostsAnomaliesResponseRT)( + await mlSystem.mlAnomalySearch( + createMetricsHostsAnomaliesQuery(jobIds, startTime, endTime, sort, expandedPagination) + ) + ); + + const { + hits: { hits }, + } = results; + const hasMoreEntries = hits.length > pagination.pageSize; + + // An extra entry was found and hasMoreEntries has been determined, the extra entry can be removed. + if (hasMoreEntries) { + hits.pop(); + } + + // To "search_before" the sort order will have been reversed for ES. + // The results are now reversed back, to match the requested sort. + if (pagination.cursor && 'searchBefore' in pagination.cursor) { + hits.reverse(); + } + + const paginationCursors = + hits.length > 0 + ? { + previousPageCursor: hits[0].sort, + nextPageCursor: hits[hits.length - 1].sort, + } + : undefined; + + const anomalies = hits.map((result) => { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + job_id, + record_score: anomalyScore, + typical, + actual, + influencers, + bucket_span: duration, + timestamp: anomalyStartTime, + by_field_value: categoryId, + } = result._source; + + const hostInfluencers = influencers.filter((i) => i.influencer_field_name === 'host.name'); + return { + id: result._id, + anomalyScore, + dataset: '', + typical: typical[0], + actual: actual[0], + jobId: job_id, + influencers: hostInfluencers.reduce( + (acc: string[], i) => [...acc, ...i.influencer_field_values], + [] + ), + startTime: anomalyStartTime, + duration: duration * 1000, + categoryId, + }; + }); + + const fetchLogEntryAnomaliesSpan = finalizeFetchLogEntryAnomaliesSpan(); + + return { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { + spans: [fetchLogEntryAnomaliesSpan], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts new file mode 100644 index 0000000000000..1a9b48ade83ed --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts @@ -0,0 +1,246 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext } from 'src/core/server'; +import { InfraRequestHandlerContext } from '../../types'; +import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; +import { fetchMlJob } from './common'; +import { getJobId, metricsK8SJobTypes } from '../../../common/infra_ml'; +import { Sort, Pagination } from '../../../common/http_api/infra_ml'; +import type { MlSystem, MlAnomalyDetectors } from '../../types'; +import { InsufficientAnomalyMlJobsConfigured, isMlPrivilegesError } from './errors'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { + metricsK8sAnomaliesResponseRT, + createMetricsK8sAnomaliesQuery, +} from './queries/metrics_k8s_anomalies'; + +interface MappedAnomalyHit { + id: string; + anomalyScore: number; + typical: number; + actual: number; + jobId: string; + startTime: number; + influencers: string[]; + duration: number; + categoryId?: string; +} + +async function getCompatibleAnomaliesJobIds( + spaceId: string, + sourceId: string, + metric: 'memory_usage' | 'network_in' | 'network_out' | undefined, + mlAnomalyDetectors: MlAnomalyDetectors +) { + let metricsK8sJobIds = metricsK8SJobTypes; + + if (metric) { + metricsK8sJobIds = metricsK8sJobIds.filter((jt) => jt === `k8s_${metric}`); + } + + const jobIds: string[] = []; + let jobSpans: TracingSpan[] = []; + + try { + await Promise.all( + metricsK8sJobIds + .map((jt) => getJobId(spaceId, sourceId, jt)) + .map((id) => { + return (async () => { + const { + timing: { spans }, + } = await fetchMlJob(mlAnomalyDetectors, id); + jobIds.push(id); + jobSpans = [...jobSpans, ...spans]; + })(); + }) + ); + } catch (e) { + if (isMlPrivilegesError(e)) { + throw e; + } + // An error is also thrown when no jobs are found + } + + return { + jobIds, + timing: { spans: jobSpans }, + }; +} + +export async function getMetricK8sAnomalies( + context: RequestHandlerContext & { infra: Required }, + sourceId: string, + startTime: number, + endTime: number, + metric: 'memory_usage' | 'network_in' | 'network_out' | undefined, + sort: Sort, + pagination: Pagination +) { + const finalizeMetricsK8sAnomaliesSpan = startTracingSpan('get metrics k8s entry anomalies'); + + const { + jobIds, + timing: { spans: jobSpans }, + } = await getCompatibleAnomaliesJobIds( + context.infra.spaceId, + sourceId, + metric, + context.infra.mlAnomalyDetectors + ); + + if (jobIds.length === 0) { + throw new InsufficientAnomalyMlJobsConfigured( + 'Log rate or categorisation ML jobs need to be configured to search anomalies' + ); + } + + const { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { spans: fetchLogEntryAnomaliesSpans }, + } = await fetchMetricK8sAnomalies( + context.infra.mlSystem, + jobIds, + startTime, + endTime, + sort, + pagination + ); + + const data = anomalies.map((anomaly) => { + const { jobId } = anomaly; + + return parseAnomalyResult(anomaly, jobId); + }); + + const metricsK8sAnomaliesSpan = finalizeMetricsK8sAnomaliesSpan(); + + return { + data, + paginationCursors, + hasMoreEntries, + timing: { + spans: [metricsK8sAnomaliesSpan, ...jobSpans, ...fetchLogEntryAnomaliesSpans], + }, + }; +} + +const parseAnomalyResult = (anomaly: MappedAnomalyHit, jobId: string) => { + const { + id, + anomalyScore, + typical, + actual, + duration, + influencers, + startTime: anomalyStartTime, + } = anomaly; + + return { + id, + anomalyScore, + typical, + actual, + duration, + startTime: anomalyStartTime, + influencers, + type: 'metrics_k8s' as const, + jobId, + }; +}; + +async function fetchMetricK8sAnomalies( + mlSystem: MlSystem, + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) { + // We'll request 1 extra entry on top of our pageSize to determine if there are + // more entries to be fetched. This avoids scenarios where the client side can't + // determine if entries.length === pageSize actually means there are more entries / next page + // or not. + const expandedPagination = { ...pagination, pageSize: pagination.pageSize + 1 }; + + const finalizeFetchLogEntryAnomaliesSpan = startTracingSpan('fetch metrics k8s anomalies'); + + const results = decodeOrThrow(metricsK8sAnomaliesResponseRT)( + await mlSystem.mlAnomalySearch( + createMetricsK8sAnomaliesQuery(jobIds, startTime, endTime, sort, expandedPagination) + ) + ); + + const { + hits: { hits }, + } = results; + const hasMoreEntries = hits.length > pagination.pageSize; + + // An extra entry was found and hasMoreEntries has been determined, the extra entry can be removed. + if (hasMoreEntries) { + hits.pop(); + } + + // To "search_before" the sort order will have been reversed for ES. + // The results are now reversed back, to match the requested sort. + if (pagination.cursor && 'searchBefore' in pagination.cursor) { + hits.reverse(); + } + + const paginationCursors = + hits.length > 0 + ? { + previousPageCursor: hits[0].sort, + nextPageCursor: hits[hits.length - 1].sort, + } + : undefined; + + const anomalies = hits.map((result) => { + const { + // eslint-disable-next-line @typescript-eslint/naming-convention + job_id, + record_score: anomalyScore, + typical, + actual, + bucket_span: duration, + timestamp: anomalyStartTime, + by_field_value: categoryId, + influencers, + } = result._source; + + const podInfluencers = influencers.filter( + (i) => i.influencer_field_name === 'kubernetes.pod.uid' + ); + return { + id: result._id, + anomalyScore, + typical: typical[0], + actual: actual[0], + jobId: job_id, + influencers: podInfluencers.reduce( + (acc: string[], i) => [...acc, ...i.influencer_field_values], + [] + ), + startTime: anomalyStartTime, + duration: duration * 1000, + categoryId, + }; + }); + + const fetchLogEntryAnomaliesSpan = finalizeFetchLogEntryAnomaliesSpan(); + + return { + anomalies, + paginationCursors, + hasMoreEntries, + timing: { + spans: [fetchLogEntryAnomaliesSpan], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts new file mode 100644 index 0000000000000..eb08b6d692336 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 defaultRequestParameters = { + allowNoIndices: true, + ignoreUnavailable: true, + trackScores: false, + trackTotalHits: false, +}; + +export const createJobIdFilters = (jobId: string) => [ + { + term: { + job_id: { + value: jobId, + }, + }, + }, +]; + +export const createJobIdsFilters = (jobIds: string[]) => [ + { + terms: { + job_id: jobIds, + }, + }, +]; + +export const createTimeRangeFilters = (startTime: number, endTime: number) => [ + { + range: { + timestamp: { + gte: startTime, + lte: endTime, + }, + }, + }, +]; + +export const createAnomalyScoreFilter = (minScore: number) => [ + { + range: { + record_score: { + gte: minScore, + }, + }, + }, +]; + +export const createResultTypeFilters = (resultTypes: Array<'model_plot' | 'record'>) => [ + { + terms: { + result_type: resultTypes, + }, + }, +]; + +export const createCategoryIdFilters = (categoryIds: number[]) => [ + { + terms: { + category_id: categoryIds, + }, + }, +]; + +export const createDatasetsFilters = (datasets?: string[]) => + datasets && datasets.length > 0 + ? [ + { + terms: { + partition_field_value: datasets, + }, + }, + ] + : []; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts new file mode 100644 index 0000000000000..5a42011e1cea1 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './metrics_k8s_anomalies'; +export * from './metrics_hosts_anomalies'; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts new file mode 100644 index 0000000000000..53971a91d86b1 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/log_entry_data_sets.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createResultTypeFilters, + createTimeRangeFilters, + defaultRequestParameters, +} from './common'; + +export const createLogEntryDatasetsQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + size: number, + afterKey?: CompositeDatasetKey +) => ({ + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: [ + ...createJobIdsFilters(jobIds), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['model_plot']), + ], + }, + }, + aggs: { + dataset_buckets: { + composite: { + after: afterKey, + size, + sources: [ + { + dataset: { + terms: { + field: 'partition_field_value', + order: 'asc', + }, + }, + }, + ], + }, + }, + }, + }, + size: 0, +}); + +const compositeDatasetKeyRT = rt.type({ + dataset: rt.string, +}); + +export type CompositeDatasetKey = rt.TypeOf; + +const logEntryDatasetBucketRT = rt.type({ + key: compositeDatasetKeyRT, +}); + +export type LogEntryDatasetBucket = rt.TypeOf; + +export const logEntryDatasetsResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.partial({ + aggregations: rt.type({ + dataset_buckets: rt.intersection([ + rt.type({ + buckets: rt.array(logEntryDatasetBucketRT), + }), + rt.partial({ + after_key: compositeDatasetKeyRT, + }), + ]), + }), + }), +]); + +export type LogEntryDatasetsResponse = rt.TypeOf; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts new file mode 100644 index 0000000000000..bbdc77af1fbe6 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_hosts_anomalies.ts @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createTimeRangeFilters, + createResultTypeFilters, + defaultRequestParameters, + createAnomalyScoreFilter, +} from './common'; +import { Sort, Pagination } from '../../../../common/http_api/infra_ml'; + +// TODO: Reassess validity of this against ML docs +const TIEBREAKER_FIELD = '_doc'; + +const sortToMlFieldMap = { + dataset: 'partition_field_value', + anomalyScore: 'record_score', + startTime: 'timestamp', +}; + +export const createMetricsHostsAnomaliesQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) => { + const { field } = sort; + const { pageSize } = pagination; + + const filters = [ + ...createJobIdsFilters(jobIds), + ...createAnomalyScoreFilter(50), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['record']), + ]; + + const sourceFields = [ + 'job_id', + 'record_score', + 'typical', + 'actual', + 'partition_field_value', + 'timestamp', + 'bucket_span', + 'by_field_value', + 'host.name', + 'influencers.influencer_field_name', + 'influencers.influencer_field_values', + ]; + + const { querySortDirection, queryCursor } = parsePaginationCursor(sort, pagination); + + const sortOptions = [ + { [sortToMlFieldMap[field]]: querySortDirection }, + { [TIEBREAKER_FIELD]: querySortDirection }, // Tiebreaker + ]; + + const resultsQuery = { + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: filters, + }, + }, + search_after: queryCursor, + sort: sortOptions, + size: pageSize, + _source: sourceFields, + }, + }; + + return resultsQuery; +}; + +export const metricsHostsAnomalyHitRT = rt.type({ + _id: rt.string, + _source: rt.intersection([ + rt.type({ + job_id: rt.string, + record_score: rt.number, + typical: rt.array(rt.number), + actual: rt.array(rt.number), + influencers: rt.array( + rt.type({ + influencer_field_name: rt.string, + influencer_field_values: rt.array(rt.string), + }) + ), + 'host.name': rt.array(rt.string), + bucket_span: rt.number, + timestamp: rt.number, + }), + rt.partial({ + by_field_value: rt.string, + }), + ]), + sort: rt.tuple([rt.union([rt.string, rt.number]), rt.union([rt.string, rt.number])]), +}); + +export type MetricsHostsAnomalyHit = rt.TypeOf; + +export const metricsHostsAnomaliesResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.type({ + hits: rt.type({ + hits: rt.array(metricsHostsAnomalyHitRT), + }), + }), +]); + +export type MetricsHostsAnomaliesResponseRT = rt.TypeOf; + +const parsePaginationCursor = (sort: Sort, pagination: Pagination) => { + const { cursor } = pagination; + const { direction } = sort; + + if (!cursor) { + return { querySortDirection: direction, queryCursor: undefined }; + } + + // We will always use ES's search_after to paginate, to mimic "search_before" behaviour we + // need to reverse the user's chosen search direction for the ES query. + if ('searchBefore' in cursor) { + return { + querySortDirection: direction === 'desc' ? 'asc' : 'desc', + queryCursor: cursor.searchBefore, + }; + } else { + return { querySortDirection: direction, queryCursor: cursor.searchAfter }; + } +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts new file mode 100644 index 0000000000000..79bfdc91dc5a4 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createTimeRangeFilters, + createResultTypeFilters, + defaultRequestParameters, + createAnomalyScoreFilter, +} from './common'; +import { Sort, Pagination } from '../../../../common/http_api/infra_ml'; + +// TODO: Reassess validity of this against ML docs +const TIEBREAKER_FIELD = '_doc'; + +const sortToMlFieldMap = { + dataset: 'partition_field_value', + anomalyScore: 'record_score', + startTime: 'timestamp', +}; + +export const createMetricsK8sAnomaliesQuery = ( + jobIds: string[], + startTime: number, + endTime: number, + sort: Sort, + pagination: Pagination +) => { + const { field } = sort; + const { pageSize } = pagination; + + const filters = [ + ...createJobIdsFilters(jobIds), + ...createAnomalyScoreFilter(50), + ...createTimeRangeFilters(startTime, endTime), + ...createResultTypeFilters(['record']), + ]; + + const sourceFields = [ + 'job_id', + 'record_score', + 'typical', + 'actual', + 'partition_field_value', + 'timestamp', + 'bucket_span', + 'by_field_value', + 'influencers.influencer_field_name', + 'influencers.influencer_field_values', + ]; + + const { querySortDirection, queryCursor } = parsePaginationCursor(sort, pagination); + + const sortOptions = [ + { [sortToMlFieldMap[field]]: querySortDirection }, + { [TIEBREAKER_FIELD]: querySortDirection }, // Tiebreaker + ]; + + const resultsQuery = { + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: filters, + }, + }, + search_after: queryCursor, + sort: sortOptions, + size: pageSize, + _source: sourceFields, + }, + }; + + return resultsQuery; +}; + +export const metricsK8sAnomalyHitRT = rt.type({ + _id: rt.string, + _source: rt.intersection([ + rt.type({ + job_id: rt.string, + record_score: rt.number, + typical: rt.array(rt.number), + actual: rt.array(rt.number), + influencers: rt.array( + rt.type({ + influencer_field_name: rt.string, + influencer_field_values: rt.array(rt.string), + }) + ), + bucket_span: rt.number, + timestamp: rt.number, + }), + rt.partial({ + by_field_value: rt.string, + }), + ]), + sort: rt.tuple([rt.union([rt.string, rt.number]), rt.union([rt.string, rt.number])]), +}); + +export type MetricsK8sAnomalyHit = rt.TypeOf; + +export const metricsK8sAnomaliesResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.type({ + hits: rt.type({ + hits: rt.array(metricsK8sAnomalyHitRT), + }), + }), +]); + +export type MetricsK8sAnomaliesResponseRT = rt.TypeOf; + +const parsePaginationCursor = (sort: Sort, pagination: Pagination) => { + const { cursor } = pagination; + const { direction } = sort; + + if (!cursor) { + return { querySortDirection: direction, queryCursor: undefined }; + } + + // We will always use ES's search_after to paginate, to mimic "search_before" behaviour we + // need to reverse the user's chosen search direction for the ES query. + if ('searchBefore' in cursor) { + return { + querySortDirection: direction === 'desc' ? 'asc' : 'desc', + queryCursor: cursor.searchBefore, + }; + } else { + return { querySortDirection: direction, queryCursor: cursor.searchAfter }; + } +}; diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.ts new file mode 100644 index 0000000000000..ee4ccbfaeb5a7 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/ml_jobs.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 * as rt from 'io-ts'; + +export const createMlJobsQuery = (jobIds: string[]) => ({ + method: 'GET', + path: `/_ml/anomaly_detectors/${jobIds.join(',')}`, + query: { + allow_no_jobs: true, + }, +}); + +export const mlJobRT = rt.type({ + job_id: rt.string, + custom_settings: rt.unknown, +}); + +export const mlJobsResponseRT = rt.type({ + jobs: rt.array(mlJobRT), +}); diff --git a/x-pack/plugins/infra/server/lib/log_analysis/common.ts b/x-pack/plugins/infra/server/lib/log_analysis/common.ts index 4d2be94c7cd62..7e4a714a47d1f 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/common.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/common.ts @@ -36,7 +36,7 @@ export async function fetchMlJob(mlAnomalyDetectors: MlAnomalyDetectors, jobId: }; } -const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; +export const COMPOSITE_AGGREGATION_BATCH_SIZE = 1000; // Finds datasets related to ML job ids export async function getLogEntryDatasets( diff --git a/x-pack/plugins/infra/server/lib/log_analysis/index.ts b/x-pack/plugins/infra/server/lib/log_analysis/index.ts index c9a176be0a28f..bb571a8edf39b 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/index.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/index.ts @@ -6,5 +6,6 @@ export * from './errors'; export * from './log_entry_categories_analysis'; +export * from './log_entry_categories_datasets_stats'; export * from './log_entry_rate_analysis'; export * from './log_entry_anomalies'; diff --git a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_datasets_stats.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_datasets_stats.ts new file mode 100644 index 0000000000000..ec5f3c88dff2a --- /dev/null +++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_categories_datasets_stats.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { startTracingSpan } from '../../../common/performance_tracing'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import type { MlAnomalyDetectors, MlSystem } from '../../types'; +import { COMPOSITE_AGGREGATION_BATCH_SIZE } from './common'; +import { + CompositeDatasetKey, + createLatestLogEntryCategoriesDatasetsStatsQuery, + latestLogEntryCategoriesDatasetsStatsResponseRT, + LogEntryCategoryDatasetStatsBucket, +} from './queries/latest_log_entry_categories_datasets_stats'; + +export async function getLatestLogEntriesCategoriesDatasetsStats( + context: { + infra: { + mlAnomalyDetectors: MlAnomalyDetectors; + mlSystem: MlSystem; + }; + }, + jobIds: string[], + startTime: number, + endTime: number, + includeCategorizerStatuses: Array<'ok' | 'warn'> = [] +) { + const finalizeLogEntryCategoriesDatasetsStats = startTracingSpan('get categories datasets stats'); + + let latestLogEntryCategoriesDatasetsStatsBuckets: LogEntryCategoryDatasetStatsBucket[] = []; + let afterLatestBatchKey: CompositeDatasetKey | undefined; + + while (true) { + const latestLogEntryCategoriesDatasetsStatsResponse = await context.infra.mlSystem.mlAnomalySearch( + createLatestLogEntryCategoriesDatasetsStatsQuery( + jobIds, + startTime, + endTime, + COMPOSITE_AGGREGATION_BATCH_SIZE, + afterLatestBatchKey + ) + ); + + const { after_key: afterKey, buckets: latestBatchBuckets = [] } = + decodeOrThrow(latestLogEntryCategoriesDatasetsStatsResponseRT)( + latestLogEntryCategoriesDatasetsStatsResponse + ).aggregations?.dataset_composite_terms ?? {}; + + const latestIncludedBatchBuckets = + includeCategorizerStatuses.length > 0 + ? latestBatchBuckets.filter((bucket) => + bucket.categorizer_stats_top_hits.hits.hits.some((hit) => + includeCategorizerStatuses.includes(hit._source.categorization_status) + ) + ) + : latestBatchBuckets; + + latestLogEntryCategoriesDatasetsStatsBuckets = [ + ...latestLogEntryCategoriesDatasetsStatsBuckets, + ...latestIncludedBatchBuckets, + ]; + + afterLatestBatchKey = afterKey; + if (afterKey == null || latestBatchBuckets.length < COMPOSITE_AGGREGATION_BATCH_SIZE) { + break; + } + } + + const logEntryCategoriesDatasetsStatsSpan = finalizeLogEntryCategoriesDatasetsStats(); + + return { + data: latestLogEntryCategoriesDatasetsStatsBuckets.map((bucket) => { + const latestHitSource = bucket.categorizer_stats_top_hits.hits.hits[0]._source; + + return { + categorization_status: latestHitSource.categorization_status, + categorized_doc_count: latestHitSource.categorized_doc_count, + dataset: bucket.key.dataset ?? '', + dead_category_count: latestHitSource.dead_category_count, + failed_category_count: latestHitSource.failed_category_count, + frequent_category_count: latestHitSource.frequent_category_count, + job_id: latestHitSource.job_id, + log_time: latestHitSource.log_time, + rare_category_count: latestHitSource.rare_category_count, + total_category_count: latestHitSource.total_category_count, + }; + }), + timing: { + spans: [logEntryCategoriesDatasetsStatsSpan], + }, + }; +} diff --git a/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts index 63e39ef022392..bb1a1969e99eb 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts @@ -40,7 +40,20 @@ export const createTimeRangeFilters = (startTime: number, endTime: number) => [ }, ]; -export const createResultTypeFilters = (resultTypes: Array<'model_plot' | 'record'>) => [ +export const createLogTimeRangeFilters = (startTime: number, endTime: number) => [ + { + range: { + log_time: { + gte: startTime, + lte: endTime, + }, + }, + }, +]; + +export const createResultTypeFilters = ( + resultTypes: Array<'categorizer_stats' | 'model_plot' | 'record'> +) => [ { terms: { result_type: resultTypes, diff --git a/x-pack/plugins/infra/server/lib/log_analysis/queries/latest_log_entry_categories_datasets_stats.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/latest_log_entry_categories_datasets_stats.ts new file mode 100644 index 0000000000000..b9224e8125a48 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/log_analysis/queries/latest_log_entry_categories_datasets_stats.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as rt from 'io-ts'; +import { commonSearchSuccessResponseFieldsRT } from '../../../utils/elasticsearch_runtime_types'; +import { + createJobIdsFilters, + createResultTypeFilters, + defaultRequestParameters, + createLogTimeRangeFilters, +} from './common'; + +export const createLatestLogEntryCategoriesDatasetsStatsQuery = ( + logEntryCategoriesJobIds: string[], + startTime: number, + endTime: number, + size: number, + afterKey?: CompositeDatasetKey +) => ({ + ...defaultRequestParameters, + body: { + query: { + bool: { + filter: [ + ...createJobIdsFilters(logEntryCategoriesJobIds), + ...createResultTypeFilters(['categorizer_stats']), + ...createLogTimeRangeFilters(startTime, endTime), + ], + }, + }, + aggregations: { + dataset_composite_terms: { + composite: { + after: afterKey, + size, + sources: [ + { + dataset: { + terms: { + field: 'partition_field_value', + missing_bucket: true, + }, + }, + }, + ], + }, + aggs: { + categorizer_stats_top_hits: { + top_hits: { + size: 1, + sort: [ + { + log_time: 'desc', + }, + ], + _source: [ + 'categorization_status', + 'categorized_doc_count', + 'dead_category_count', + 'failed_category_count', + 'frequent_category_count', + 'job_id', + 'log_time', + 'rare_category_count', + 'total_category_count', + ], + }, + }, + }, + }, + }, + }, + size: 0, +}); + +export const logEntryCategoryStatusRT = rt.keyof({ + ok: null, + warn: null, +}); + +export const logEntryCategorizerStatsHitRT = rt.type({ + _source: rt.type({ + categorization_status: logEntryCategoryStatusRT, + categorized_doc_count: rt.number, + dead_category_count: rt.number, + failed_category_count: rt.number, + frequent_category_count: rt.number, + job_id: rt.string, + log_time: rt.number, + rare_category_count: rt.number, + total_category_count: rt.number, + }), +}); + +export type LogEntryCategorizerStatsHit = rt.TypeOf; + +const compositeDatasetKeyRT = rt.type({ + dataset: rt.union([rt.string, rt.null]), +}); + +export type CompositeDatasetKey = rt.TypeOf; + +const logEntryCategoryDatasetStatsBucketRT = rt.type({ + key: compositeDatasetKeyRT, + categorizer_stats_top_hits: rt.type({ + hits: rt.type({ + hits: rt.array(logEntryCategorizerStatsHitRT), + }), + }), +}); + +export type LogEntryCategoryDatasetStatsBucket = rt.TypeOf< + typeof logEntryCategoryDatasetStatsBucketRT +>; + +export const latestLogEntryCategoriesDatasetsStatsResponseRT = rt.intersection([ + commonSearchSuccessResponseFieldsRT, + rt.partial({ + aggregations: rt.type({ + dataset_composite_terms: rt.type({ + after_key: compositeDatasetKeyRT, + buckets: rt.array(logEntryCategoryDatasetStatsBucketRT), + }), + }), + }), +]); + +export type LatestLogEntryCategoriesDatasetsStatsResponse = rt.TypeOf< + typeof latestLogEntryCategoriesDatasetsStatsResponseRT +>; diff --git a/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_examples.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_examples.ts index eac5fa84d85a7..1b6a4c611e177 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_examples.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/queries/log_entry_examples.ts @@ -33,7 +33,7 @@ export const createLogEntryExamplesQuery = ( }, }, }, - ...(!!dataset + ...(dataset !== '' ? [ { term: { @@ -41,7 +41,19 @@ export const createLogEntryExamplesQuery = ( }, }, ] - : []), + : [ + { + bool: { + must_not: [ + { + exists: { + field: partitionField, + }, + }, + ], + }, + }, + ]), ...(categoryQuery ? [ { diff --git a/x-pack/plugins/infra/server/lib/metrics/constants.ts b/x-pack/plugins/infra/server/lib/metrics/constants.ts index 590eaf5605c72..dcff96ed155dc 100644 --- a/x-pack/plugins/infra/server/lib/metrics/constants.ts +++ b/x-pack/plugins/infra/server/lib/metrics/constants.ts @@ -4,13 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ export const EMPTY_RESPONSE = { - series: [ - { - id: '*', - keys: ['*'], - columns: [], - rows: [], - }, - ], + series: [], info: { total: 0, afterKey: null, interval: 0 }, }; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/index.ts b/x-pack/plugins/infra/server/routes/infra_ml/index.ts new file mode 100644 index 0000000000000..38684cb22e237 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './results'; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/index.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/index.ts new file mode 100644 index 0000000000000..82e30291faa20 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/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 * from './metrics_hosts_anomalies'; +export * from './metrics_k8s_anomalies'; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts new file mode 100644 index 0000000000000..9dc309c605206 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 Boom from 'boom'; +import { InfraBackendLibs } from '../../../lib/infra_types'; +import { + INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + getMetricsHostsAnomaliesSuccessReponsePayloadRT, + getMetricsHostsAnomaliesRequestPayloadRT, + GetMetricsHostsAnomaliesRequestPayload, + Sort, + Pagination, +} from '../../../../common/http_api/infra_ml'; +import { createValidationFunction } from '../../../../common/runtime_types'; +import { assertHasInfraMlPlugins } from '../../../utils/request_context'; + +import { isMlPrivilegesError } from '../../../lib/infra_ml/errors'; +import { getMetricsHostsAnomalies } from '../../../lib/infra_ml'; + +export const initGetHostsAnomaliesRoute = ({ framework }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, + validate: { + body: createValidationFunction(getMetricsHostsAnomaliesRequestPayloadRT), + }, + }, + framework.router.handleLegacyErrors(async (requestContext, request, response) => { + const { + data: { + sourceId, + timeRange: { startTime, endTime }, + sort: sortParam, + pagination: paginationParam, + metric, + }, + } = request.body; + + const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); + + try { + assertHasInfraMlPlugins(requestContext); + + const { + data: anomalies, + paginationCursors, + hasMoreEntries, + timing, + } = await getMetricsHostsAnomalies( + requestContext, + sourceId, + startTime, + endTime, + metric, + sort, + pagination + ); + + return response.ok({ + body: getMetricsHostsAnomaliesSuccessReponsePayloadRT.encode({ + data: { + anomalies, + hasMoreEntries, + paginationCursors, + }, + timing, + }), + }); + } catch (error) { + if (Boom.isBoom(error)) { + throw error; + } + + if (isMlPrivilegesError(error)) { + return response.customError({ + statusCode: 403, + body: { + message: error.message, + }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + }) + ); +}; + +const getSortAndPagination = ( + sort: Partial = {}, + pagination: Partial = {} +): { + sort: Sort; + pagination: Pagination; +} => { + const sortDefaults = { + field: 'anomalyScore' as const, + direction: 'desc' as const, + }; + + const sortWithDefaults = { + ...sortDefaults, + ...sort, + }; + + const paginationDefaults = { + pageSize: 50, + }; + + const paginationWithDefaults = { + ...paginationDefaults, + ...pagination, + }; + + return { sort: sortWithDefaults, pagination: paginationWithDefaults }; +}; diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts new file mode 100644 index 0000000000000..1618018b85fcf --- /dev/null +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { InfraBackendLibs } from '../../../lib/infra_types'; +import { + INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + getMetricsK8sAnomaliesSuccessReponsePayloadRT, + getMetricsK8sAnomaliesRequestPayloadRT, + GetMetricsK8sAnomaliesRequestPayload, + Sort, + Pagination, +} from '../../../../common/http_api/infra_ml'; +import { createValidationFunction } from '../../../../common/runtime_types'; +import { assertHasInfraMlPlugins } from '../../../utils/request_context'; +import { getMetricK8sAnomalies } from '../../../lib/infra_ml'; +import { isMlPrivilegesError } from '../../../lib/infra_ml/errors'; + +export const initGetK8sAnomaliesRoute = ({ framework }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, + validate: { + body: createValidationFunction(getMetricsK8sAnomaliesRequestPayloadRT), + }, + }, + framework.router.handleLegacyErrors(async (requestContext, request, response) => { + const { + data: { + sourceId, + timeRange: { startTime, endTime }, + sort: sortParam, + pagination: paginationParam, + metric, + }, + } = request.body; + + const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); + + try { + assertHasInfraMlPlugins(requestContext); + + const { + data: anomalies, + paginationCursors, + hasMoreEntries, + timing, + } = await getMetricK8sAnomalies( + requestContext, + sourceId, + startTime, + endTime, + metric, + sort, + pagination + ); + + return response.ok({ + body: getMetricsK8sAnomaliesSuccessReponsePayloadRT.encode({ + data: { + anomalies, + hasMoreEntries, + paginationCursors, + }, + timing, + }), + }); + } catch (error) { + if (Boom.isBoom(error)) { + throw error; + } + + if (isMlPrivilegesError(error)) { + return response.customError({ + statusCode: 403, + body: { + message: error.message, + }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + }) + ); +}; + +const getSortAndPagination = ( + sort: Partial = {}, + pagination: Partial = {} +): { + sort: Sort; + pagination: Pagination; +} => { + const sortDefaults = { + field: 'anomalyScore' as const, + direction: 'desc' as const, + }; + + const sortWithDefaults = { + ...sortDefaults, + ...sort, + }; + + const paginationDefaults = { + pageSize: 50, + }; + + const paginationWithDefaults = { + ...paginationDefaults, + ...pagination, + }; + + return { sort: sortWithDefaults, pagination: paginationWithDefaults }; +}; diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/index.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/index.ts index a01042616a872..e696477253823 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/index.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/index.ts @@ -6,6 +6,7 @@ export * from './log_entry_categories'; export * from './log_entry_category_datasets'; +export * from './log_entry_category_datasets_stats'; export * from './log_entry_category_examples'; export * from './log_entry_rate'; export * from './log_entry_examples'; diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts new file mode 100644 index 0000000000000..8414fc2062ae9 --- /dev/null +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 Boom from 'boom'; +import { + getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT, + getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT, + LOG_ANALYSIS_GET_LATEST_LOG_ENTRY_CATEGORY_DATASETS_STATS_PATH, +} from '../../../../common/http_api/log_analysis'; +import { createValidationFunction } from '../../../../common/runtime_types'; +import type { InfraBackendLibs } from '../../../lib/infra_types'; +import { getLatestLogEntriesCategoriesDatasetsStats } from '../../../lib/log_analysis'; +import { isMlPrivilegesError } from '../../../lib/log_analysis/errors'; +import { assertHasInfraMlPlugins } from '../../../utils/request_context'; + +export const initGetLogEntryCategoryDatasetsStatsRoute = ({ framework }: InfraBackendLibs) => { + framework.registerRoute( + { + method: 'post', + path: LOG_ANALYSIS_GET_LATEST_LOG_ENTRY_CATEGORY_DATASETS_STATS_PATH, + validate: { + body: createValidationFunction(getLatestLogEntryCategoryDatasetsStatsRequestPayloadRT), + }, + }, + framework.router.handleLegacyErrors(async (requestContext, request, response) => { + const { + data: { + jobIds, + timeRange: { startTime, endTime }, + includeCategorizerStatuses, + }, + } = request.body; + + try { + assertHasInfraMlPlugins(requestContext); + + const { data: datasetStats, timing } = await getLatestLogEntriesCategoriesDatasetsStats( + requestContext, + jobIds, + startTime, + endTime, + includeCategorizerStatuses + ); + + return response.ok({ + body: getLatestLogEntryCategoryDatasetsStatsSuccessResponsePayloadRT.encode({ + data: { + datasetStats, + }, + timing, + }), + }); + } catch (error) { + if (Boom.isBoom(error)) { + throw error; + } + + if (isMlPrivilegesError(error)) { + return response.customError({ + statusCode: 403, + body: { + message: error.message, + }, + }); + } + + return response.customError({ + statusCode: error.statusCode ?? 500, + body: { + message: error.message ?? 'An unexpected error occurred', + }, + }); + } + }) + ); +}; diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts index 700f4ef39bb66..b18b45f4935d2 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/transform_request_to_metrics_api_request.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { findInventoryFields } from '../../../../common/inventory_models'; +import { findInventoryFields, findInventoryModel } from '../../../../common/inventory_models'; import { MetricsAPIRequest, SnapshotRequest } from '../../../../common/http_api'; import { ESSearchClient } from '../../../lib/metrics/types'; import { InfraSource } from '../../../lib/sources'; @@ -34,7 +34,7 @@ export const transformRequestToMetricsAPIRequest = async ( interval: timeRangeWithIntervalApplied.interval, }, metrics: transformSnapshotMetricsToMetricsAPIMetrics(snapshotRequest), - limit: snapshotRequest.overrideCompositeSize ? snapshotRequest.overrideCompositeSize : 10, + limit: snapshotRequest.overrideCompositeSize ? snapshotRequest.overrideCompositeSize : 5, alignDataToEnd: true, }; @@ -52,12 +52,19 @@ export const transformRequestToMetricsAPIRequest = async ( filters.push({ term: { 'cloud.region': snapshotRequest.region } }); } + const inventoryModel = findInventoryModel(snapshotRequest.nodeType); + if (inventoryModel && inventoryModel.nodeFilter) { + inventoryModel.nodeFilter?.forEach((f) => filters.push(f)); + } + const inventoryFields = findInventoryFields( snapshotRequest.nodeType, source.configuration.fields ); - const groupBy = snapshotRequest.groupBy.map((g) => g.field).filter(Boolean) as string[]; - metricsApiRequest.groupBy = [...groupBy, inventoryFields.id]; + if (snapshotRequest.groupBy) { + const groupBy = snapshotRequest.groupBy.map((g) => g.field).filter(Boolean) as string[]; + metricsApiRequest.groupBy = [...groupBy, inventoryFields.id]; + } const metaAggregation = { id: META_KEY, diff --git a/x-pack/plugins/ingest_manager/common/constants/index.ts b/x-pack/plugins/ingest_manager/common/constants/index.ts index 519e2861cdc1d..bdc5714f7e2fe 100644 --- a/x-pack/plugins/ingest_manager/common/constants/index.ts +++ b/x-pack/plugins/ingest_manager/common/constants/index.ts @@ -13,3 +13,9 @@ export * from './epm'; export * from './output'; export * from './enrollment_api_key'; export * from './settings'; + +// TODO: This is the default `index.max_result_window` ES setting, which dictates +// the maximum amount of results allowed to be returned from a search. It's possible +// for the actual setting to differ from the default. Can we retrieve the real +// setting in the future? +export const SO_SEARCH_LIMIT = 10000; diff --git a/x-pack/plugins/ingest_manager/common/constants/routes.ts b/x-pack/plugins/ingest_manager/common/constants/routes.ts index 3e065142ea101..69672dfb9ec6c 100644 --- a/x-pack/plugins/ingest_manager/common/constants/routes.ts +++ b/x-pack/plugins/ingest_manager/common/constants/routes.ts @@ -15,9 +15,11 @@ export const LIMITED_CONCURRENCY_ROUTE_TAG = 'ingest:limited-concurrency'; // EPM API routes const EPM_PACKAGES_MANY = `${EPM_API_ROOT}/packages`; +const EPM_PACKAGES_BULK = `${EPM_PACKAGES_MANY}/_bulk`; const EPM_PACKAGES_ONE = `${EPM_PACKAGES_MANY}/{pkgkey}`; const EPM_PACKAGES_FILE = `${EPM_PACKAGES_MANY}/{pkgName}/{pkgVersion}`; export const EPM_API_ROUTES = { + BULK_INSTALL_PATTERN: EPM_PACKAGES_BULK, LIST_PATTERN: EPM_PACKAGES_MANY, LIMITED_LIST_PATTERN: `${EPM_PACKAGES_MANY}/limited`, INFO_PATTERN: EPM_PACKAGES_ONE, @@ -84,8 +86,11 @@ export const AGENT_API_ROUTES = { ACTIONS_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/actions`, ENROLL_PATTERN: `${FLEET_API_ROOT}/agents/enroll`, UNENROLL_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/unenroll`, + BULK_UNENROLL_PATTERN: `${FLEET_API_ROOT}/agents/bulk_unenroll`, REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/reassign`, + BULK_REASSIGN_PATTERN: `${FLEET_API_ROOT}/agents/bulk_reassign`, STATUS_PATTERN: `${FLEET_API_ROOT}/agent-status`, + UPGRADE_PATTERN: `${FLEET_API_ROOT}/agents/{agentId}/upgrade`, }; export const ENROLLMENT_API_KEY_ROUTES = { diff --git a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json index b7856e6d57402..28a88aa2be605 100644 --- a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json +++ b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json @@ -2757,7 +2757,7 @@ "data": "{\"config\":{\"id\":\"ae556400-5e39-11ea-8b49-f9747e466f7b\",\"outputs\":{\"default\":{\"type\":\"elasticsearch\",\"hosts\":[\"http://localhost:9200\"],\"api_key\":\"\",\"api_token\":\"6ckkp3ABz7e_XRqr3LM8:gQuDfUNSRgmY0iziYqP9Hw\"}},\"packagePolicies\":[]}}", "created_at": "2020-03-04T20:02:56.149Z", "id": "6a95c00a-d76d-4931-97c3-0bf935272d7d", - "type": "CONFIG_CHANGE" + "type": "POLICY_CHANGE" } ], "access_api_key_id": "6Mkkp3ABz7e_XRqrzLNJ", @@ -2920,7 +2920,7 @@ "actions": [ { "agent_id": "a6f14bd2-1a2a-481c-9212-9494d064ffdf", - "type": "CONFIG_CHANGE", + "type": "POLICY_CHANGE", "data": { "config": { "id": "2fe89350-a5e0-11ea-a587-5f886c8a849f", diff --git a/x-pack/plugins/ingest_manager/common/services/agent_status.ts b/x-pack/plugins/ingest_manager/common/services/agent_status.ts index fe4e094e1bb22..70f4d7f9344f9 100644 --- a/x-pack/plugins/ingest_manager/common/services/agent_status.ts +++ b/x-pack/plugins/ingest_manager/common/services/agent_status.ts @@ -19,6 +19,9 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta if (!agent.last_checkin) { return 'enrolling'; } + if (agent.upgrade_started_at && !agent.upgraded_at) { + return 'upgrading'; + } const msLastCheckIn = new Date(lastCheckIn || 0).getTime(); const msSinceLastCheckIn = new Date().getTime() - msLastCheckIn; diff --git a/x-pack/plugins/ingest_manager/common/services/index.ts b/x-pack/plugins/ingest_manager/common/services/index.ts index 46a1c65872d1b..4bffa01ad5ee2 100644 --- a/x-pack/plugins/ingest_manager/common/services/index.ts +++ b/x-pack/plugins/ingest_manager/common/services/index.ts @@ -12,3 +12,4 @@ export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage } from './limite export { decodeCloudId } from './decode_cloud_id'; export { isValidNamespace } from './is_valid_namespace'; export { isDiffPathProtocol } from './is_diff_path_protocol'; +export { LicenseService } from './license'; diff --git a/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts b/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts index 40f37cc456f94..3ed9e3a087a92 100644 --- a/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/is_valid_namespace.test.ts @@ -7,22 +7,32 @@ import { isValidNamespace } from './is_valid_namespace'; describe('Ingest Manager - isValidNamespace', () => { it('returns true for valid namespaces', () => { - expect(isValidNamespace('default')).toBe(true); - expect(isValidNamespace('namespace-with-dash')).toBe(true); - expect(isValidNamespace('123')).toBe(true); + expect(isValidNamespace('default').valid).toBe(true); + expect(isValidNamespace('namespace-with-dash').valid).toBe(true); + expect(isValidNamespace('123').valid).toBe(true); + expect(isValidNamespace('testlength😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀').valid).toBe( + true + ); }); it('returns false for invalid namespaces', () => { - expect(isValidNamespace('Default')).toBe(false); - expect(isValidNamespace('namespace with spaces')).toBe(false); - expect(isValidNamespace('foo/bar')).toBe(false); - expect(isValidNamespace('foo\\bar')).toBe(false); - expect(isValidNamespace('foo*bar')).toBe(false); - expect(isValidNamespace('foo?bar')).toBe(false); - expect(isValidNamespace('foo"bar')).toBe(false); - expect(isValidNamespace('foo, |, space character, comma, #, : - /^[^\*\\/\?"<>|\s,#:]+$/.test(namespace) - ); +// and implements a limit based on https://github.com/elastic/kibana/issues/75846 +export function isValidNamespace(namespace: string): { valid: boolean; error?: string } { + if (!namespace.trim()) { + return { + valid: false, + error: i18n.translate('xpack.ingestManager.namespaceValidation.requiredErrorMessage', { + defaultMessage: 'Namespace is required', + }), + }; + } else if (namespace !== namespace.toLowerCase()) { + return { + valid: false, + error: i18n.translate('xpack.ingestManager.namespaceValidation.lowercaseErrorMessage', { + defaultMessage: 'Namespace must be lowercase', + }), + }; + } else if (/[\*\\/\?"<>|\s,#:]+/.test(namespace)) { + return { + valid: false, + error: i18n.translate( + 'xpack.ingestManager.namespaceValidation.invalidCharactersErrorMessage', + { + defaultMessage: 'Namespace contains invalid characters', + } + ), + }; + } + // Node.js doesn't have Blob, and browser doesn't have Buffer :) + else if ( + (typeof Blob === 'function' && new Blob([namespace]).size > 100) || + (typeof Buffer === 'function' && Buffer.from(namespace).length > 100) + ) { + return { + valid: false, + error: i18n.translate('xpack.ingestManager.namespaceValidation.tooLongErrorMessage', { + defaultMessage: 'Namespace cannot be more than 100 bytes', + }), + }; + } + + return { valid: true }; } diff --git a/x-pack/plugins/ingest_manager/common/services/license.ts b/x-pack/plugins/ingest_manager/common/services/license.ts new file mode 100644 index 0000000000000..6d9b20a8456c0 --- /dev/null +++ b/x-pack/plugins/ingest_manager/common/services/license.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 { Observable, Subscription } from 'rxjs'; +import { ILicense } from '../../../licensing/common/types'; + +// Generic license service class that works with the license observable +// Both server and client plugins instancates a singleton version of this class +export class LicenseService { + private observable: Observable | null = null; + private subscription: Subscription | null = null; + private licenseInformation: ILicense | null = null; + + private updateInformation(licenseInformation: ILicense) { + this.licenseInformation = licenseInformation; + } + + public start(license$: Observable) { + this.observable = license$; + this.subscription = this.observable.subscribe(this.updateInformation.bind(this)); + } + + public stop() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + public getLicenseInformation() { + return this.licenseInformation; + } + + public getLicenseInformation$() { + return this.observable; + } + + public isGoldPlus() { + return ( + this.licenseInformation?.isAvailable && + this.licenseInformation?.isActive && + this.licenseInformation?.hasAtLeast('gold') + ); + } +} diff --git a/x-pack/plugins/ingest_manager/common/services/routes.ts b/x-pack/plugins/ingest_manager/common/services/routes.ts index b7521f95b4f83..3c3534926908a 100644 --- a/x-pack/plugins/ingest_manager/common/services/routes.ts +++ b/x-pack/plugins/ingest_manager/common/services/routes.ts @@ -46,6 +46,10 @@ export const epmRouteService = { ); // trim trailing slash }, + getBulkInstallPath: () => { + return EPM_API_ROUTES.BULK_INSTALL_PATTERN; + }, + getRemovePath: (pkgkey: string) => { return EPM_API_ROUTES.DELETE_PATTERN.replace('{pkgkey}', pkgkey).replace(/\/$/, ''); // trim trailing slash }, @@ -127,8 +131,10 @@ export const agentRouteService = { getEventsPath: (agentId: string) => AGENT_API_ROUTES.EVENTS_PATTERN.replace('{agentId}', agentId), getUnenrollPath: (agentId: string) => AGENT_API_ROUTES.UNENROLL_PATTERN.replace('{agentId}', agentId), + getBulkUnenrollPath: () => AGENT_API_ROUTES.BULK_UNENROLL_PATTERN, getReassignPath: (agentId: string) => AGENT_API_ROUTES.REASSIGN_PATTERN.replace('{agentId}', agentId), + getBulkReassignPath: () => AGENT_API_ROUTES.BULK_REASSIGN_PATTERN, getListPath: () => AGENT_API_ROUTES.LIST_PATTERN, getStatusPath: () => AGENT_API_ROUTES.STATUS_PATTERN, }; diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index a204373fe2e56..6ac783820ce82 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { FullAgentPolicy } from './agent_policy'; import { AGENT_TYPE_EPHEMERAL, AGENT_TYPE_PERMANENT, AGENT_TYPE_TEMPORARY } from '../../constants'; export type AgentType = @@ -19,10 +19,10 @@ export type AgentStatus = | 'warning' | 'enrolling' | 'unenrolling' + | 'upgrading' | 'degraded'; -export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL'; - +export type AgentActionType = 'POLICY_CHANGE' | 'UNENROLL' | 'UPGRADE'; export interface NewAgentAction { type: AgentActionType; data?: any; @@ -42,13 +42,24 @@ export interface AgentAction extends NewAgentAction { export interface AgentPolicyAction extends NewAgentAction { id: string; type: AgentActionType; - data?: any; + data: { + policy: FullAgentPolicy; + }; policy_id: string; policy_revision: number; created_at: string; ack_data?: any; } +// Make policy change action renaming BWC with agent version <= 7.9 +// eslint-disable-next-line @typescript-eslint/naming-convention +export type AgentPolicyActionV7_9 = Omit & { + type: 'CONFIG_CHANGE'; + data: { + config: FullAgentPolicy; + }; +}; + interface CommonAgentActionSOAttributes { type: AgentActionType; sent_at?: string; @@ -65,7 +76,6 @@ export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & { policy_id: string; policy_revision: number; }; - export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes; export interface NewAgentEvent { @@ -110,6 +120,8 @@ interface AgentBase { enrolled_at: string; unenrolled_at?: string; unenrollment_started_at?: string; + upgraded_at?: string; + upgrade_started_at?: string; shared_id?: string; access_api_key_id?: string; default_api_key?: string; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 54cdeade3764e..ab4c372c4e1d6 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -26,6 +26,7 @@ export interface GetAgentsRequest { export interface GetAgentsResponse { list: Agent[]; total: number; + totalInactive: number; page: number; perPage: number; } @@ -104,11 +105,31 @@ export interface PostAgentUnenrollRequest { params: { agentId: string; }; + body: { + force?: boolean; + }; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PostAgentUnenrollResponse {} +export interface PostAgentUpgradeRequest { + params: { + agentId: string; + }; +} +export interface PostBulkAgentUnenrollRequest { + body: { + agents: string[] | string; + force?: boolean; + }; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PostAgentUpgradeResponse {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface PostBulkAgentUnenrollResponse {} + export interface PutAgentReassignRequest { params: { agentId: string; @@ -119,6 +140,20 @@ export interface PutAgentReassignRequest { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PutAgentReassignResponse {} +export interface PostBulkAgentReassignRequest { + body: { + policy_id: string; + agents: string[] | string; + }; +} + +export interface PostBulkAgentReassignResponse { + [key: string]: { + success: boolean; + error?: Error; + }; +} + export interface GetOneAgentEventsRequest { params: { agentId: string; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts index 54e767fee4b22..0709eddaa52ec 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/epm.ts @@ -71,6 +71,30 @@ export interface InstallPackageResponse { response: AssetReference[]; } +export interface IBulkInstallPackageHTTPError { + name: string; + statusCode: number; + error: string | Error; +} + +export interface BulkInstallPackageInfo { + name: string; + newVersion: string; + // this will be null if no package was present before the upgrade (aka it was an install) + oldVersion: string | null; + assets: AssetReference[]; +} + +export interface BulkInstallPackagesResponse { + response: Array; +} + +export interface BulkInstallPackagesRequest { + body: { + packages: string[]; + }; +} + export interface MessageResponse { response: string; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts index 185e1fa5eb0ce..b97d39bac920b 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts @@ -7,6 +7,7 @@ export { PLUGIN_ID, EPM_API_ROUTES, AGENT_API_ROUTES, + SO_SEARCH_LIMIT, AGENT_POLICY_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts index 36b7d412bf276..64434e163f043 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts @@ -8,6 +8,7 @@ export { useCapabilities } from './use_capabilities'; export { useCore } from './use_core'; export { useConfig, ConfigContext } from './use_config'; export { useSetupDeps, useStartDeps, DepsContext } from './use_deps'; +export { licenseService, useLicense } from './use_license'; export { useBreadcrumbs } from './use_breadcrumbs'; export { useLink } from './use_link'; export { useKibanaLink } from './use_kibana_link'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx index 1d80495d2b347..b263f46b90a25 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx @@ -11,7 +11,7 @@ import { useCore } from './use_core'; const BASE_BREADCRUMB: ChromeBreadcrumb = { href: pagePathGetters.overview(), text: i18n.translate('xpack.ingestManager.breadcrumbs.appTitle', { - defaultMessage: 'Ingest Manager', + defaultMessage: 'Fleet', }), }; @@ -155,21 +155,15 @@ const breadcrumbGetters: { fleet: () => [ BASE_BREADCRUMB, { - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { - defaultMessage: 'Fleet', + text: i18n.translate('xpack.ingestManager.breadcrumbs.agentsPageTitle', { + defaultMessage: 'Agents', }), }, ], fleet_agent_list: () => [ BASE_BREADCRUMB, { - href: pagePathGetters.fleet(), - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { - defaultMessage: 'Fleet', - }), - }, - { - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetAgentsPageTitle', { + text: i18n.translate('xpack.ingestManager.breadcrumbs.agentsPageTitle', { defaultMessage: 'Agents', }), }, @@ -178,12 +172,7 @@ const breadcrumbGetters: { BASE_BREADCRUMB, { href: pagePathGetters.fleet(), - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { - defaultMessage: 'Fleet', - }), - }, - { - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetAgentsPageTitle', { + text: i18n.translate('xpack.ingestManager.breadcrumbs.agentsPageTitle', { defaultMessage: 'Agents', }), }, @@ -193,12 +182,12 @@ const breadcrumbGetters: { BASE_BREADCRUMB, { href: pagePathGetters.fleet(), - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { - defaultMessage: 'Fleet', + text: i18n.translate('xpack.ingestManager.breadcrumbs.agentsPageTitle', { + defaultMessage: 'Agents', }), }, { - text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetEnrollmentTokensPageTitle', { + text: i18n.translate('xpack.ingestManager.breadcrumbs.enrollmentTokensPageTitle', { defaultMessage: 'Enrollment tokens', }), }, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_license.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_license.ts new file mode 100644 index 0000000000000..411a6d6f2168f --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_license.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 { LicenseService } from '../services'; + +export const licenseService = new LicenseService(); + +export function useLicense() { + return licenseService; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts index cad1791af41be..41967fd068e0b 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts @@ -10,8 +10,14 @@ import { GetOneAgentResponse, GetOneAgentEventsResponse, GetOneAgentEventsRequest, + PostAgentUnenrollRequest, + PostBulkAgentUnenrollRequest, + PostBulkAgentUnenrollResponse, + PostAgentUnenrollResponse, PutAgentReassignRequest, PutAgentReassignResponse, + PostBulkAgentReassignRequest, + PostBulkAgentReassignResponse, GetAgentsRequest, GetAgentsResponse, GetAgentStatusRequest, @@ -83,3 +89,40 @@ export function sendPutAgentReassign( ...options, }); } + +export function sendPostBulkAgentReassign( + body: PostBulkAgentReassignRequest['body'], + options?: RequestOptions +) { + return sendRequest({ + method: 'post', + path: agentRouteService.getBulkReassignPath(), + body, + ...options, + }); +} + +export function sendPostAgentUnenroll( + agentId: string, + body: PostAgentUnenrollRequest['body'], + options?: RequestOptions +) { + return sendRequest({ + path: agentRouteService.getUnenrollPath(agentId), + method: 'post', + body, + ...options, + }); +} + +export function sendPostBulkAgentUnenroll( + body: PostBulkAgentUnenrollRequest['body'], + options?: RequestOptions +) { + return sendRequest({ + path: agentRouteService.getBulkUnenrollPath(), + method: 'post', + body, + ...options, + }); +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 5520a50463db4..0bef3c20ddd1a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -12,7 +12,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import styled from 'styled-components'; import { EuiErrorBoundary, EuiPanel, EuiEmptyPrompt, EuiCode } from '@elastic/eui'; import { CoreStart, AppMountParameters } from 'src/core/public'; -import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components'; +import { EuiThemeProvider } from '../../../../xpack_legacy/common'; import { IngestManagerSetupDeps, IngestManagerConfigType, @@ -22,9 +22,16 @@ import { PAGE_ROUTING_PATHS } from './constants'; import { DefaultLayout, WithoutHeaderLayout } from './layouts'; import { Loading, Error } from './components'; import { IngestManagerOverview, EPMApp, AgentPolicyApp, FleetApp, DataStreamApp } from './sections'; -import { DepsContext, ConfigContext, useConfig } from './hooks'; +import { + DepsContext, + ConfigContext, + useConfig, + useCore, + sendSetup, + sendGetPermissionsCheck, + licenseService, +} from './hooks'; import { PackageInstallProvider } from './sections/epm/hooks'; -import { useCore, sendSetup, sendGetPermissionsCheck } from './hooks'; import { FleetStatusProvider } from './hooks/use_fleet_status'; import './index.scss'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; @@ -279,4 +286,5 @@ export function renderApp( export const teardownIngestManager = (coreStart: CoreStart) => { coreStart.chrome.docTitle.reset(); coreStart.chrome.setBreadcrumbs([]); + licenseService.stop(); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx index 7da8330740532..5de47ee4f410b 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx @@ -83,8 +83,8 @@ export const DefaultLayout: React.FunctionComponent = ({ disabled={!fleet?.enabled} > diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx index b216270aa08f0..c716f7b12e78c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/components/agent_policy_form.tsx @@ -28,7 +28,7 @@ import { isValidNamespace } from '../../../services'; import { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; interface ValidationResults { - [key: string]: JSX.Element[]; + [key: string]: Array; } const StyledEuiAccordion = styled(EuiAccordion)` @@ -41,6 +41,7 @@ export const agentPolicyFormValidation = ( agentPolicy: Partial ): ValidationResults => { const errors: ValidationResults = {}; + const namespaceValidation = isValidNamespace(agentPolicy.namespace || ''); if (!agentPolicy.name?.trim()) { errors.name = [ @@ -51,20 +52,8 @@ export const agentPolicyFormValidation = ( ]; } - if (!agentPolicy.namespace?.trim()) { - errors.namespace = [ - , - ]; - } else if (!isValidNamespace(agentPolicy.namespace)) { - errors.namespace = [ - , - ]; + if (!namespaceValidation.valid && namespaceValidation.error) { + errors.namespace = [namespaceValidation.error]; } return errors; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts index 2714f1fe2e6e5..03060c5dcb20e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts @@ -50,6 +50,7 @@ export const validatePackagePolicy = ( namespace: null, inputs: {}, }; + const namespaceValidation = isValidNamespace(packagePolicy.namespace); if (!packagePolicy.name.trim()) { validationResults.name = [ @@ -59,18 +60,8 @@ export const validatePackagePolicy = ( ]; } - if (!packagePolicy.namespace.trim()) { - validationResults.namespace = [ - i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceRequiredErrorMessage', { - defaultMessage: 'Namespace is required', - }), - ]; - } else if (!isValidNamespace(packagePolicy.namespace)) { - validationResults.namespace = [ - i18n.translate('xpack.ingestManager.packagePolicyValidation.namespaceInvalidErrorMessage', { - defaultMessage: 'Namespace contains invalid characters', - }), - ]; + if (!namespaceValidation.valid && namespaceValidation.error) { + validationResults.namespace = [namespaceValidation.error]; } if ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx index 636ff7a5ff989..ea5dcce8c05bb 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { Agent } from '../../../../types'; import { useCapabilities } from '../../../../hooks'; import { ContextMenuActions } from '../../../../components'; -import { AgentUnenrollProvider, AgentReassignAgentPolicyFlyout } from '../../components'; +import { AgentUnenrollAgentModal, AgentReassignAgentPolicyFlyout } from '../../components'; import { useAgentRefresh } from '../hooks'; export const AgentDetailsActionMenu: React.FunctionComponent<{ @@ -20,6 +20,7 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ const hasWriteCapabilites = useCapabilities().write; const refreshAgent = useAgentRefresh(); const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(assignFlyoutOpenByDefault); + const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false); const isUnenrolling = agent.status === 'unenrolling'; const onClose = useMemo(() => { @@ -34,7 +35,20 @@ export const AgentDetailsActionMenu: React.FunctionComponent<{ <> {isReassignFlyoutOpen && ( - + + + )} + {isUnenrollModalOpen && ( + + { + setIsUnenrollModalOpen(false); + refreshAgent(); + }} + useForceUnenroll={isUnenrolling} + /> )} , - - {(unenrollAgentsPrompt) => ( - { - unenrollAgentsPrompt([agent.id], 1, refreshAgent); - }} - > - {isUnenrolling ? ( - - ) : ( - - )} - + { + setIsUnenrollModalOpen(true); + }} + > + {isUnenrolling ? ( + + ) : ( + )} - , + , ]} /> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/bulk_actions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/bulk_actions.tsx new file mode 100644 index 0000000000000..ee453b9e786f1 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/bulk_actions.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 styled from 'styled-components'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiPopover, + EuiContextMenu, + EuiButtonEmpty, + EuiIcon, + EuiPortal, +} from '@elastic/eui'; +import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; +import { SO_SEARCH_LIMIT } from '../../../../constants'; +import { Agent } from '../../../../types'; +import { AgentReassignAgentPolicyFlyout, AgentUnenrollAgentModal } from '../../components'; + +const Divider = styled.div` + width: 0; + height: ${(props) => props.theme.eui.euiSizeL}; + border-left: ${(props) => props.theme.eui.euiBorderThin}; +`; + +const FlexItem = styled(EuiFlexItem)` + height: ${(props) => props.theme.eui.euiSizeL}; +`; + +const Button = styled(EuiButtonEmpty)` + .euiButtonEmpty__text { + font-size: ${(props) => props.theme.eui.euiFontSizeXS}; + } +`; + +export type SelectionMode = 'manual' | 'query'; + +export const AgentBulkActions: React.FunctionComponent<{ + totalAgents: number; + totalInactiveAgents: number; + selectableAgents: number; + selectionMode: SelectionMode; + setSelectionMode: (mode: SelectionMode) => void; + currentQuery: string; + selectedAgents: Agent[]; + setSelectedAgents: (agents: Agent[]) => void; + refreshAgents: () => void; +}> = ({ + totalAgents, + totalInactiveAgents, + selectableAgents, + selectionMode, + setSelectionMode, + currentQuery, + selectedAgents, + setSelectedAgents, + refreshAgents, +}) => { + // Bulk actions menu states + const [isMenuOpen, setIsMenuOpen] = useState(false); + const closeMenu = () => setIsMenuOpen(false); + const openMenu = () => setIsMenuOpen(true); + + // Actions states + const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false); + const [isUnenrollModalOpen, setIsUnenrollModalOpen] = useState(false); + + // Check if user is working with only inactive agents + const atLeastOneActiveAgentSelected = + selectionMode === 'manual' + ? !!selectedAgents.find((agent) => agent.active) + : totalAgents > totalInactiveAgents; + + const panels = [ + { + id: 0, + items: [ + { + name: ( + + ), + icon: , + disabled: !atLeastOneActiveAgentSelected, + onClick: () => { + closeMenu(); + setIsReassignFlyoutOpen(true); + }, + }, + { + name: ( + + ), + icon: , + disabled: !atLeastOneActiveAgentSelected, + onClick: () => { + closeMenu(); + setIsUnenrollModalOpen(true); + }, + }, + { + name: ( + + ), + icon: , + onClick: () => { + closeMenu(); + setSelectionMode('manual'); + setSelectedAgents([]); + }, + }, + ], + }, + ]; + + return ( + <> + {isReassignFlyoutOpen && ( + + { + setIsReassignFlyoutOpen(false); + refreshAgents(); + }} + /> + + )} + {isUnenrollModalOpen && ( + + { + setIsUnenrollModalOpen(false); + refreshAgents(); + }} + /> + + )} + + + + {totalAgents > SO_SEARCH_LIMIT ? ( + , + total: , + }} + /> + ) : ( + + )} + + + {(selectionMode === 'manual' && selectedAgents.length) || + (selectionMode === 'query' && totalAgents > 0) ? ( + <> + + + + + + + + } + isOpen={isMenuOpen} + closePopover={closeMenu} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + + {selectionMode === 'manual' && + selectedAgents.length === selectableAgents && + selectableAgents < totalAgents ? ( + + + + ) : null} + + ) : ( + + )} + + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index 46f7ffb85b21f..0bc463ce98590 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useMemo, useCallback } from 'react'; +import React, { useState, useMemo, useCallback, useRef } from 'react'; import { EuiBasicTable, EuiButton, @@ -20,6 +20,7 @@ import { EuiContextMenuItem, EuiIcon, EuiPortal, + EuiHorizontalRule, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react'; @@ -33,11 +34,17 @@ import { useUrlParams, useLink, useBreadcrumbs, + useLicense, } from '../../../hooks'; import { SearchBar, ContextMenuActions } from '../../../components'; import { AgentStatusKueryHelper } from '../../../services'; import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; -import { AgentReassignAgentPolicyFlyout, AgentHealth, AgentUnenrollProvider } from '../components'; +import { + AgentReassignAgentPolicyFlyout, + AgentHealth, + AgentUnenrollAgentModal, +} from '../components'; +import { AgentBulkActions, SelectionMode } from './components/bulk_actions'; const REFRESH_INTERVAL_MS = 5000; @@ -63,72 +70,68 @@ const statusFilters = [ }, ] as Array<{ label: string; status: string }>; -const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refresh: () => void }>( - ({ agent, refresh, onReassignClick }) => { - const { getHref } = useLink(); - const hasWriteCapabilites = useCapabilities().write; +const RowActions = React.memo<{ + agent: Agent; + refresh: () => void; + onReassignClick: () => void; + onUnenrollClick: () => void; +}>(({ agent, refresh, onReassignClick, onUnenrollClick }) => { + const { getHref } = useLink(); + const hasWriteCapabilites = useCapabilities().write; - const isUnenrolling = agent.status === 'unenrolling'; - const [isMenuOpen, setIsMenuOpen] = useState(false); - return ( - setIsMenuOpen(isOpen)} - items={[ - + const isUnenrolling = agent.status === 'unenrolling'; + const [isMenuOpen, setIsMenuOpen] = useState(false); + return ( + setIsMenuOpen(isOpen)} + items={[ + + + , + { + onReassignClick(); + }} + disabled={!agent.active} + key="reassignPolicy" + > + + , + { + onUnenrollClick(); + }} + > + {isUnenrolling ? ( - , - { - onReassignClick(); - }} - disabled={!agent.active} - key="reassignPolicy" - > + ) : ( - , - - {(unenrollAgentsPrompt) => ( - { - unenrollAgentsPrompt([agent.id], 1, () => { - refresh(); - setIsMenuOpen(false); - }); - }} - > - {isUnenrolling ? ( - - ) : ( - - )} - - )} - , - ]} - /> - ); - } -); + )} + , + ]} + /> + ); +}); function safeMetadata(val: any) { if (typeof val !== 'string') { @@ -142,12 +145,16 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const { getHref } = useLink(); const defaultKuery: string = (useUrlParams().urlParams.kuery as string) || ''; const hasWriteCapabilites = useCapabilities().write; + const isGoldPlus = useLicense().isGoldPlus(); // Agent data states const [showInactive, setShowInactive] = useState(false); // Table and search states - const [search, setSearch] = useState(defaultKuery); + const [search, setSearch] = useState(defaultKuery); + const [selectionMode, setSelectionMode] = useState('manual'); + const [selectedAgents, setSelectedAgents] = useState([]); + const tableRef = useRef>(null); const { pagination, pageSizeOptions, setPagination } = usePagination(); // Policies state for filtering @@ -179,8 +186,9 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { // Agent enrollment flyout state const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); - // Agent reassignment flyout state - const [agentToReassignId, setAgentToReassignId] = useState(undefined); + // Agent actions states + const [agentToReassign, setAgentToReassign] = useState(undefined); + const [agentToUnenroll, setAgentToUnenroll] = useState(undefined); let kuery = search.trim(); if (selectedAgentPolicies.length) { @@ -229,6 +237,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const agents = agentsRequest.data ? agentsRequest.data.list : []; const totalAgents = agentsRequest.data ? agentsRequest.data.total : 0; + const totalInactiveAgents = agentsRequest.data ? agentsRequest.data.totalInactive : 0; const { isLoading } = agentsRequest; const agentPoliciesRequest = useGetAgentPolicies({ @@ -345,7 +354,8 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { agentsRequest.resendRequest()} - onReassignClick={() => setAgentToReassignId(agent.id)} + onReassignClick={() => setAgentToReassign(agent)} + onUnenrollClick={() => setAgentToUnenroll(agent)} /> ); }, @@ -378,8 +388,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { /> ); - const agentToReassign = agentToReassignId && agents.find((a) => a.id === agentToReassignId); - return ( <> {isEnrollmentFlyoutOpen ? ( @@ -391,15 +399,30 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { {agentToReassign && ( { - setAgentToReassignId(undefined); + setAgentToReassign(undefined); agentsRequest.resendRequest(); }} /> )} - + {agentToUnenroll && ( + + { + setAgentToUnenroll(undefined); + agentsRequest.resendRequest(); + }} + useForceUnenroll={agentToUnenroll.status === 'unenrolling'} + /> + + )} + + {/* Search and filter bar */} + @@ -510,9 +533,31 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { - + + {/* Agent total and bulk actions */} + agent.active).length || 0} + selectionMode={selectionMode} + setSelectionMode={setSelectionMode} + currentQuery={kuery} + selectedAgents={selectedAgents} + setSelectedAgents={(newAgents: Agent[]) => { + if (tableRef?.current) { + tableRef.current.setSelection(newAgents); + setSelectionMode('manual'); + } + }} + refreshAgents={() => agentsRequest.resendRequest()} + /> + + + + {/* Agent list table */} + ref={tableRef} className="fleet__agentList__table" data-test-subj="fleetAgentListTable" loading={isLoading} @@ -551,6 +596,18 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { totalItemCount: totalAgents, pageSizeOptions, }} + isSelectable={true} + selection={ + isGoldPlus + ? { + onSelectionChange: (newAgents: Agent[]) => { + setSelectedAgents(newAgents); + setSelectionMode('manual'); + }, + selectable: (agent: Agent) => agent.active, + } + : undefined + } onChange={({ page }: { page: { index: number; size: number } }) => { const newPagination = { ...pagination, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/agent_policy_selection.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/agent_policy_selection.tsx index 7f23c645f9a2e..874d42a8db095 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/agent_policy_selection.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/agent_policy_selection.tsx @@ -8,6 +8,7 @@ import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiSelect, EuiSpacer, EuiText, EuiButtonEmpty } from '@elastic/eui'; +import { SO_SEARCH_LIMIT } from '../../../../constants'; import { AgentPolicy, GetEnrollmentAPIKeysResponse } from '../../../../types'; import { sendGetEnrollmentAPIKeys, useCore } from '../../../../hooks'; import { AgentPolicyPackageBadges } from '../agent_policy_package_badges'; @@ -98,7 +99,7 @@ export const EnrollmentStepAgentPolicy: React.FC = (props) => { try { const res = await sendGetEnrollmentAPIKeys({ page: 1, - perPage: 10000, + perPage: SO_SEARCH_LIMIT, }); if (res.error) { throw res.error; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx index 04fef7f4b3f21..c840b487a3970 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx @@ -74,14 +74,14 @@ export const ManagedInstructions = React.memo(({ agentPolicies }) => { ) : ( <> ), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_policy_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_policy_flyout/index.tsx index 0c154bf1074c0..d3af1287c4025 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_policy_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_policy_flyout/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlyout, @@ -22,40 +22,55 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { Agent } from '../../../../types'; -import { sendPutAgentReassign, useCore, useGetAgentPolicies } from '../../../../hooks'; +import { + sendPutAgentReassign, + sendPostBulkAgentReassign, + useCore, + useGetAgentPolicies, +} from '../../../../hooks'; import { AgentPolicyPackageBadges } from '../agent_policy_package_badges'; interface Props { onClose: () => void; - agent: Agent; + agents: Agent[] | string; } export const AgentReassignAgentPolicyFlyout: React.FunctionComponent = ({ onClose, - agent, + agents, }) => { const { notifications } = useCore(); + const isSingleAgent = Array.isArray(agents) && agents.length === 1; + const [selectedAgentPolicyId, setSelectedAgentPolicyId] = useState( - agent.policy_id + isSingleAgent ? (agents[0] as Agent).policy_id : undefined ); - const agentPoliciesRequest = useGetAgentPolicies({ page: 1, perPage: 1000, }); const agentPolicies = agentPoliciesRequest.data ? agentPoliciesRequest.data.items : []; + useEffect(() => { + if (!selectedAgentPolicyId && agentPolicies[0]) { + setSelectedAgentPolicyId(agentPolicies[0].id); + } + }, [agentPolicies, selectedAgentPolicyId]); const [isSubmitting, setIsSubmitting] = useState(false); - async function onSubmit() { try { setIsSubmitting(true); if (!selectedAgentPolicyId) { throw new Error('No selected agent policy id'); } - const res = await sendPutAgentReassign(agent.id, { - policy_id: selectedAgentPolicyId, - }); + const res = isSingleAgent + ? await sendPutAgentReassign((agents[0] as Agent).id, { + policy_id: selectedAgentPolicyId, + }) + : await sendPostBulkAgentReassign({ + policy_id: selectedAgentPolicyId, + agents: Array.isArray(agents) ? agents.map((agent) => agent.id) : agents, + }); if (res.error) { throw res.error; } @@ -91,7 +106,10 @@ export const AgentReassignAgentPolicyFlyout: React.FunctionComponent = ({ @@ -106,6 +124,7 @@ export const AgentReassignAgentPolicyFlyout: React.FunctionComponent = ({ > ({ value: agentPolicy.id, text: agentPolicy.name, @@ -134,7 +153,7 @@ export const AgentReassignAgentPolicyFlyout: React.FunctionComponent = ({ void; + agents: Agent[] | string; + agentCount: number; + useForceUnenroll?: boolean; +} + +export const AgentUnenrollAgentModal: React.FunctionComponent = ({ + onClose, + agents, + agentCount, + useForceUnenroll, +}) => { + const { notifications } = useCore(); + const [forceUnenroll, setForceUnenroll] = useState(useForceUnenroll || false); + const [isSubmitting, setIsSubmitting] = useState(false); + const isSingleAgent = Array.isArray(agents) && agents.length === 1; + + async function onSubmit() { + try { + setIsSubmitting(true); + const { error } = isSingleAgent + ? await sendPostAgentUnenroll((agents[0] as Agent).id, { + force: forceUnenroll, + }) + : await sendPostBulkAgentUnenroll({ + agents: Array.isArray(agents) ? agents.map((agent) => agent.id) : agents, + force: forceUnenroll, + }); + if (error) { + throw error; + } + setIsSubmitting(false); + if (forceUnenroll) { + const successMessage = isSingleAgent + ? i18n.translate( + 'xpack.ingestManager.unenrollAgents.successForceSingleNotificationTitle', + { defaultMessage: 'Agent unenrolled' } + ) + : i18n.translate( + 'xpack.ingestManager.unenrollAgents.successForceMultiNotificationTitle', + { defaultMessage: 'Agents unenrolled' } + ); + notifications.toasts.addSuccess(successMessage); + } else { + const successMessage = isSingleAgent + ? i18n.translate('xpack.ingestManager.unenrollAgents.successSingleNotificationTitle', { + defaultMessage: 'Unenrolling agent', + }) + : i18n.translate('xpack.ingestManager.unenrollAgents.successMultiNotificationTitle', { + defaultMessage: 'Unenrolling agents', + }); + notifications.toasts.addSuccess(successMessage); + } + onClose(); + } catch (error) { + setIsSubmitting(false); + notifications.toasts.addError(error, { + title: i18n.translate('xpack.ingestManager.unenrollAgents.fatalErrorNotificationTitle', { + defaultMessage: 'Error unenrolling {count, plural, one {agent} other {agents}}', + values: { count: agentCount }, + }), + }); + } + } + + return ( + + + ) : ( + + ) + } + onCancel={onClose} + onConfirm={onSubmit} + cancelButtonText={ + + } + confirmButtonDisabled={isSubmitting} + confirmButtonText={ + isSingleAgent ? ( + + ) : ( + + ) + } + buttonColor="danger" + > +

+ {isSingleAgent ? ( + + ) : ( + + )} +

+ + ), + }} + > + + } + checked={forceUnenroll} + onChange={(e) => setForceUnenroll(e.target.checked)} + disabled={useForceUnenroll} + /> + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx deleted file mode 100644 index 6f1cba70bbcee..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_unenroll_provider.tsx +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Fragment, useRef, useState } from 'react'; -import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useCore, sendRequest } from '../../../hooks'; -import { PostAgentUnenrollResponse } from '../../../types'; -import { agentRouteService } from '../../../services'; - -interface Props { - children: (unenrollAgents: UnenrollAgents) => React.ReactElement; - forceUnenroll?: boolean; -} - -export type UnenrollAgents = ( - agents: string[] | string, - agentsCount: number, - onSuccess?: OnSuccessCallback -) => void; - -type OnSuccessCallback = (agentsUnenrolled: string[]) => void; - -export const AgentUnenrollProvider: React.FunctionComponent = ({ - children, - forceUnenroll = false, -}) => { - const core = useCore(); - const [agents, setAgents] = useState([]); - const [agentsCount, setAgentsCount] = useState(0); - const [isModalOpen, setIsModalOpen] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const onSuccessCallback = useRef(null); - - const unenrollAgentsPrompt: UnenrollAgents = ( - agentsToUnenroll, - agentsToUnenrollCount, - onSuccess = () => undefined - ) => { - if ( - agentsToUnenroll === undefined || - // !Only supports unenrolling one agent - (Array.isArray(agentsToUnenroll) && agentsToUnenroll.length !== 1) - ) { - throw new Error('No agents specified for unenrollment'); - } - setIsModalOpen(true); - setAgents(agentsToUnenroll); - setAgentsCount(agentsToUnenrollCount); - onSuccessCallback.current = onSuccess; - }; - - const closeModal = () => { - setAgents([]); - setAgentsCount(0); - setIsLoading(false); - setIsModalOpen(false); - }; - - const unenrollAgents = async () => { - setIsLoading(true); - - try { - const agentId = agents[0]; - const { error } = await sendRequest({ - path: agentRouteService.getUnenrollPath(agentId), - method: 'post', - body: { - force: forceUnenroll, - }, - }); - - if (error) { - throw new Error(error.message); - } - - const successMessage = forceUnenroll - ? i18n.translate('xpack.ingestManager.unenrollAgents.successForceSingleNotificationTitle', { - defaultMessage: "Agent '{id}' unenrolled", - values: { id: agentId }, - }) - : i18n.translate('xpack.ingestManager.unenrollAgents.successSingleNotificationTitle', { - defaultMessage: "Unenrolling agent '{id}'", - values: { id: agentId }, - }); - core.notifications.toasts.addSuccess(successMessage); - - if (onSuccessCallback.current) { - onSuccessCallback.current([agentId]); - } - } catch (e) { - core.notifications.toasts.addDanger( - i18n.translate('xpack.ingestManager.unenrollAgents.fatalErrorNotificationTitle', { - defaultMessage: 'Error unenrolling agents', - }) - ); - } - - closeModal(); - }; - - const renderModal = () => { - if (!isModalOpen) { - return null; - } - - const unenrollByKuery = typeof agents === 'string'; - const isSingle = agentsCount === 1; - - return ( - - - ) : ( - - ) - ) : ( - - ) - } - onCancel={closeModal} - onConfirm={unenrollAgents} - cancelButtonText={ - - } - confirmButtonText={ - isLoading ? ( - - ) : ( - - ) - } - buttonColor="danger" - confirmButtonDisabled={isLoading} - /> - - ); - }; - - return ( - - {children(unenrollAgentsPrompt)} - {renderModal()} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx index 527f920f24365..eea4ed3b712b1 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx @@ -8,4 +8,4 @@ export * from './loading'; export * from './agent_reassign_policy_flyout'; export * from './agent_enrollment_flyout'; export * from './agent_health'; -export * from './agent_unenroll_provider'; +export * from './agent_unenroll_modal'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx index b01dbbd57c16f..278beb5dfe35f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx @@ -126,7 +126,7 @@ export const ListLayout: React.FunctionComponent<{}> = ({ children }) => {

- +

@@ -134,7 +134,7 @@ export const ListLayout: React.FunctionComponent<{}> = ({ children }) => {

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_policy_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_policy_section.tsx index 617be92b3b1fe..e54eff1cbd4a5 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_policy_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_policy_section.tsx @@ -15,6 +15,7 @@ import { } from '@elastic/eui'; import { OverviewPanel } from './overview_panel'; import { OverviewStats } from './overview_stats'; +import { SO_SEARCH_LIMIT } from '../../../constants'; import { useLink, useGetPackagePolicies } from '../../../hooks'; import { AgentPolicy } from '../../../types'; import { Loading } from '../../fleet/components'; @@ -25,7 +26,7 @@ export const OverviewPolicySection: React.FC<{ agentPolicies: AgentPolicy[] }> = const { getHref } = useLink(); const packagePoliciesRequest = useGetPackagePolicies({ page: 1, - perPage: 10000, + perPage: SO_SEARCH_LIMIT, }); return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx index d7b08bf5ffa3a..482105cdea300 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx @@ -25,8 +25,8 @@ export const OverviewAgentSection = () => { return ( {

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts index 2c9e8b84d4069..ed6ba5c891a0b 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts @@ -25,4 +25,5 @@ export { isPackageLimited, doesAgentPolicyAlreadyIncludePackage, isValidNamespace, + LicenseService, } from '../../../../common'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 30a6742af6ea6..71a44089b8bf7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -49,13 +49,18 @@ export { GetAgentsResponse, GetAgentsRequest, GetOneAgentResponse, + PostAgentUnenrollRequest, PostAgentUnenrollResponse, + PostBulkAgentUnenrollRequest, + PostBulkAgentUnenrollResponse, GetOneAgentEventsRequest, GetOneAgentEventsResponse, GetAgentStatusRequest, GetAgentStatusResponse, PutAgentReassignRequest, PutAgentReassignResponse, + PostBulkAgentReassignRequest, + PostBulkAgentReassignResponse, // API schemas - Enrollment API Keys GetEnrollmentAPIKeysResponse, GetEnrollmentAPIKeysRequest, diff --git a/x-pack/plugins/ingest_manager/public/plugin.ts b/x-pack/plugins/ingest_manager/public/plugin.ts index 536832cdaed64..59741ce79dd8b 100644 --- a/x-pack/plugins/ingest_manager/public/plugin.ts +++ b/x-pack/plugins/ingest_manager/public/plugin.ts @@ -23,7 +23,7 @@ import { BASE_PATH } from './applications/ingest_manager/constants'; import { IngestManagerConfigType } from '../common/types'; import { setupRouteService, appRoutesService } from '../common'; -import { setHttpClient } from './applications/ingest_manager/hooks'; +import { setHttpClient, licenseService } from './applications/ingest_manager/hooks'; import { TutorialDirectoryNotice, TutorialDirectoryHeaderLink, @@ -71,11 +71,14 @@ export class IngestManagerPlugin // Set up http client setHttpClient(core.http); + // Set up license service + licenseService.start(deps.licensing.license$); + // Register main Ingest Manager app core.application.register({ id: PLUGIN_ID, category: DEFAULT_APP_CATEGORIES.management, - title: i18n.translate('xpack.ingestManager.appTitle', { defaultMessage: 'Ingest Manager' }), + title: i18n.translate('xpack.ingestManager.appTitle', { defaultMessage: 'Fleet' }), order: 9020, euiIconType: 'logoElastic', async mount(params: AppMountParameters) { diff --git a/x-pack/plugins/ingest_manager/server/collectors/register.ts b/x-pack/plugins/ingest_manager/server/collectors/register.ts index 2be8eb22bc98c..cb39e6a5be579 100644 --- a/x-pack/plugins/ingest_manager/server/collectors/register.ts +++ b/x-pack/plugins/ingest_manager/server/collectors/register.ts @@ -50,9 +50,12 @@ export function registerIngestManagerUsageCollector( offline: { type: 'long' }, }, packages: { - name: { type: 'keyword' }, - version: { type: 'keyword' }, - enabled: { type: 'boolean' }, + type: 'array', + items: { + name: { type: 'keyword' }, + version: { type: 'keyword' }, + enabled: { type: 'boolean' }, + }, }, }, }); diff --git a/x-pack/plugins/ingest_manager/server/constants/index.ts b/x-pack/plugins/ingest_manager/server/constants/index.ts index d677b79bb46f8..3965e27da0542 100644 --- a/x-pack/plugins/ingest_manager/server/constants/index.ts +++ b/x-pack/plugins/ingest_manager/server/constants/index.ts @@ -31,6 +31,7 @@ export { SETTINGS_API_ROUTES, APP_API_ROUTES, // Saved object types + SO_SEARCH_LIMIT, AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_ACTION_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/server/errors/handlers.ts b/x-pack/plugins/ingest_manager/server/errors/handlers.ts index 9f776565cf262..b621f2dd29331 100644 --- a/x-pack/plugins/ingest_manager/server/errors/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/errors/handlers.ts @@ -56,10 +56,7 @@ const getHTTPResponseCode = (error: IngestManagerError): number => { return 400; // Bad Request }; -export const defaultIngestErrorHandler: IngestErrorHandler = async ({ - error, - response, -}: IngestErrorHandlerParams): Promise => { +export function ingestErrorToResponseOptions(error: IngestErrorHandlerParams['error']) { const logger = appContextService.getLogger(); if (isLegacyESClientError(error)) { // there was a problem communicating with ES (e.g. via `callCluster`) @@ -72,36 +69,44 @@ export const defaultIngestErrorHandler: IngestErrorHandler = async ({ logger.error(message); - return response.customError({ + return { statusCode: error?.statusCode || error.status, body: { message }, - }); + }; } // our "expected" errors if (error instanceof IngestManagerError) { // only log the message logger.error(error.message); - return response.customError({ + return { statusCode: getHTTPResponseCode(error), body: { message: error.message }, - }); + }; } // handle any older Boom-based errors or the few places our app uses them if (isBoom(error)) { // only log the message logger.error(error.output.payload.message); - return response.customError({ + return { statusCode: error.output.statusCode, body: { message: error.output.payload.message }, - }); + }; } // not sure what type of error this is. log as much as possible logger.error(error); - return response.customError({ + return { statusCode: 500, body: { message: error.message }, - }); + }; +} + +export const defaultIngestErrorHandler: IngestErrorHandler = async ({ + error, + response, +}: IngestErrorHandlerParams): Promise => { + const options = ingestErrorToResponseOptions(error); + return response.customError(options); }; diff --git a/x-pack/plugins/ingest_manager/server/errors/index.ts b/x-pack/plugins/ingest_manager/server/errors/index.ts index 5e36a2ec9a884..f495bf551dcff 100644 --- a/x-pack/plugins/ingest_manager/server/errors/index.ts +++ b/x-pack/plugins/ingest_manager/server/errors/index.ts @@ -5,7 +5,7 @@ */ /* eslint-disable max-classes-per-file */ -export { defaultIngestErrorHandler } from './handlers'; +export { defaultIngestErrorHandler, ingestErrorToResponseOptions } from './handlers'; export class IngestManagerError extends Error { constructor(message?: string) { diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts index 33b9dc617075b..3d7f5c4a17adb 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.test.ts @@ -73,7 +73,7 @@ describe('test acks handlers', () => { const ackService: AcksService = { acknowledgeAgentActions: jest.fn().mockReturnValueOnce([ { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', id: 'action1', }, ]), diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index 5445a46fbe2b4..4574bcc64d4ce 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -23,7 +23,7 @@ describe('test actions handlers schema', () => { it('validate that new agent actions schema is valid', async () => { expect( NewAgentActionSchema.validate({ - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', data: 'data', sent_at: '2020-03-14T19:45:02.620Z', }) @@ -53,7 +53,7 @@ describe('test actions handlers', () => { const postNewAgentActionRequest: PostNewAgentActionRequest = { body: { action: { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', data: 'data', sent_at: '2020-03-14T19:45:02.620Z', }, @@ -66,7 +66,7 @@ describe('test actions handlers', () => { const mockRequest = httpServerMock.createKibanaRequest(postNewAgentActionRequest); const agentAction = ({ - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', id: 'action1', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index 2ebb7a0667aab..fb867af513fdc 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -16,6 +16,7 @@ import { GetAgentStatusResponse, PutAgentReassignResponse, PostAgentEnrollRequest, + PostBulkAgentReassignResponse, } from '../../../common/types'; import { GetAgentsRequestSchema, @@ -26,11 +27,13 @@ import { PostAgentCheckinRequest, GetAgentStatusRequestSchema, PutAgentReassignRequestSchema, + PostBulkAgentReassignRequestSchema, } from '../../types'; +import { defaultIngestErrorHandler } from '../../errors'; +import { licenseService } from '../../services'; import * as AgentService from '../../services/agents'; import * as APIKeyService from '../../services/api_keys'; import { appContextService } from '../../services/app_context'; -import { defaultIngestErrorHandler } from '../../errors'; export const getAgentHandler: RequestHandler ({ @@ -245,6 +253,7 @@ export const getAgentsHandler: RequestHandler< status: AgentService.getAgentStatus(agent), })), total, + totalInactive, page, perPage, }; @@ -270,6 +279,47 @@ export const putAgentsReassignHandler: RequestHandler< } }; +export const postBulkAgentsReassignHandler: RequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + if (!licenseService.isGoldPlus()) { + return response.customError({ + statusCode: 403, + body: { message: 'Requires Gold license' }, + }); + } + + const soClient = context.core.savedObjects.client; + try { + // Reassign by array of IDs + const result = Array.isArray(request.body.agents) + ? await AgentService.reassignAgents( + soClient, + { agentIds: request.body.agents }, + request.body.policy_id + ) + : await AgentService.reassignAgents( + soClient, + { kuery: request.body.agents }, + request.body.policy_id + ); + const body: PostBulkAgentReassignResponse = result.saved_objects.reduce((acc, so) => { + return { + ...acc, + [so.id]: { + success: !so.error, + error: so.error || undefined, + }, + }; + }, {}); + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; + export const getAgentStatusForAgentPolicyHandler: RequestHandler< undefined, TypeOf diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index a2e5c742ad6b5..73ed276ba02e7 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -23,10 +23,13 @@ import { PostAgentAcksRequestParamsJSONSchema, PostAgentAcksRequestBodyJSONSchema, PostAgentUnenrollRequestSchema, + PostBulkAgentUnenrollRequestSchema, GetAgentStatusRequestSchema, PostNewAgentActionRequestSchema, PutAgentReassignRequestSchema, + PostBulkAgentReassignRequestSchema, PostAgentEnrollRequestBodyJSONSchema, + PostAgentUpgradeRequestSchema, } from '../../types'; import { getAgentsHandler, @@ -38,13 +41,15 @@ import { postAgentEnrollHandler, getAgentStatusForAgentPolicyHandler, putAgentsReassignHandler, + postBulkAgentsReassignHandler, } from './handlers'; import { postAgentAcksHandlerBuilder } from './acks_handlers'; import * as AgentService from '../../services/agents'; import { postNewAgentActionHandlerBuilder } from './actions_handlers'; import { appContextService } from '../../services'; -import { postAgentsUnenrollHandler } from './unenroll_handler'; +import { postAgentUnenrollHandler, postBulkAgentsUnenrollHandler } from './unenroll_handler'; import { IngestManagerConfigType } from '../..'; +import { postAgentUpgradeHandler } from './upgrade_handler'; const ajv = new Ajv({ coerceTypes: true, @@ -181,7 +186,7 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) validate: PostAgentUnenrollRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, - postAgentsUnenrollHandler + postAgentUnenrollHandler ); router.put( @@ -212,4 +217,32 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) }, getAgentStatusForAgentPolicyHandler ); + // upgrade agent + router.post( + { + path: AGENT_API_ROUTES.UPGRADE_PATTERN, + validate: PostAgentUpgradeRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + postAgentUpgradeHandler + ); + // Bulk reassign + router.post( + { + path: AGENT_API_ROUTES.BULK_REASSIGN_PATTERN, + validate: PostBulkAgentReassignRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + postBulkAgentsReassignHandler + ); + + // Bulk unenroll + router.post( + { + path: AGENT_API_ROUTES.BULK_UNENROLL_PATTERN, + validate: PostBulkAgentUnenrollRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + postBulkAgentsUnenrollHandler + ); }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts index fa200e912d625..861d7c45c6f0a 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts @@ -6,12 +6,13 @@ import { RequestHandler } from 'src/core/server'; import { TypeOf } from '@kbn/config-schema'; -import { PostAgentUnenrollResponse } from '../../../common/types'; -import { PostAgentUnenrollRequestSchema } from '../../types'; +import { PostAgentUnenrollResponse, PostBulkAgentUnenrollResponse } from '../../../common/types'; +import { PostAgentUnenrollRequestSchema, PostBulkAgentUnenrollRequestSchema } from '../../types'; +import { licenseService } from '../../services'; import * as AgentService from '../../services/agents'; import { defaultIngestErrorHandler } from '../../errors'; -export const postAgentsUnenrollHandler: RequestHandler< +export const postAgentUnenrollHandler: RequestHandler< TypeOf, undefined, TypeOf @@ -30,3 +31,32 @@ export const postAgentsUnenrollHandler: RequestHandler< return defaultIngestErrorHandler({ error, response }); } }; + +export const postBulkAgentsUnenrollHandler: RequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + if (!licenseService.isGoldPlus()) { + return response.customError({ + statusCode: 403, + body: { message: 'Requires Gold license' }, + }); + } + const soClient = context.core.savedObjects.client; + const unenrollAgents = + request.body?.force === true ? AgentService.forceUnenrollAgents : AgentService.unenrollAgents; + + try { + if (Array.isArray(request.body.agents)) { + await unenrollAgents(soClient, { agentIds: request.body.agents }); + } else { + await unenrollAgents(soClient, { kuery: request.body.agents }); + } + + const body: PostBulkAgentUnenrollResponse = {}; + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts new file mode 100644 index 0000000000000..e5d7a44c00768 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/routes/agent/upgrade_handler.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { RequestHandler } from 'src/core/server'; +import { TypeOf } from '@kbn/config-schema'; +import { PostAgentUpgradeResponse } from '../../../common/types'; +import { PostAgentUpgradeRequestSchema } from '../../types'; +import * as AgentService from '../../services/agents'; +import { appContextService } from '../../services'; +import { defaultIngestErrorHandler } from '../../errors'; + +export const postAgentUpgradeHandler: RequestHandler< + TypeOf, + undefined, + TypeOf +> = async (context, request, response) => { + const soClient = context.core.savedObjects.client; + const { version, source_uri: sourceUri } = request.body; + + // temporarily only allow upgrading to the same version as the installed kibana version + const kibanaVersion = appContextService.getKibanaVersion(); + if (kibanaVersion !== version) { + return response.customError({ + statusCode: 400, + body: { + message: `cannot upgrade agent to ${version} because it is different than the installed kibana version ${kibanaVersion}`, + }, + }); + } + + try { + await AgentService.sendUpgradeAgentAction({ + soClient, + agentId: request.params.agentId, + version, + sourceUri, + }); + + const body: PostAgentUpgradeResponse = {}; + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts index c40e0e4ac5c0b..c55979d187f9d 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts @@ -5,7 +5,6 @@ */ import { TypeOf } from '@kbn/config-schema'; import { RequestHandler, CustomHttpResponseOptions } from 'src/core/server'; -import { appContextService } from '../../services'; import { GetInfoResponse, InstallPackageResponse, @@ -14,6 +13,9 @@ import { GetCategoriesResponse, GetPackagesResponse, GetLimitedPackagesResponse, + BulkInstallPackageInfo, + BulkInstallPackagesResponse, + IBulkInstallPackageHTTPError, } from '../../../common'; import { GetCategoriesRequestSchema, @@ -23,20 +25,24 @@ import { InstallPackageFromRegistryRequestSchema, InstallPackageByUploadRequestSchema, DeletePackageRequestSchema, + BulkUpgradePackagesFromRegistryRequestSchema, } from '../../types'; import { + BulkInstallResponse, + bulkInstallPackages, getCategories, getPackages, getFile, getPackageInfo, + handleInstallPackageFailure, installPackage, + isBulkInstallError, removeInstallation, getLimitedPackages, getInstallationObject, } from '../../services/epm/packages'; -import { IngestManagerError, defaultIngestErrorHandler } from '../../errors'; +import { defaultIngestErrorHandler, ingestErrorToResponseOptions } from '../../errors'; import { splitPkgKey } from '../../services/epm/registry'; -import { getInstallType } from '../../services/epm/packages/install'; export const getCategoriesHandler: RequestHandler< undefined, @@ -136,13 +142,11 @@ export const installPackageFromRegistryHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const logger = appContextService.getLogger(); const savedObjectsClient = context.core.savedObjects.client; const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; const { pkgkey } = request.params; const { pkgName, pkgVersion } = splitPkgKey(pkgkey); const installedPkg = await getInstallationObject({ savedObjectsClient, pkgName }); - const installType = getInstallType({ pkgVersion, installedPkg }); try { const res = await installPackage({ savedObjectsClient, @@ -155,36 +159,54 @@ export const installPackageFromRegistryHandler: RequestHandler< }; return response.ok({ body }); } catch (e) { - // could have also done `return defaultIngestErrorHandler({ error: e, response })` at each of the returns, - // but doing it this way will log the outer/install errors before any inner/rollback errors const defaultResult = await defaultIngestErrorHandler({ error: e, response }); - if (e instanceof IngestManagerError) { - return defaultResult; - } + await handleInstallPackageFailure({ + savedObjectsClient, + error: e, + pkgName, + pkgVersion, + installedPkg, + callCluster, + }); - // if there is an unknown server error, uninstall any package assets or reinstall the previous version if update - try { - if (installType === 'install' || installType === 'reinstall') { - logger.error(`uninstalling ${pkgkey} after error installing`); - await removeInstallation({ savedObjectsClient, pkgkey, callCluster }); - } - if (installType === 'update') { - // @ts-ignore getInstallType ensures we have installedPkg - const prevVersion = `${pkgName}-${installedPkg.attributes.version}`; - logger.error(`rolling back to ${prevVersion} after error installing ${pkgkey}`); - await installPackage({ - savedObjectsClient, - pkgkey: prevVersion, - callCluster, - }); - } - } catch (error) { - logger.error(`failed to uninstall or rollback package after installation error ${error}`); - } return defaultResult; } }; +const bulkInstallServiceResponseToHttpEntry = ( + result: BulkInstallResponse +): BulkInstallPackageInfo | IBulkInstallPackageHTTPError => { + if (isBulkInstallError(result)) { + const { statusCode, body } = ingestErrorToResponseOptions(result.error); + return { + name: result.name, + statusCode, + error: body.message, + }; + } else { + return result; + } +}; + +export const bulkInstallPackagesFromRegistryHandler: RequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + const savedObjectsClient = context.core.savedObjects.client; + const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; + const bulkInstalledResponses = await bulkInstallPackages({ + savedObjectsClient, + callCluster, + packagesToUpgrade: request.body.packages, + }); + const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry); + const body: BulkInstallPackagesResponse = { + response: payload, + }; + return response.ok({ body }); +}; + export const installPackageByUploadHandler: RequestHandler< undefined, undefined, diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts index 9048652f0e8a9..eaf61335b5e06 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/index.ts @@ -14,6 +14,7 @@ import { installPackageFromRegistryHandler, installPackageByUploadHandler, deletePackageHandler, + bulkInstallPackagesFromRegistryHandler, } from './handlers'; import { GetCategoriesRequestSchema, @@ -23,6 +24,7 @@ import { InstallPackageFromRegistryRequestSchema, InstallPackageByUploadRequestSchema, DeletePackageRequestSchema, + BulkUpgradePackagesFromRegistryRequestSchema, } from '../../types'; const MAX_FILE_SIZE_BYTES = 104857600; // 100MB @@ -82,6 +84,15 @@ export const registerRoutes = (router: IRouter) => { installPackageFromRegistryHandler ); + router.post( + { + path: EPM_API_ROUTES.BULK_INSTALL_PATTERN, + validate: BulkUpgradePackagesFromRegistryRequestSchema, + options: { tags: [`access:${PLUGIN_ID}-all`] }, + }, + bulkInstallPackagesFromRegistryHandler + ); + router.post( { path: EPM_API_ROUTES.INSTALL_BY_UPLOAD_PATTERN, diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts index e86f7b24e2c78..b3a8c7390176f 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -24,6 +24,7 @@ import { migrateEnrollmentApiKeysToV7100, migratePackagePolicyToV7100, migrateSettingsToV7100, + migrateAgentActionToV7100, } from './migrations/to_v7_10_0'; /* @@ -68,6 +69,8 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { enrolled_at: { type: 'date' }, unenrolled_at: { type: 'date' }, unenrollment_started_at: { type: 'date' }, + upgraded_at: { type: 'date' }, + upgrade_started_at: { type: 'date' }, access_api_key_id: { type: 'keyword' }, version: { type: 'keyword' }, user_provided_metadata: { type: 'flattened' }, @@ -107,6 +110,9 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { created_at: { type: 'date' }, }, }, + migrations: { + '7.10.0': migrateAgentActionToV7100, + }, }, [AGENT_EVENT_SAVED_OBJECT_TYPE]: { name: AGENT_EVENT_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts index 5e36ce46c099b..53af5ae42e410 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts @@ -12,6 +12,7 @@ import { PackagePolicy, EnrollmentAPIKey, Settings, + AgentAction, } from '../../types'; export const migrateAgentToV7100: SavedObjectMigrationFn< @@ -92,3 +93,18 @@ export const migrateSettingsToV7100: SavedObjectMigrationFn< return settingsDoc; }; + +export const migrateAgentActionToV7100: SavedObjectMigrationFn = ( + agentActionDoc +) => { + // @ts-expect-error + if (agentActionDoc.attributes.type === 'CONFIG_CHANGE') { + agentActionDoc.attributes.type = 'POLICY_CHANGE'; + if (agentActionDoc.attributes.data?.config) { + agentActionDoc.attributes.data.policy = agentActionDoc.attributes.data.config; + delete agentActionDoc.attributes.data.config; + } + } + + return agentActionDoc; +}; diff --git a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts index 938cfb4351630..29821a530098c 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts @@ -26,6 +26,7 @@ import { packagePolicyService } from './package_policy'; import { outputService } from './output'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; import { getSettings } from './settings'; +import { normalizeKuery } from './saved_object'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; @@ -166,13 +167,7 @@ class AgentPolicyService { sortOrder, page, perPage, - // To ensure users don't need to know about SO data structure... - filter: kuery - ? kuery.replace( - new RegExp(`${SAVED_OBJECT_TYPE}\.`, 'g'), - `${SAVED_OBJECT_TYPE}.attributes.` - ) - : undefined, + filter: kuery ? normalizeKuery(SAVED_OBJECT_TYPE, kuery) : undefined, }); const agentPolicies = await Promise.all( @@ -404,8 +399,8 @@ class AgentPolicyService { }, []); await createAgentPolicyAction(soClient, { - type: 'CONFIG_CHANGE', - data: { config: policy } as any, + type: 'POLICY_CHANGE', + data: { policy }, ack_data: { packages }, created_at: new Date().toISOString(), policy_id: policy.id, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts index 866aa587b8a56..8bcf275fce6ac 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts @@ -28,7 +28,7 @@ describe('test agent acks services', () => { references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, attributes: { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', agent_id: 'id', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', @@ -57,11 +57,11 @@ describe('test agent acks services', () => { ); }); - it('should update config field on the agent if a policy change is acknowledged', async () => { + it('should update config field on the agent if a policy change is acknowledged with an agent without policy', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); const actionAttributes = { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', policy_id: 'policy1', policy_revision: 4, sent_at: '2020-03-14T19:45:02.620Z', @@ -116,6 +116,114 @@ describe('test agent acks services', () => { `); }); + it('should update config field on the agent if a policy change is acknowledged with a higher revision than the agent one', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + + const actionAttributes = { + type: 'POLICY_CHANGE', + policy_id: 'policy1', + policy_revision: 4, + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + ack_data: JSON.stringify({ packages: ['system'] }), + }; + + mockSavedObjectsClient.bulkGet.mockReturnValue( + Promise.resolve({ + saved_objects: [ + { + id: 'action2', + references: [], + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + attributes: actionAttributes, + }, + ], + } as SavedObjectsBulkResponse) + ); + + await acknowledgeAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + policy_id: 'policy1', + policy_revision: 3, + } as unknown) as Agent, + [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action2', + agent_id: 'id', + } as AgentEvent, + ] + ); + expect(mockSavedObjectsClient.bulkUpdate).toBeCalled(); + expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(1); + expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0][0]).toMatchInlineSnapshot(` + Object { + "attributes": Object { + "packages": Array [ + "system", + ], + "policy_revision": 4, + }, + "id": "id", + "type": "fleet-agents", + } + `); + }); + + it('should not update config field on the agent if a policy change is acknowledged with a lower revision than the agent one', async () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + + const actionAttributes = { + type: 'POLICY_CHANGE', + policy_id: 'policy1', + policy_revision: 4, + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + ack_data: JSON.stringify({ packages: ['system'] }), + }; + + mockSavedObjectsClient.bulkGet.mockReturnValue( + Promise.resolve({ + saved_objects: [ + { + id: 'action2', + references: [], + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + attributes: actionAttributes, + }, + ], + } as SavedObjectsBulkResponse) + ); + + await acknowledgeAgentActions( + mockSavedObjectsClient, + ({ + id: 'id', + type: AGENT_TYPE_PERMANENT, + policy_id: 'policy1', + policy_revision: 5, + } as unknown) as Agent, + [ + { + type: 'ACTION_RESULT', + subtype: 'CONFIG', + timestamp: '2019-01-04T14:32:03.36764-05:00', + action_id: 'action2', + agent_id: 'id', + } as AgentEvent, + ] + ); + expect(mockSavedObjectsClient.bulkUpdate).toBeCalled(); + expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(0); + }); + it('should not update config field on the agent if a policy change for an old revision is acknowledged', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); @@ -127,7 +235,7 @@ describe('test agent acks services', () => { references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, attributes: { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', created_at: '2020-03-14T19:45:02.620Z', @@ -211,7 +319,7 @@ describe('test agent acks services', () => { references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, attributes: { - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', agent_id: 'id', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index d29dfcec7ef30..a552caa12b95e 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -16,6 +16,7 @@ import { Agent, AgentAction, AgentPolicyAction, + AgentPolicyActionV7_9, AgentEvent, AgentEventSOAttributes, AgentSOAttributes, @@ -28,6 +29,7 @@ import { } from '../../constants'; import { getAgentActionByIds } from './actions'; import { forceUnenrollAgent } from './unenroll'; +import { ackAgentUpgraded } from './upgrade'; const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT']; @@ -80,6 +82,11 @@ export async function acknowledgeAgentActions( await forceUnenrollAgent(soClient, agent.id); } + const upgradeAction = actions.find((action) => action.type === 'UPGRADE'); + if (upgradeAction) { + await ackAgentUpgraded(soClient, upgradeAction); + } + const configChangeAction = getLatestConfigChangePolicyActionIfUpdated(agent, actions); await soClient.bulkUpdate([ @@ -126,29 +133,27 @@ async function fetchActionsUsingCache( return [...freshActions, ...actions]; } -function isAgentPolicyAction(action: AgentAction | AgentPolicyAction): action is AgentPolicyAction { +function isAgentPolicyAction( + action: AgentAction | AgentPolicyAction | AgentPolicyActionV7_9 +): action is AgentPolicyAction | AgentPolicyActionV7_9 { return (action as AgentPolicyAction).policy_id !== undefined; } function getLatestConfigChangePolicyActionIfUpdated( agent: Agent, - actions: Array -): AgentPolicyAction | null { - return actions.reduce((acc, action) => { + actions: Array +): AgentPolicyAction | AgentPolicyActionV7_9 | null { + return actions.reduce((acc, action) => { if ( !isAgentPolicyAction(action) || - action.type !== 'CONFIG_CHANGE' || + (action.type !== 'POLICY_CHANGE' && action.type !== 'CONFIG_CHANGE') || action.policy_id !== agent.policy_id || - (acc?.policy_revision ?? 0) < (agent.policy_revision || 0) + (action?.policy_revision ?? 0) < (agent.policy_revision || 0) ) { return acc; } - if (action.policy_revision > (acc?.policy_revision ?? 0)) { - return action; - } - - return acc; + return action; }, null); } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index bcb3fc7fdc7bd..8fde684aa38bf 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -15,7 +15,7 @@ describe('test agent actions services', () => { const newAgentAction: Omit = { agent_id: 'agentid', - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', data: { content: 'data' }, sent_at: '2020-03-14T19:45:02.620Z', created_at: '2020-03-14T19:45:02.620Z', @@ -24,7 +24,7 @@ describe('test agent actions services', () => { Promise.resolve({ attributes: { agent_id: 'agentid', - type: 'CONFIG_CHANGE', + type: 'POLICY_CHANGE', data: JSON.stringify({ content: 'data' }), sent_at: '2020-03-14T19:45:02.620Z', created_at: '2020-03-14T19:45:02.620Z', diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 254c2c8b21e32..f018eea61e4f3 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -29,12 +29,20 @@ export async function createAgentAction( return createAction(soClient, newAgentAction); } +export async function bulkCreateAgentActions( + soClient: SavedObjectsClientContract, + newAgentActions: Array> +): Promise { + return bulkCreateActions(soClient, newAgentActions); +} + export function createAgentPolicyAction( soClient: SavedObjectsClientContract, newAgentAction: Omit ): Promise { return createAction(soClient, newAgentAction); } + async function createAction( soClient: SavedObjectsClientContract, newAgentAction: Omit @@ -47,19 +55,25 @@ async function createAction( soClient: SavedObjectsClientContract, newAgentAction: Omit | Omit ): Promise { - const so = await soClient.create(AGENT_ACTION_SAVED_OBJECT_TYPE, { - ...newAgentAction, - data: newAgentAction.data ? JSON.stringify(newAgentAction.data) : undefined, - ack_data: newAgentAction.ack_data ? JSON.stringify(newAgentAction.ack_data) : undefined, - }); + const actionSO = await soClient.create( + AGENT_ACTION_SAVED_OBJECT_TYPE, + { + ...newAgentAction, + data: newAgentAction.data ? JSON.stringify(newAgentAction.data) : undefined, + ack_data: newAgentAction.ack_data ? JSON.stringify(newAgentAction.ack_data) : undefined, + } + ); - if (isAgentActionSavedObject(so)) { - const agentAction = savedObjectToAgentAction(so); + if (isAgentActionSavedObject(actionSO)) { + const agentAction = savedObjectToAgentAction(actionSO); + // Action `data` is encrypted, so is not returned from the saved object + // so we add back the original value from the request to form the expected + // response shape for POST create agent action endpoint agentAction.data = newAgentAction.data; return agentAction; - } else if (isPolicyActionSavedObject(so)) { - const agentAction = savedObjectToAgentAction(so); + } else if (isPolicyActionSavedObject(actionSO)) { + const agentAction = savedObjectToAgentAction(actionSO); agentAction.data = newAgentAction.data; return agentAction; @@ -67,6 +81,44 @@ async function createAction( throw new Error('Invalid action'); } +async function bulkCreateActions( + soClient: SavedObjectsClientContract, + newAgentActions: Array> +): Promise; +async function bulkCreateActions( + soClient: SavedObjectsClientContract, + newAgentActions: Array> +): Promise; +async function bulkCreateActions( + soClient: SavedObjectsClientContract, + newAgentActions: Array | Omit> +): Promise> { + const { saved_objects: actionSOs } = await soClient.bulkCreate( + newAgentActions.map((newAgentAction) => ({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + attributes: { + ...newAgentAction, + data: newAgentAction.data ? JSON.stringify(newAgentAction.data) : undefined, + ack_data: newAgentAction.ack_data ? JSON.stringify(newAgentAction.ack_data) : undefined, + }, + })) + ); + + return actionSOs.map((actionSO) => { + if (isAgentActionSavedObject(actionSO)) { + const agentAction = savedObjectToAgentAction(actionSO); + // Compared to single create (createAction()), we don't add back the + // original value of `agentAction.data` as this method isn't exposed + // via an HTTP endpoint + return agentAction; + } else if (isPolicyActionSavedObject(actionSO)) { + const agentAction = savedObjectToAgentAction(actionSO); + return agentAction; + } + throw new Error('Invalid action'); + }); +} + export async function getAgentActionsForCheckin( soClient: SavedObjectsClientContract, agentId: string @@ -173,7 +225,11 @@ export async function getAgentPolicyActionByIds( ); } -export async function getNewActionsSince(soClient: SavedObjectsClientContract, timestamp: string) { +export async function getNewActionsSince( + soClient: SavedObjectsClientContract, + timestamp: string, + decryptData: boolean = true +) { const filter = nodeTypes.function.buildNode('and', [ nodeTypes.function.buildNode( 'not', @@ -191,14 +247,33 @@ export async function getNewActionsSince(soClient: SavedObjectsClientContract, t } ), ]); - const res = await soClient.find({ - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - filter, - }); - return res.saved_objects + const actions = ( + await soClient.find({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + filter, + }) + ).saved_objects .filter(isAgentActionSavedObject) .map((so) => savedObjectToAgentAction(so)); + + if (!decryptData) { + return actions; + } + + return await Promise.all( + actions.map(async (action) => { + // Get decrypted actions + return savedObjectToAgentAction( + await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + AGENT_ACTION_SAVED_OBJECT_TYPE, + action.id + ) + ); + }) + ); } export async function getLatestConfigChangeAction( diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts new file mode 100644 index 0000000000000..5e84e3a50bb44 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.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 { TestScheduler } from 'rxjs/testing'; +import { createRateLimiter } from './rxjs_utils'; + +describe('createRateLimiter', () => { + it('should rate limit correctly with 1 request per 10ms', async () => { + const scheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + + scheduler.run(({ expectObservable, cold }) => { + const source = cold('a-b-c-d-e-f|'); + const rateLimiter = createRateLimiter(10, 1, 2, scheduler); + const obs = source.pipe(rateLimiter()); + const results = 'a 9ms b 9ms c 9ms d 9ms e 9ms (f|)'; + expectObservable(obs).toBe(results); + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts index dddade6841460..3bbfbbd4ec1bf 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts @@ -5,6 +5,7 @@ */ import * as Rx from 'rxjs'; +import { concatMap, delay } from 'rxjs/operators'; export class AbortError extends Error {} @@ -45,63 +46,35 @@ export const toPromiseAbortable = ( export function createRateLimiter( ratelimitIntervalMs: number, - ratelimitRequestPerInterval: number + ratelimitRequestPerInterval: number, + maxDelay: number, + scheduler = Rx.asyncScheduler ) { - function createCurrentInterval() { - return { - startedAt: Rx.asyncScheduler.now(), - numRequests: 0, - }; - } + let intervalEnd = 0; + let countInCurrentInterval = 0; - let currentInterval: { startedAt: number; numRequests: number } = createCurrentInterval(); - let observers: Array<[Rx.Subscriber, any]> = []; - let timerSubscription: Rx.Subscription | undefined; + function createRateLimitOperator(): Rx.OperatorFunction { + return Rx.pipe( + concatMap(function rateLimit(value: T) { + const now = scheduler.now(); + if (intervalEnd <= now) { + countInCurrentInterval = 1; + intervalEnd = now + ratelimitIntervalMs; + return Rx.of(value); + } else if (intervalEnd >= now + maxDelay) { + // re-rate limit in the future to avoid to schedule too far in the future as some observer can unsubscribe + return Rx.of(value).pipe(delay(maxDelay, scheduler), createRateLimitOperator()); + } else { + if (++countInCurrentInterval > ratelimitRequestPerInterval) { + countInCurrentInterval = 1; + intervalEnd += ratelimitIntervalMs; + } - function createTimeout() { - if (timerSubscription) { - return; - } - timerSubscription = Rx.asyncScheduler.schedule(() => { - timerSubscription = undefined; - currentInterval = createCurrentInterval(); - for (const [waitingObserver, value] of observers) { - if (currentInterval.numRequests >= ratelimitRequestPerInterval) { - createTimeout(); - continue; + const wait = intervalEnd - ratelimitIntervalMs - now; + return wait > 0 ? Rx.of(value).pipe(delay(wait, scheduler)) : Rx.of(value); } - currentInterval.numRequests++; - waitingObserver.next(value); - } - }, ratelimitIntervalMs); + }) + ); } - - return function limit(): Rx.MonoTypeOperatorFunction { - return (observable) => - new Rx.Observable((observer) => { - const subscription = observable.subscribe({ - next(value) { - if (currentInterval.numRequests < ratelimitRequestPerInterval) { - currentInterval.numRequests++; - observer.next(value); - return; - } - - observers = [...observers, [observer, value]]; - createTimeout(); - }, - error(err) { - observer.error(err); - }, - complete() { - observer.complete(); - }, - }); - - return () => { - observers = observers.filter((o) => o[0] !== observer); - subscription.unsubscribe(); - }; - }); - }; + return createRateLimitOperator; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.test.ts new file mode 100644 index 0000000000000..f4a2147131570 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.test.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { savedObjectsClientMock } from 'src/core/server/mocks'; +import { createAgentActionFromPolicyAction } from './state_new_actions'; +import { OutputType, Agent, AgentPolicyAction } from '../../../types'; + +jest.mock('../../app_context', () => ({ + appContextService: { + getEncryptedSavedObjects: () => ({ + getDecryptedAsInternalUser: () => ({ + attributes: { + default_api_key: 'MOCK_API_KEY', + }, + }), + }), + }, +})); + +describe('test agent checkin new action services', () => { + describe('createAgentActionFromPolicyAction()', () => { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockAgent: Agent = { + id: 'agent1', + active: true, + type: 'PERMANENT', + local_metadata: { elastic: { agent: { version: '7.10.0' } } }, + user_provided_metadata: {}, + current_error_events: [], + packages: [], + enrolled_at: '2020-03-14T19:45:02.620Z', + }; + const mockPolicyAction: AgentPolicyAction = { + id: 'action1', + type: 'POLICY_CHANGE', + policy_id: 'policy1', + policy_revision: 1, + sent_at: '2020-03-14T19:45:02.620Z', + created_at: '2020-03-14T19:45:02.620Z', + data: { + policy: { + id: 'policy1', + outputs: { + default: { + type: OutputType.Elasticsearch, + hosts: [], + ca_sha256: undefined, + api_key: undefined, + }, + }, + inputs: [], + }, + }, + }; + + it('should return POLICY_CHANGE and data.policy for agent version >= 7.10', async () => { + const expectedResult = [ + { + agent_id: 'agent1', + created_at: '2020-03-14T19:45:02.620Z', + data: { + policy: { + id: 'policy1', + inputs: [], + outputs: { default: { api_key: 'MOCK_API_KEY', hosts: [], type: 'elasticsearch' } }, + }, + }, + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + type: 'POLICY_CHANGE', + }, + ]; + + expect( + await createAgentActionFromPolicyAction(mockSavedObjectsClient, mockAgent, mockPolicyAction) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.10.0-SNAPSHOT' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.10.2' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '8.0.0' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '8.0.0-SNAPSHOT' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + }); + + it('should return CONNFIG_CHANGE and data.config for agent version <= 7.9', async () => { + const expectedResult = [ + { + agent_id: 'agent1', + created_at: '2020-03-14T19:45:02.620Z', + data: { + config: { + id: 'policy1', + inputs: [], + outputs: { default: { api_key: 'MOCK_API_KEY', hosts: [], type: 'elasticsearch' } }, + }, + }, + id: 'action1', + sent_at: '2020-03-14T19:45:02.620Z', + type: 'CONFIG_CHANGE', + }, + ]; + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.0' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.3' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.9.1-SNAPSHOT' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + + expect( + await createAgentActionFromPolicyAction( + mockSavedObjectsClient, + { ...mockAgent, local_metadata: { elastic: { agent: { version: '7.8.2' } } } }, + mockPolicyAction + ) + ).toEqual(expectedResult); + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts index 4122677a615ca..51ccdc8eb1c7c 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts @@ -3,21 +3,26 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import semver from 'semver'; import { timer, from, Observable, TimeoutError } from 'rxjs'; import { omit } from 'lodash'; import { shareReplay, distinctUntilKeyChanged, switchMap, - mergeMap, merge, filter, timeout, take, } from 'rxjs/operators'; import { SavedObjectsClientContract, KibanaRequest } from 'src/core/server'; -import { Agent, AgentAction, AgentPolicyAction, AgentSOAttributes } from '../../../types'; +import { + Agent, + AgentAction, + AgentPolicyAction, + AgentPolicyActionV7_9, + AgentSOAttributes, +} from '../../../types'; import * as APIKeysService from '../../api_keys'; import { AGENT_SAVED_OBJECT_TYPE, @@ -33,6 +38,8 @@ import { import { appContextService } from '../../app_context'; import { toPromiseAbortable, AbortError, createRateLimiter } from './rxjs_utils'; +const RATE_LIMIT_MAX_DELAY_MS = 5 * 60 * 1000; // 5 minutes + function getInternalUserSOClient() { const fakeRequest = ({ headers: {}, @@ -104,15 +111,40 @@ async function getOrCreateAgentDefaultOutputAPIKey( return outputAPIKey.key; } -async function createAgentActionFromPolicyAction( +export async function createAgentActionFromPolicyAction( soClient: SavedObjectsClientContract, agent: Agent, policyAction: AgentPolicyAction ) { + // Transform the policy action for agent version <= 7.9.x for BWC + const agentVersion = semver.parse((agent.local_metadata?.elastic as any)?.agent?.version); + const agentPolicyAction: AgentPolicyAction | AgentPolicyActionV7_9 = + agentVersion && + semver.lt( + agentVersion, + // A prerelease tag is added here so that agent versions with prerelease tags can be compared + // correctly using `semvar` + '7.10.0-SNAPSHOT', + // `@types/semvar` is out of date with the version of `semvar` we use and doesn't have a + // corresponding release version we can update the typing to :( so, the typing error is + // suppressed here even though it is supported by `semvar` + // @ts-expect-error + { includePrerelease: true } + ) + ? { + ...policyAction, + type: 'CONFIG_CHANGE', + data: { + config: policyAction.data.policy, + }, + } + : policyAction; + + // Create agent action const newAgentAction: AgentAction = Object.assign( omit( // Faster than clone - JSON.parse(JSON.stringify(policyAction)) as AgentPolicyAction, + JSON.parse(JSON.stringify(agentPolicyAction)) as AgentPolicyAction, 'policy_id', 'policy_revision' ), @@ -122,10 +154,14 @@ async function createAgentActionFromPolicyAction( ); // Mutate the policy to set the api token for this agent - newAgentAction.data.config.outputs.default.api_key = await getOrCreateAgentDefaultOutputAPIKey( - soClient, - agent - ); + const apiKey = await getOrCreateAgentDefaultOutputAPIKey(soClient, agent); + if (newAgentAction.data.policy) { + newAgentAction.data.policy.outputs.default.api_key = apiKey; + } + // BWC for agent <= 7.9 + else if (newAgentAction.data.config) { + newAgentAction.data.config.outputs.default.api_key = apiKey; + } return [newAgentAction]; } @@ -135,11 +171,19 @@ export function agentCheckinStateNewActionsFactory() { const agentPolicies$ = new Map>(); const newActions$ = createNewActionsSharedObservable(); // Rx operators - const rateLimiter = createRateLimiter( + const pollingTimeoutMs = appContextService.getConfig()?.fleet.pollingRequestTimeout ?? 0; + const rateLimiterIntervalMs = appContextService.getConfig()?.fleet.agentPolicyRolloutRateLimitIntervalMs ?? - AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS, + AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS; + const rateLimiterRequestPerInterval = appContextService.getConfig()?.fleet.agentPolicyRolloutRateLimitRequestPerInterval ?? - AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL + AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL; + const rateLimiterMaxDelay = Math.min(RATE_LIMIT_MAX_DELAY_MS, pollingTimeoutMs); + + const rateLimiter = createRateLimiter( + rateLimiterIntervalMs, + rateLimiterRequestPerInterval, + rateLimiterMaxDelay ); async function subscribeToNewActions( @@ -162,7 +206,7 @@ export function agentCheckinStateNewActionsFactory() { const stream$ = agentPolicy$.pipe( timeout( // Set a timeout 3s before the real timeout to have a chance to respond an empty response before socket timeout - Math.max((appContextService.getConfig()?.fleet.pollingRequestTimeout ?? 0) - 3000, 3000) + Math.max(pollingTimeoutMs - 3000, 3000) ), filter( (action) => @@ -173,9 +217,9 @@ export function agentCheckinStateNewActionsFactory() { (!agent.policy_revision || action.policy_revision > agent.policy_revision) ), rateLimiter(), - mergeMap((policyAction) => createAgentActionFromPolicyAction(soClient, agent, policyAction)), + switchMap((policyAction) => createAgentActionFromPolicyAction(soClient, agent, policyAction)), merge(newActions$), - mergeMap(async (data) => { + switchMap(async (data) => { if (!data) { return; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts index a57735e25ff7b..c941b0512e597 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts @@ -3,25 +3,37 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import Boom from 'boom'; import { SavedObjectsClientContract } from 'src/core/server'; -import { - AGENT_SAVED_OBJECT_TYPE, - AGENT_EVENT_SAVED_OBJECT_TYPE, - AGENT_TYPE_EPHEMERAL, - AGENT_POLLING_THRESHOLD_MS, -} from '../../constants'; +import { AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentSOAttributes, Agent, AgentEventSOAttributes, ListWithKuery } from '../../types'; +import { escapeSearchQueryPhrase, normalizeKuery, findAllSOs } from '../saved_object'; import { savedObjectToAgent } from './saved_objects'; -import { escapeSearchQueryPhrase } from '../saved_object'; + +const ACTIVE_AGENT_CONDITION = `${AGENT_SAVED_OBJECT_TYPE}.attributes.active:true`; +const INACTIVE_AGENT_CONDITION = `NOT (${ACTIVE_AGENT_CONDITION})`; + +function _joinFilters(filters: string[], operator = 'AND') { + return filters.reduce((acc: string | undefined, filter) => { + if (acc) { + return `${acc} ${operator} (${filter})`; + } + + return `(${filter})`; + }, undefined); +} export async function listAgents( soClient: SavedObjectsClientContract, options: ListWithKuery & { showInactive: boolean; } -) { +): Promise<{ + agents: Agent[]; + total: number; + page: number; + perPage: number; +}> { const { page = 1, perPage = 20, @@ -30,47 +42,86 @@ export async function listAgents( kuery, showInactive = false, } = options; - const filters = []; if (kuery && kuery !== '') { - // To ensure users dont need to know about SO data structure... - filters.push( - kuery.replace( - new RegExp(`${AGENT_SAVED_OBJECT_TYPE}\.`, 'g'), - `${AGENT_SAVED_OBJECT_TYPE}.attributes.` - ) - ); + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); } if (showInactive === false) { - const agentActiveCondition = `${AGENT_SAVED_OBJECT_TYPE}.attributes.active:true AND not ${AGENT_SAVED_OBJECT_TYPE}.attributes.type:${AGENT_TYPE_EPHEMERAL}`; - const recentlySeenEphemeralAgent = `${AGENT_SAVED_OBJECT_TYPE}.attributes.active:true AND ${AGENT_SAVED_OBJECT_TYPE}.attributes.type:${AGENT_TYPE_EPHEMERAL} AND ${AGENT_SAVED_OBJECT_TYPE}.attributes.last_checkin > ${ - Date.now() - 3 * AGENT_POLLING_THRESHOLD_MS - }`; - filters.push(`(${agentActiveCondition}) OR (${recentlySeenEphemeralAgent})`); + filters.push(ACTIVE_AGENT_CONDITION); } - // eslint-disable-next-line @typescript-eslint/naming-convention - const { saved_objects, total } = await soClient.find({ + const { saved_objects: agentSOs, total } = await soClient.find({ type: AGENT_SAVED_OBJECT_TYPE, + filter: _joinFilters(filters), sortField, sortOrder, page, perPage, - filter: _joinFilters(filters), }); - const agents: Agent[] = saved_objects.map(savedObjectToAgent); - return { - agents, + agents: agentSOs.map(savedObjectToAgent), total, page, perPage, }; } +export async function listAllAgents( + soClient: SavedObjectsClientContract, + options: Omit & { + showInactive: boolean; + } +): Promise<{ + agents: Agent[]; + total: number; +}> { + const { sortField = 'enrolled_at', sortOrder = 'desc', kuery, showInactive = false } = options; + const filters = []; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + if (showInactive === false) { + filters.push(ACTIVE_AGENT_CONDITION); + } + + const { saved_objects: agentSOs, total } = await findAllSOs(soClient, { + type: AGENT_SAVED_OBJECT_TYPE, + kuery: _joinFilters(filters), + sortField, + sortOrder, + }); + + return { + agents: agentSOs.map(savedObjectToAgent), + total, + }; +} + +export async function countInactiveAgents( + soClient: SavedObjectsClientContract, + options: Pick +): Promise { + const { kuery } = options; + const filters = [INACTIVE_AGENT_CONDITION]; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + const { total } = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + filter: _joinFilters(filters), + perPage: 0, + }); + + return total; +} + export async function getAgent(soClient: SavedObjectsClientContract, agentId: string) { const agent = savedObjectToAgent( await soClient.get(AGENT_SAVED_OBJECT_TYPE, agentId) @@ -78,6 +129,17 @@ export async function getAgent(soClient: SavedObjectsClientContract, agentId: st return agent; } +export async function getAgents(soClient: SavedObjectsClientContract, agentIds: string[]) { + const agentSOs = await soClient.bulkGet( + agentIds.map((agentId) => ({ + id: agentId, + type: AGENT_SAVED_OBJECT_TYPE, + })) + ); + const agents = agentSOs.saved_objects.map(savedObjectToAgent); + return agents; +} + export async function getAgentByAccessAPIKeyId( soClient: SavedObjectsClientContract, accessAPIKeyId: string @@ -142,13 +204,3 @@ export async function deleteAgent(soClient: SavedObjectsClientContract, agentId: active: false, }); } - -function _joinFilters(filters: string[], operator = 'AND') { - return filters.reduce((acc: string | undefined, filter) => { - if (acc) { - return `${acc} ${operator} (${filter})`; - } - - return `(${filter})`; - }, undefined); -} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/events.ts b/x-pack/plugins/ingest_manager/server/services/agents/events.ts index dfa599e4ffdfd..627fe4f231d3d 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/events.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/events.ts @@ -7,6 +7,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentEventSOAttributes, AgentEvent } from '../../types'; +import { normalizeKuery } from '../saved_object'; export async function getAgentEvents( soClient: SavedObjectsClientContract, @@ -23,12 +24,7 @@ export async function getAgentEvents( const { total, saved_objects } = await soClient.find({ type: AGENT_EVENT_SAVED_OBJECT_TYPE, filter: - kuery && kuery !== '' - ? kuery.replace( - new RegExp(`${AGENT_EVENT_SAVED_OBJECT_TYPE}\.`, 'g'), - `${AGENT_EVENT_SAVED_OBJECT_TYPE}.attributes.` - ) - : undefined, + kuery && kuery !== '' ? normalizeKuery(AGENT_EVENT_SAVED_OBJECT_TYPE, kuery) : undefined, perPage, page, sortField: 'timestamp', diff --git a/x-pack/plugins/ingest_manager/server/services/agents/index.ts b/x-pack/plugins/ingest_manager/server/services/agents/index.ts index 400c099af4e93..c878b666bde88 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/index.ts @@ -9,6 +9,7 @@ export * from './events'; export * from './checkin'; export * from './enroll'; export * from './unenroll'; +export * from './upgrade'; export * from './status'; export * from './crud'; export * from './update'; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts index 3075e146093e3..345c07511f032 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/reassign.ts @@ -9,6 +9,7 @@ import Boom from 'boom'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentSOAttributes } from '../../types'; import { agentPolicyService } from '../agent_policy'; +import { getAgents, listAllAgents } from './crud'; export async function reassignAgent( soClient: SavedObjectsClientContract, @@ -25,3 +26,44 @@ export async function reassignAgent( policy_revision: null, }); } + +export async function reassignAgents( + soClient: SavedObjectsClientContract, + options: + | { + agentIds: string[]; + } + | { + kuery: string; + }, + newAgentPolicyId: string +) { + const agentPolicy = await agentPolicyService.get(soClient, newAgentPolicyId); + if (!agentPolicy) { + throw Boom.notFound(`Agent policy not found: ${newAgentPolicyId}`); + } + + // Filter to agents that do not already use the new agent policy ID + const agents = + 'agentIds' in options + ? await getAgents(soClient, options.agentIds) + : ( + await listAllAgents(soClient, { + kuery: options.kuery, + showInactive: false, + }) + ).agents; + const agentsToUpdate = agents.filter((agent) => agent.policy_id !== newAgentPolicyId); + + // Update the necessary agents + return await soClient.bulkUpdate( + agentsToUpdate.map((agent) => ({ + type: AGENT_SAVED_OBJECT_TYPE, + id: agent.id, + attributes: { + policy_id: newAgentPolicyId, + policy_revision: null, + }, + })) + ); +} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts index e0ac2620cafd3..60533e1285141 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/unenroll.ts @@ -3,13 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { chunk } from 'lodash'; import { SavedObjectsClientContract } from 'src/core/server'; import { AgentSOAttributes } from '../../types'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { getAgent } from './crud'; import * as APIKeyService from '../api_keys'; -import { createAgentAction } from './actions'; +import { createAgentAction, bulkCreateAgentActions } from './actions'; +import { getAgents, listAllAgents } from './crud'; export async function unenrollAgent(soClient: SavedObjectsClientContract, agentId: string) { const now = new Date().toISOString(); @@ -23,6 +24,53 @@ export async function unenrollAgent(soClient: SavedObjectsClientContract, agentI }); } +export async function unenrollAgents( + soClient: SavedObjectsClientContract, + options: + | { + agentIds: string[]; + } + | { + kuery: string; + } +) { + // Filter to agents that do not already unenrolled, or unenrolling + const agents = + 'agentIds' in options + ? await getAgents(soClient, options.agentIds) + : ( + await listAllAgents(soClient, { + kuery: options.kuery, + showInactive: false, + }) + ).agents; + const agentsToUpdate = agents.filter( + (agent) => !agent.unenrollment_started_at && !agent.unenrolled_at + ); + const now = new Date().toISOString(); + + // Create unenroll action for each agent + await bulkCreateAgentActions( + soClient, + agentsToUpdate.map((agent) => ({ + agent_id: agent.id, + created_at: now, + type: 'UNENROLL', + })) + ); + + // Update the necessary agents + return await soClient.bulkUpdate( + agentsToUpdate.map((agent) => ({ + type: AGENT_SAVED_OBJECT_TYPE, + id: agent.id, + attributes: { + unenrollment_started_at: now, + }, + })) + ); +} + export async function forceUnenrollAgent(soClient: SavedObjectsClientContract, agentId: string) { const agent = await getAgent(soClient, agentId); @@ -40,3 +88,63 @@ export async function forceUnenrollAgent(soClient: SavedObjectsClientContract, a unenrolled_at: new Date().toISOString(), }); } + +export async function forceUnenrollAgents( + soClient: SavedObjectsClientContract, + options: + | { + agentIds: string[]; + } + | { + kuery: string; + } +) { + // Filter to agents that are not already unenrolled + const agents = + 'agentIds' in options + ? await getAgents(soClient, options.agentIds) + : ( + await listAllAgents(soClient, { + kuery: options.kuery, + showInactive: false, + }) + ).agents; + const agentsToUpdate = agents.filter((agent) => !agent.unenrolled_at); + const now = new Date().toISOString(); + const apiKeys: string[] = []; + + // Get all API keys that need to be invalidated + agentsToUpdate.forEach((agent) => { + if (agent.access_api_key_id) { + apiKeys.push(agent.access_api_key_id); + } + if (agent.default_api_key_id) { + apiKeys.push(agent.default_api_key_id); + } + }); + + // Invalidate all API keys + // ES doesn't provide a bulk invalidate API, so this could take a long time depending on + // number of keys to invalidate. We run these in batches to avoid overloading ES. + if (apiKeys.length) { + const BATCH_SIZE = 500; + const batches = chunk(apiKeys, BATCH_SIZE); + for (const apiKeysBatch of batches) { + await Promise.all( + apiKeysBatch.map((apiKey) => APIKeyService.invalidateAPIKey(soClient, apiKey)) + ); + } + } + + // Update the necessary agents + return await soClient.bulkUpdate( + agentsToUpdate.map((agent) => ({ + type: AGENT_SAVED_OBJECT_TYPE, + id: agent.id, + attributes: { + active: false, + unenrolled_at: now, + }, + })) + ); +} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts b/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts new file mode 100644 index 0000000000000..cee3bc69f25db --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/upgrade.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'src/core/server'; +import { AgentSOAttributes, AgentAction, AgentActionSOAttributes } from '../../types'; +import { AGENT_ACTION_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { createAgentAction } from './actions'; + +export async function sendUpgradeAgentAction({ + soClient, + agentId, + version, + sourceUri, +}: { + soClient: SavedObjectsClientContract; + agentId: string; + version: string; + sourceUri: string; +}) { + const now = new Date().toISOString(); + const data = { + version, + source_uri: sourceUri, + }; + await createAgentAction(soClient, { + agent_id: agentId, + created_at: now, + data, + ack_data: data, + type: 'UPGRADE', + }); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + upgraded_at: undefined, + upgrade_started_at: now, + }); +} + +export async function ackAgentUpgraded( + soClient: SavedObjectsClientContract, + agentAction: AgentAction +) { + const { + attributes: { ack_data: ackData }, + } = await soClient.get(AGENT_ACTION_SAVED_OBJECT_TYPE, agentAction.id); + if (!ackData) throw new Error('data missing from UPGRADE action'); + const { version } = JSON.parse(ackData); + if (!version) throw new Error('version missing from UPGRADE action'); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentAction.agent_id, { + upgraded_at: new Date().toISOString(), + local_metadata: { + elastic: { + agent: { + version, + }, + }, + }, + }); +} diff --git a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts index f058166fc2a4f..ea5d25dc9884f 100644 --- a/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/ingest_manager/server/services/api_keys/enrollment_api_key.ts @@ -12,6 +12,7 @@ import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; import { createAPIKey, invalidateAPIKey } from './security'; import { agentPolicyService } from '../agent_policy'; import { appContextService } from '../app_context'; +import { normalizeKuery } from '../saved_object'; export async function listEnrollmentApiKeys( soClient: SavedObjectsClientContract, @@ -33,10 +34,7 @@ export async function listEnrollmentApiKeys( sortOrder: 'desc', filter: kuery && kuery !== '' - ? kuery.replace( - new RegExp(`${ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE}\.`, 'g'), - `${ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE}.attributes.` - ) + ? normalizeKuery(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, kuery) : undefined, }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/install.ts index dfa03ec9d527d..d8aff10492595 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/install.ts @@ -9,7 +9,6 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { saveInstalledEsRefs } from '../../packages/install'; import * as Registry from '../../registry'; import { - Dataset, ElasticsearchAssetType, EsAssetReference, RegistryPackage, @@ -24,12 +23,7 @@ interface TransformInstallation { content: string; } -interface TransformPathDataset { - path: string; - dataset: Dataset; -} - -export const installTransformForDataset = async ( +export const installTransform = async ( registryPackage: RegistryPackage, paths: string[], callCluster: CallESAsCurrentUser, @@ -51,53 +45,32 @@ export const installTransformForDataset = async ( callCluster, previousInstalledTransformEsAssets.map((asset) => asset.id) ); - // install the latest dataset - const datasets = registryPackage.datasets; - if (!datasets?.length) return []; - const installNameSuffix = `${registryPackage.version}`; + const installNameSuffix = `${registryPackage.version}`; const transformPaths = paths.filter((path) => isTransform(path)); let installedTransforms: EsAssetReference[] = []; if (transformPaths.length > 0) { - const transformPathDatasets = datasets.reduce((acc, dataset) => { - transformPaths.forEach((path) => { - if (isDatasetTransform(path, dataset.path)) { - acc.push({ path, dataset }); - } + const transformRefs = transformPaths.reduce((acc, path) => { + acc.push({ + id: getTransformNameForInstallation(registryPackage, path, installNameSuffix), + type: ElasticsearchAssetType.transform, }); + return acc; }, []); - const transformRefs = transformPathDatasets.reduce( - (acc, transformPathDataset) => { - if (transformPathDataset) { - acc.push({ - id: getTransformNameForInstallation(transformPathDataset, installNameSuffix), - type: ElasticsearchAssetType.transform, - }); - } - return acc; - }, - [] - ); - // get and save transform refs before installing transforms await saveInstalledEsRefs(savedObjectsClient, registryPackage.name, transformRefs); - const transforms: TransformInstallation[] = transformPathDatasets.map( - (transformPathDataset: TransformPathDataset) => { - return { - installationName: getTransformNameForInstallation( - transformPathDataset, - installNameSuffix - ), - content: getAsset(transformPathDataset.path).toString('utf-8'), - }; - } - ); + const transforms: TransformInstallation[] = transformPaths.map((path: string) => { + return { + installationName: getTransformNameForInstallation(registryPackage, path, installNameSuffix), + content: getAsset(path).toString('utf-8'), + }; + }); const installationPromises = transforms.map(async (transform) => { - return installTransform({ callCluster, transform }); + return handleTransformInstall({ callCluster, transform }); }); installedTransforms = await Promise.all(installationPromises).then((results) => results.flat()); @@ -123,20 +96,10 @@ export const installTransformForDataset = async ( const isTransform = (path: string) => { const pathParts = Registry.pathParts(path); - return pathParts.type === ElasticsearchAssetType.transform; + return !path.endsWith('/') && pathParts.type === ElasticsearchAssetType.transform; }; -const isDatasetTransform = (path: string, datasetName: string) => { - const pathParts = Registry.pathParts(path); - return ( - !path.endsWith('/') && - pathParts.type === ElasticsearchAssetType.transform && - pathParts.dataset !== undefined && - datasetName === pathParts.dataset - ); -}; - -async function installTransform({ +async function handleTransformInstall({ callCluster, transform, }: { @@ -160,9 +123,12 @@ async function installTransform({ } const getTransformNameForInstallation = ( - transformDataset: TransformPathDataset, + registryPackage: RegistryPackage, + path: string, suffix: string ) => { - const filename = transformDataset?.path.split('/')?.pop()?.split('.')[0]; - return `${transformDataset.dataset.type}-${transformDataset.dataset.name}-${filename}-${suffix}`; + const pathPaths = path.split('/'); + const filename = pathPaths?.pop()?.split('.')[0]; + const folderName = pathPaths?.pop(); + return `${registryPackage.name}.${folderName}-${filename}-${suffix}`; }; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/remove.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/remove.ts index a527d05f1c49b..02d5dfc64d07d 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/remove.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/remove.ts @@ -25,6 +25,19 @@ export const deleteTransforms = async ( ) => { await Promise.all( transformIds.map(async (transformId) => { + // get the index the transform + const transformResponse: { + count: number; + transforms: Array<{ + dest: { + index: string; + }; + }>; + } = await callCluster('transport.request', { + method: 'GET', + path: `/_transform/${transformId}`, + }); + await stopTransforms([transformId], callCluster); await callCluster('transport.request', { method: 'DELETE', @@ -32,6 +45,15 @@ export const deleteTransforms = async ( path: `/_transform/${transformId}`, ignore: [404], }); + + // expect this to be 1 + for (const transform of transformResponse.transforms) { + await callCluster('transport.request', { + method: 'DELETE', + path: `/${transform?.dest?.index}`, + ignore: [404], + }); + } }) ); }; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts index c43a33df2db61..7cb507d15679e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts @@ -14,7 +14,7 @@ jest.mock('./common', () => { }; }); -import { installTransformForDataset } from './install'; +import { installTransform } from './install'; import { ILegacyScopedClusterClient, SavedObject, SavedObjectsClientContract } from 'kibana/server'; import { ElasticsearchAssetType, Installation, RegistryPackage } from '../../../../types'; import { getInstallation, getInstallationObject } from '../../packages'; @@ -47,7 +47,7 @@ describe('test transform install', () => { type: ElasticsearchAssetType.ingestPipeline, }, { - id: 'metrics-endpoint.metadata_current-default-0.15.0-dev.0', + id: 'endpoint.metadata_current-default-0.15.0-dev.0', type: ElasticsearchAssetType.transform, }, ], @@ -60,15 +60,15 @@ describe('test transform install', () => { type: ElasticsearchAssetType.ingestPipeline, }, { - id: 'metrics-endpoint.metadata_current-default-0.15.0-dev.0', + id: 'endpoint.metadata_current-default-0.15.0-dev.0', type: ElasticsearchAssetType.transform, }, { - id: 'metrics-endpoint.metadata_current-default-0.16.0-dev.0', + id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: ElasticsearchAssetType.transform, }, { - id: 'metrics-endpoint.metadata-default-0.16.0-dev.0', + id: 'endpoint.metadata-default-0.16.0-dev.0', type: ElasticsearchAssetType.transform, }, ], @@ -91,7 +91,26 @@ describe('test transform install', () => { } as unknown) as SavedObject) ); - await installTransformForDataset( + legacyScopedClusterClient.callAsCurrentUser.mockReturnValueOnce( + Promise.resolve({ + count: 1, + transforms: [ + { + dest: { + index: 'index', + }, + }, + ], + } as { + count: number; + transforms: Array<{ + dest: { + index: string; + }; + }>; + }) + ); + await installTransform( ({ name: 'endpoint', version: '0.16.0-dev.0', @@ -128,18 +147,26 @@ describe('test transform install', () => { } as unknown) as RegistryPackage, [ 'endpoint-0.16.0-dev.0/dataset/policy/elasticsearch/ingest_pipeline/default.json', - 'endpoint-0.16.0-dev.0/dataset/metadata/elasticsearch/transform/default.json', - 'endpoint-0.16.0-dev.0/dataset/metadata_current/elasticsearch/transform/default.json', + 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata/default.json', + 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json', ], legacyScopedClusterClient.callAsCurrentUser, savedObjectsClient ); + expect(legacyScopedClusterClient.callAsCurrentUser.mock.calls).toEqual([ + [ + 'transport.request', + { + method: 'GET', + path: '/_transform/endpoint.metadata_current-default-0.15.0-dev.0', + }, + ], [ 'transport.request', { method: 'POST', - path: '/_transform/metrics-endpoint.metadata_current-default-0.15.0-dev.0/_stop', + path: '/_transform/endpoint.metadata_current-default-0.15.0-dev.0/_stop', query: 'force=true', ignore: [404], }, @@ -149,7 +176,15 @@ describe('test transform install', () => { { method: 'DELETE', query: 'force=true', - path: '/_transform/metrics-endpoint.metadata_current-default-0.15.0-dev.0', + path: '/_transform/endpoint.metadata_current-default-0.15.0-dev.0', + ignore: [404], + }, + ], + [ + 'transport.request', + { + method: 'DELETE', + path: '/index', ignore: [404], }, ], @@ -157,7 +192,7 @@ describe('test transform install', () => { 'transport.request', { method: 'PUT', - path: '/_transform/metrics-endpoint.metadata-default-0.16.0-dev.0', + path: '/_transform/endpoint.metadata-default-0.16.0-dev.0', query: 'defer_validation=true', body: '{"content": "data"}', }, @@ -166,7 +201,7 @@ describe('test transform install', () => { 'transport.request', { method: 'PUT', - path: '/_transform/metrics-endpoint.metadata_current-default-0.16.0-dev.0', + path: '/_transform/endpoint.metadata_current-default-0.16.0-dev.0', query: 'defer_validation=true', body: '{"content": "data"}', }, @@ -175,14 +210,14 @@ describe('test transform install', () => { 'transport.request', { method: 'POST', - path: '/_transform/metrics-endpoint.metadata-default-0.16.0-dev.0/_start', + path: '/_transform/endpoint.metadata-default-0.16.0-dev.0/_start', }, ], [ 'transport.request', { method: 'POST', - path: '/_transform/metrics-endpoint.metadata_current-default-0.16.0-dev.0/_start', + path: '/_transform/endpoint.metadata_current-default-0.16.0-dev.0/_start', }, ], ]); @@ -198,15 +233,15 @@ describe('test transform install', () => { type: 'ingest_pipeline', }, { - id: 'metrics-endpoint.metadata_current-default-0.15.0-dev.0', + id: 'endpoint.metadata_current-default-0.15.0-dev.0', type: 'transform', }, { - id: 'metrics-endpoint.metadata-default-0.16.0-dev.0', + id: 'endpoint.metadata-default-0.16.0-dev.0', type: 'transform', }, { - id: 'metrics-endpoint.metadata_current-default-0.16.0-dev.0', + id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform', }, ], @@ -222,11 +257,11 @@ describe('test transform install', () => { type: 'ingest_pipeline', }, { - id: 'metrics-endpoint.metadata_current-default-0.16.0-dev.0', + id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform', }, { - id: 'metrics-endpoint.metadata-default-0.16.0-dev.0', + id: 'endpoint.metadata-default-0.16.0-dev.0', type: 'transform', }, ], @@ -263,7 +298,7 @@ describe('test transform install', () => { >) ); legacyScopedClusterClient.callAsCurrentUser = jest.fn(); - await installTransformForDataset( + await installTransform( ({ name: 'endpoint', version: '0.16.0-dev.0', @@ -284,7 +319,7 @@ describe('test transform install', () => { }, ], } as unknown) as RegistryPackage, - ['endpoint-0.16.0-dev.0/dataset/metadata_current/elasticsearch/transform/default.json'], + ['endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json'], legacyScopedClusterClient.callAsCurrentUser, savedObjectsClient ); @@ -294,7 +329,7 @@ describe('test transform install', () => { 'transport.request', { method: 'PUT', - path: '/_transform/metrics-endpoint.metadata_current-default-0.16.0-dev.0', + path: '/_transform/endpoint.metadata_current-default-0.16.0-dev.0', query: 'defer_validation=true', body: '{"content": "data"}', }, @@ -303,7 +338,7 @@ describe('test transform install', () => { 'transport.request', { method: 'POST', - path: '/_transform/metrics-endpoint.metadata_current-default-0.16.0-dev.0/_start', + path: '/_transform/endpoint.metadata_current-default-0.16.0-dev.0/_start', }, ], ]); @@ -313,7 +348,7 @@ describe('test transform install', () => { 'endpoint', { installed_es: [ - { id: 'metrics-endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform' }, + { id: 'endpoint.metadata_current-default-0.16.0-dev.0', type: 'transform' }, ], }, ], @@ -324,7 +359,7 @@ describe('test transform install', () => { const previousInstallation: Installation = ({ installed_es: [ { - id: 'metrics-endpoint.metadata-current-default-0.15.0-dev.0', + id: 'endpoint.metadata-current-default-0.15.0-dev.0', type: ElasticsearchAssetType.transform, }, ], @@ -346,7 +381,26 @@ describe('test transform install', () => { } as unknown) as SavedObject) ); - await installTransformForDataset( + legacyScopedClusterClient.callAsCurrentUser.mockReturnValueOnce( + Promise.resolve({ + count: 1, + transforms: [ + { + dest: { + index: 'index', + }, + }, + ], + } as { + count: number; + transforms: Array<{ + dest: { + index: string; + }; + }>; + }) + ); + await installTransform( ({ name: 'endpoint', version: '0.16.0-dev.0', @@ -387,11 +441,18 @@ describe('test transform install', () => { ); expect(legacyScopedClusterClient.callAsCurrentUser.mock.calls).toEqual([ + [ + 'transport.request', + { + method: 'GET', + path: '/_transform/endpoint.metadata-current-default-0.15.0-dev.0', + }, + ], [ 'transport.request', { method: 'POST', - path: '/_transform/metrics-endpoint.metadata-current-default-0.15.0-dev.0/_stop', + path: '/_transform/endpoint.metadata-current-default-0.15.0-dev.0/_stop', query: 'force=true', ignore: [404], }, @@ -401,7 +462,15 @@ describe('test transform install', () => { { method: 'DELETE', query: 'force=true', - path: '/_transform/metrics-endpoint.metadata-current-default-0.15.0-dev.0', + path: '/_transform/endpoint.metadata-current-default-0.15.0-dev.0', + ignore: [404], + }, + ], + [ + 'transport.request', + { + method: 'DELETE', + path: '/index', ignore: [404], }, ], diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/bulk_install_packages.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/bulk_install_packages.ts new file mode 100644 index 0000000000000..af937c5593082 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/bulk_install_packages.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsClientContract } from 'src/core/server'; +import { CallESAsCurrentUser } from '../../../types'; +import * as Registry from '../registry'; +import { getInstallationObject } from './index'; +import { BulkInstallResponse, IBulkInstallPackageError, upgradePackage } from './install'; + +interface BulkInstallPackagesParams { + savedObjectsClient: SavedObjectsClientContract; + packagesToUpgrade: string[]; + callCluster: CallESAsCurrentUser; +} + +export async function bulkInstallPackages({ + savedObjectsClient, + packagesToUpgrade, + callCluster, +}: BulkInstallPackagesParams): Promise { + const installedAndLatestPromises = packagesToUpgrade.map((pkgToUpgrade) => + Promise.all([ + getInstallationObject({ savedObjectsClient, pkgName: pkgToUpgrade }), + Registry.fetchFindLatestPackage(pkgToUpgrade), + ]) + ); + const installedAndLatestResults = await Promise.allSettled(installedAndLatestPromises); + const installResponsePromises = installedAndLatestResults.map(async (result, index) => { + const pkgToUpgrade = packagesToUpgrade[index]; + if (result.status === 'fulfilled') { + const [installedPkg, latestPkg] = result.value; + return upgradePackage({ + savedObjectsClient, + callCluster, + installedPkg, + latestPkg, + pkgToUpgrade, + }); + } else { + return { name: pkgToUpgrade, error: result.reason }; + } + }); + const installResults = await Promise.allSettled(installResponsePromises); + const installResponses = installResults.map((result, index) => { + const pkgToUpgrade = packagesToUpgrade[index]; + if (result.status === 'fulfilled') { + return result.value; + } else { + return { name: pkgToUpgrade, error: result.reason }; + } + }); + + return installResponses; +} + +export function isBulkInstallError(test: any): test is IBulkInstallPackageError { + return 'error' in test && test.error instanceof Error; +} diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/ensure_installed_default_packages.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/ensure_installed_default_packages.test.ts new file mode 100644 index 0000000000000..f0b487ad59774 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/ensure_installed_default_packages.test.ts @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types'; +import { SavedObject, SavedObjectsClientContract } from 'src/core/server'; + +jest.mock('./install'); +jest.mock('./bulk_install_packages'); +jest.mock('./get'); + +import { bulkInstallPackages, isBulkInstallError } from './bulk_install_packages'; +const { ensureInstalledDefaultPackages } = jest.requireActual('./install'); +const { isBulkInstallError: actualIsBulkInstallError } = jest.requireActual( + './bulk_install_packages' +); +import { getInstallation } from './get'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { appContextService } from '../../app_context'; +import { createAppContextStartContractMock } from '../../../mocks'; + +// if we add this assertion, TS will type check the return value +// and the editor will also know about .mockImplementation, .mock.calls, etc +const mockedBulkInstallPackages = bulkInstallPackages as jest.MockedFunction< + typeof bulkInstallPackages +>; +const mockedIsBulkInstallError = isBulkInstallError as jest.MockedFunction< + typeof isBulkInstallError +>; +const mockedGetInstallation = getInstallation as jest.MockedFunction; + +// I was unable to get the actual implementation set in the `jest.mock()` call at the top to work +// so this will set the `isBulkInstallError` function back to the actual implementation +mockedIsBulkInstallError.mockImplementation(actualIsBulkInstallError); + +const mockInstallation: SavedObject = { + id: 'test-pkg', + references: [], + type: 'epm-packages', + attributes: { + id: 'test-pkg', + installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }], + installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + es_index_patterns: { pattern: 'pattern-name' }, + name: 'test package', + version: '1.0.0', + install_status: 'installed', + install_version: '1.0.0', + install_started_at: new Date().toISOString(), + }, +}; + +describe('ensureInstalledDefaultPackages', () => { + let soClient: jest.Mocked; + beforeEach(async () => { + soClient = savedObjectsClientMock.create(); + appContextService.start(createAppContextStartContractMock()); + }); + afterEach(async () => { + appContextService.stop(); + }); + it('should return an array of Installation objects when successful', async () => { + mockedGetInstallation.mockImplementation(async () => { + return mockInstallation.attributes; + }); + mockedBulkInstallPackages.mockImplementationOnce(async function () { + return [ + { + name: mockInstallation.attributes.name, + assets: [], + newVersion: '', + oldVersion: '', + statusCode: 200, + }, + ]; + }); + const resp = await ensureInstalledDefaultPackages(soClient, jest.fn()); + expect(resp).toEqual([mockInstallation.attributes]); + }); + it('should throw the first Error it finds', async () => { + class SomeCustomError extends Error {} + mockedGetInstallation.mockImplementation(async () => { + return mockInstallation.attributes; + }); + mockedBulkInstallPackages.mockImplementationOnce(async function () { + return [ + { + name: 'success one', + assets: [], + newVersion: '', + oldVersion: '', + statusCode: 200, + }, + { + name: 'success two', + assets: [], + newVersion: '', + oldVersion: '', + statusCode: 200, + }, + { + name: 'failure one', + error: new SomeCustomError('abc 123'), + }, + { + name: 'success three', + assets: [], + newVersion: '', + oldVersion: '', + statusCode: 200, + }, + { + name: 'failure two', + error: new Error('zzz'), + }, + ]; + }); + const installPromise = ensureInstalledDefaultPackages(soClient, jest.fn()); + expect.assertions(2); + expect(installPromise).rejects.toThrow(SomeCustomError); + expect(installPromise).rejects.toThrow('abc 123'); + }); + it('should throw an error when get installation returns undefined', async () => { + mockedGetInstallation.mockImplementation(async () => { + return undefined; + }); + mockedBulkInstallPackages.mockImplementationOnce(async function () { + return [ + { + name: 'undefined package', + assets: [], + newVersion: '', + oldVersion: '', + statusCode: 200, + }, + ]; + }); + const installPromise = ensureInstalledDefaultPackages(soClient, jest.fn()); + expect.assertions(1); + expect(installPromise).rejects.toThrow(); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get_install_type.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get_install_type.test.ts new file mode 100644 index 0000000000000..cce4b7fee8fd7 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get_install_type.test.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { SavedObject } from 'src/core/server'; +import { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types'; +import { getInstallType } from './install'; + +const mockInstallation: SavedObject = { + id: 'test-pkg', + references: [], + type: 'epm-packages', + attributes: { + id: 'test-pkg', + installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }], + installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + es_index_patterns: { pattern: 'pattern-name' }, + name: 'test packagek', + version: '1.0.0', + install_status: 'installed', + install_version: '1.0.0', + install_started_at: new Date().toISOString(), + }, +}; +const mockInstallationUpdateFail: SavedObject = { + id: 'test-pkg', + references: [], + type: 'epm-packages', + attributes: { + id: 'test-pkg', + installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }], + installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], + es_index_patterns: { pattern: 'pattern-name' }, + name: 'test packagek', + version: '1.0.0', + install_status: 'installing', + install_version: '1.0.1', + install_started_at: new Date().toISOString(), + }, +}; + +describe('getInstallType', () => { + it('should return correct type when installing and no other version is currently installed', () => { + const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined }); + expect(installTypeInstall).toBe('install'); + + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'update').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'reinstall').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'reupdate').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'rollback').toBe(false); + }); + + it('should return correct type when installing the same version', () => { + const installTypeReinstall = getInstallType({ + pkgVersion: '1.0.0', + installedPkg: mockInstallation, + }); + expect(installTypeReinstall).toBe('reinstall'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeReinstall === 'install').toBe(false); + }); + + it('should return correct type when moving from one version to another', () => { + const installTypeUpdate = getInstallType({ + pkgVersion: '1.0.1', + installedPkg: mockInstallation, + }); + expect(installTypeUpdate).toBe('update'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeUpdate === 'install').toBe(false); + }); + + it('should return correct type when update fails and trys again', () => { + const installTypeReupdate = getInstallType({ + pkgVersion: '1.0.1', + installedPkg: mockInstallationUpdateFail, + }); + expect(installTypeReupdate).toBe('reupdate'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeReupdate === 'install').toBe(false); + }); + + it('should return correct type when attempting to rollback from a failed update', () => { + const installTypeRollback = getInstallType({ + pkgVersion: '1.0.0', + installedPkg: mockInstallationUpdateFail, + }); + expect(installTypeRollback).toBe('rollback'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeRollback === 'install').toBe(false); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts index 57c4f77432455..94aa969c2d2b8 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/index.ts @@ -12,6 +12,8 @@ import { InstallationStatus, KibanaAssetType, } from '../../../types'; + +export { bulkInstallPackages, isBulkInstallError } from './bulk_install_packages'; export { getCategories, getFile, @@ -23,7 +25,13 @@ export { SearchParams, } from './get'; -export { installPackage, ensureInstalledPackage } from './install'; +export { + BulkInstallResponse, + handleInstallPackageFailure, + installPackage, + IBulkInstallPackageError, + ensureInstalledPackage, +} from './install'; export { removeInstallation } from './remove'; type RequiredPackage = 'system' | 'endpoint'; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts deleted file mode 100644 index 2f60c74d3514f..0000000000000 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts +++ /dev/null @@ -1,103 +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 { ElasticsearchAssetType, Installation, KibanaAssetType } from '../../../types'; -import { SavedObject } from 'src/core/server'; -import { getInstallType } from './install'; - -const mockInstallation: SavedObject = { - id: 'test-pkg', - references: [], - type: 'epm-packages', - attributes: { - id: 'test-pkg', - installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }], - installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], - es_index_patterns: { pattern: 'pattern-name' }, - name: 'test packagek', - version: '1.0.0', - install_status: 'installed', - install_version: '1.0.0', - install_started_at: new Date().toISOString(), - }, -}; -const mockInstallationUpdateFail: SavedObject = { - id: 'test-pkg', - references: [], - type: 'epm-packages', - attributes: { - id: 'test-pkg', - installed_kibana: [{ type: KibanaAssetType.dashboard, id: 'dashboard-1' }], - installed_es: [{ type: ElasticsearchAssetType.ingestPipeline, id: 'pipeline' }], - es_index_patterns: { pattern: 'pattern-name' }, - name: 'test packagek', - version: '1.0.0', - install_status: 'installing', - install_version: '1.0.1', - install_started_at: new Date().toISOString(), - }, -}; -describe('install', () => { - describe('getInstallType', () => { - it('should return correct type when installing and no other version is currently installed', () => { - const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined }); - expect(installTypeInstall).toBe('install'); - - // @ts-expect-error can only be 'install' if no installedPkg given - expect(installTypeInstall === 'update').toBe(false); - // @ts-expect-error can only be 'install' if no installedPkg given - expect(installTypeInstall === 'reinstall').toBe(false); - // @ts-expect-error can only be 'install' if no installedPkg given - expect(installTypeInstall === 'reupdate').toBe(false); - // @ts-expect-error can only be 'install' if no installedPkg given - expect(installTypeInstall === 'rollback').toBe(false); - }); - - it('should return correct type when installing the same version', () => { - const installTypeReinstall = getInstallType({ - pkgVersion: '1.0.0', - installedPkg: mockInstallation, - }); - expect(installTypeReinstall).toBe('reinstall'); - - // @ts-expect-error cannot be 'install' if given installedPkg - expect(installTypeReinstall === 'install').toBe(false); - }); - - it('should return correct type when moving from one version to another', () => { - const installTypeUpdate = getInstallType({ - pkgVersion: '1.0.1', - installedPkg: mockInstallation, - }); - expect(installTypeUpdate).toBe('update'); - - // @ts-expect-error cannot be 'install' if given installedPkg - expect(installTypeUpdate === 'install').toBe(false); - }); - - it('should return correct type when update fails and trys again', () => { - const installTypeReupdate = getInstallType({ - pkgVersion: '1.0.1', - installedPkg: mockInstallationUpdateFail, - }); - expect(installTypeReupdate).toBe('reupdate'); - - // @ts-expect-error cannot be 'install' if given installedPkg - expect(installTypeReupdate === 'install').toBe(false); - }); - - it('should return correct type when attempting to rollback from a failed update', () => { - const installTypeRollback = getInstallType({ - pkgVersion: '1.0.0', - installedPkg: mockInstallationUpdateFail, - }); - expect(installTypeRollback).toBe('rollback'); - - // @ts-expect-error cannot be 'install' if given installedPkg - expect(installTypeRollback === 'install').toBe(false); - }); - }); -}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index 54b9c4d3fbb17..d501b05d96c1c 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -6,6 +6,9 @@ import { SavedObject, SavedObjectsClientContract } from 'src/core/server'; import semver from 'semver'; +import Boom from 'boom'; +import { UnwrapPromise } from '@kbn/utility-types'; +import { BulkInstallPackageInfo } from '../../../../common'; import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL } from '../../../constants'; import { AssetReference, @@ -20,7 +23,13 @@ import { } from '../../../types'; import { installIndexPatterns } from '../kibana/index_pattern/install'; import * as Registry from '../registry'; -import { getInstallation, getInstallationObject, isRequiredPackage } from './index'; +import { + getInstallation, + getInstallationObject, + isRequiredPackage, + bulkInstallPackages, + isBulkInstallError, +} from './index'; import { installTemplates } from '../elasticsearch/template/install'; import { generateESIndexPatterns } from '../elasticsearch/template/template'; import { installPipelines, deletePreviousPipelines } from '../elasticsearch/ingest_pipeline/'; @@ -32,10 +41,11 @@ import { ArchiveAsset, } from '../kibana/assets/install'; import { updateCurrentWriteIndices } from '../elasticsearch/template/template'; -import { deleteKibanaSavedObjectsAssets } from './remove'; -import { PackageOutdatedError } from '../../../errors'; +import { deleteKibanaSavedObjectsAssets, removeInstallation } from './remove'; +import { IngestManagerError, PackageOutdatedError } from '../../../errors'; import { getPackageSavedObjects } from './get'; -import { installTransformForDataset } from '../elasticsearch/transform/install'; +import { installTransform } from '../elasticsearch/transform/install'; +import { appContextService } from '../../app_context'; export async function installLatestPackage(options: { savedObjectsClient: SavedObjectsClientContract; @@ -60,17 +70,27 @@ export async function ensureInstalledDefaultPackages( callCluster: CallESAsCurrentUser ): Promise { const installations = []; - for (const pkgName in DefaultPackages) { - if (!DefaultPackages.hasOwnProperty(pkgName)) continue; - const installation = ensureInstalledPackage({ - savedObjectsClient, - pkgName, - callCluster, - }); - installations.push(installation); + const bulkResponse = await bulkInstallPackages({ + savedObjectsClient, + packagesToUpgrade: Object.values(DefaultPackages), + callCluster, + }); + + for (const resp of bulkResponse) { + if (isBulkInstallError(resp)) { + throw resp.error; + } else { + installations.push(getInstallation({ savedObjectsClient, pkgName: resp.name })); + } } - return Promise.all(installations); + const retrievedInstallations = await Promise.all(installations); + return retrievedInstallations.map((installation, index) => { + if (!installation) { + throw new Error(`could not get installation ${bulkResponse[index].name}`); + } + return installation; + }); } export async function ensureInstalledPackage(options: { @@ -94,17 +114,130 @@ export async function ensureInstalledPackage(options: { return installation; } -export async function installPackage({ +export async function handleInstallPackageFailure({ savedObjectsClient, - pkgkey, + error, + pkgName, + pkgVersion, + installedPkg, callCluster, - force = false, }: { + savedObjectsClient: SavedObjectsClientContract; + error: IngestManagerError | Boom | Error; + pkgName: string; + pkgVersion: string; + installedPkg: SavedObject | undefined; + callCluster: CallESAsCurrentUser; +}) { + if (error instanceof IngestManagerError) { + return; + } + const logger = appContextService.getLogger(); + const pkgkey = Registry.pkgToPkgKey({ + name: pkgName, + version: pkgVersion, + }); + + // if there is an unknown server error, uninstall any package assets or reinstall the previous version if update + try { + const installType = getInstallType({ pkgVersion, installedPkg }); + if (installType === 'install' || installType === 'reinstall') { + logger.error(`uninstalling ${pkgkey} after error installing`); + await removeInstallation({ savedObjectsClient, pkgkey, callCluster }); + } + + if (installType === 'update') { + if (!installedPkg) { + logger.error( + `failed to rollback package after installation error ${error} because saved object was undefined` + ); + return; + } + const prevVersion = `${pkgName}-${installedPkg.attributes.version}`; + logger.error(`rolling back to ${prevVersion} after error installing ${pkgkey}`); + await installPackage({ + savedObjectsClient, + pkgkey: prevVersion, + callCluster, + }); + } + } catch (e) { + logger.error(`failed to uninstall or rollback package after installation error ${e}`); + } +} + +export interface IBulkInstallPackageError { + name: string; + error: Error; +} +export type BulkInstallResponse = BulkInstallPackageInfo | IBulkInstallPackageError; + +interface UpgradePackageParams { + savedObjectsClient: SavedObjectsClientContract; + callCluster: CallESAsCurrentUser; + installedPkg: UnwrapPromise>; + latestPkg: UnwrapPromise>; + pkgToUpgrade: string; +} +export async function upgradePackage({ + savedObjectsClient, + callCluster, + installedPkg, + latestPkg, + pkgToUpgrade, +}: UpgradePackageParams): Promise { + if (!installedPkg || semver.gt(latestPkg.version, installedPkg.attributes.version)) { + const pkgkey = Registry.pkgToPkgKey({ + name: latestPkg.name, + version: latestPkg.version, + }); + + try { + const assets = await installPackage({ savedObjectsClient, pkgkey, callCluster }); + return { + name: pkgToUpgrade, + newVersion: latestPkg.version, + oldVersion: installedPkg?.attributes.version ?? null, + assets, + }; + } catch (installFailed) { + await handleInstallPackageFailure({ + savedObjectsClient, + error: installFailed, + pkgName: latestPkg.name, + pkgVersion: latestPkg.version, + installedPkg, + callCluster, + }); + return { name: pkgToUpgrade, error: installFailed }; + } + } else { + // package was already at the latest version + return { + name: pkgToUpgrade, + newVersion: latestPkg.version, + oldVersion: latestPkg.version, + assets: [ + ...installedPkg.attributes.installed_es, + ...installedPkg.attributes.installed_kibana, + ], + }; + } +} + +interface InstallPackageParams { savedObjectsClient: SavedObjectsClientContract; pkgkey: string; callCluster: CallESAsCurrentUser; force?: boolean; -}): Promise { +} + +export async function installPackage({ + savedObjectsClient, + pkgkey, + callCluster, + force = false, +}: InstallPackageParams): Promise { // TODO: change epm API to /packageName/version so we don't need to do this const { pkgName, pkgVersion } = Registry.splitPkgKey(pkgkey); // TODO: calls to getInstallationObject, Registry.fetchInfo, and Registry.fetchFindLatestPackge @@ -192,7 +325,7 @@ export async function installPackage({ // update current backing indices of each data stream await updateCurrentWriteIndices(callCluster, installedTemplates); - const installedTransforms = await installTransformForDataset( + const installedTransforms = await installTransform( registryPackageInfo, paths, callCluster, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts index b788d1bcbb4a9..6618220a27085 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts @@ -29,9 +29,8 @@ const getDefaultRegistryUrl = (): string => { }; export const getRegistryUrl = (): string => { - const license = licenseService.getLicenseInformation(); const customUrl = appContextService.getConfig()?.registryUrl; - const isGoldPlus = license?.isAvailable && license?.isActive && license?.hasAtLeast('gold'); + const isGoldPlus = licenseService.isGoldPlus(); if (customUrl && isGoldPlus) { return customUrl; diff --git a/x-pack/plugins/ingest_manager/server/services/index.ts b/x-pack/plugins/ingest_manager/server/services/index.ts index 5942277e90824..7a62c307973c2 100644 --- a/x-pack/plugins/ingest_manager/server/services/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/index.ts @@ -7,6 +7,7 @@ import { SavedObjectsClientContract, KibanaRequest } from 'kibana/server'; import { AgentStatus, Agent, EsAssetReference } from '../types'; import * as settingsService from './settings'; +import { getAgent, listAgents } from './agents'; export { ESIndexPatternSavedObjectService } from './es_index_pattern'; export { getRegistryUrl } from './epm/registry/registry_url'; @@ -40,7 +41,7 @@ export interface AgentService { /** * Get an Agent by id */ - getAgent(soClient: SavedObjectsClientContract, agentId: string): Promise; + getAgent: typeof getAgent; /** * Authenticate an agent with access toekn */ @@ -55,20 +56,7 @@ export interface AgentService { /** * List agents */ - listAgents( - soClient: SavedObjectsClientContract, - options: { - page: number; - perPage: number; - kuery?: string; - showInactive: boolean; - } - ): Promise<{ - agents: Agent[]; - total: number; - page: number; - perPage: number; - }>; + listAgents: typeof listAgents; } // Saved object services diff --git a/x-pack/plugins/ingest_manager/server/services/license.ts b/x-pack/plugins/ingest_manager/server/services/license.ts index bd96dbc7e3aff..a67ec9880ec09 100644 --- a/x-pack/plugins/ingest_manager/server/services/license.ts +++ b/x-pack/plugins/ingest_manager/server/services/license.ts @@ -3,36 +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 { Observable, Subscription } from 'rxjs'; -import { ILicense } from '../../../licensing/server'; - -class LicenseService { - private observable: Observable | null = null; - private subscription: Subscription | null = null; - private licenseInformation: ILicense | null = null; - - private updateInformation(licenseInformation: ILicense) { - this.licenseInformation = licenseInformation; - } - - public start(license$: Observable) { - this.observable = license$; - this.subscription = this.observable.subscribe(this.updateInformation.bind(this)); - } - - public stop() { - if (this.subscription) { - this.subscription.unsubscribe(); - } - } - - public getLicenseInformation() { - return this.licenseInformation; - } - - public getLicenseInformation$() { - return this.observable; - } -} +import { LicenseService } from '../../common'; export const licenseService = new LicenseService(); diff --git a/x-pack/plugins/ingest_manager/server/services/package_policy.ts b/x-pack/plugins/ingest_manager/server/services/package_policy.ts index b7e1806979db8..3a02544250ff0 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_policy.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_policy.ts @@ -30,6 +30,7 @@ import * as Registry from './epm/registry'; import { getPackageInfo, getInstallation, ensureInstalledPackage } from './epm/packages'; import { getAssetsData } from './epm/packages/assets'; import { createStream } from './epm/agent/agent'; +import { normalizeKuery } from './saved_object'; const SAVED_OBJECT_TYPE = PACKAGE_POLICY_SAVED_OBJECT_TYPE; @@ -211,13 +212,7 @@ class PackagePolicyService { sortOrder, page, perPage, - // To ensure users don't need to know about SO data structure... - filter: kuery - ? kuery.replace( - new RegExp(`${SAVED_OBJECT_TYPE}\.`, 'g'), - `${SAVED_OBJECT_TYPE}.attributes.` - ) - : undefined, + filter: kuery ? normalizeKuery(SAVED_OBJECT_TYPE, kuery) : undefined, }); return { diff --git a/x-pack/plugins/ingest_manager/server/services/saved_object.ts b/x-pack/plugins/ingest_manager/server/services/saved_object.ts index 8fe7ffcdfc896..77c0e446d5c23 100644 --- a/x-pack/plugins/ingest_manager/server/services/saved_object.ts +++ b/x-pack/plugins/ingest_manager/server/services/saved_object.ts @@ -3,6 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { SavedObjectsClientContract, SavedObjectsFindResponse } from 'src/core/server'; +import { SO_SEARCH_LIMIT } from '../constants'; +import { ListWithKuery } from '../types'; /** * Escape a value with double quote to use with saved object search @@ -12,3 +15,64 @@ export function escapeSearchQueryPhrase(val: string): string { return `"${val.replace(/["]/g, '"')}"`; } + +// Adds `.attribute` to any kuery strings that are missing it, this comes from +// internal SO structure. Kuery strings that come from UI will typicall have +// `.attribute` hidden to simplify UX, so this normalizes any kuery string for +// filtering SOs +export const normalizeKuery = (savedObjectType: string, kuery: string): string => { + return kuery.replace( + new RegExp(`${savedObjectType}\.(?!attributes\.)`, 'g'), + `${savedObjectType}.attributes.` + ); +}; + +// Like saved object client `.find()`, but ignores `page` and `perPage` parameters and +// returns *all* matching saved objects by collocating results from all `.find` pages. +// This function actually doesn't offer any additional benefits over `.find()` for now +// due to SO client limitations (see comments below), so is a placeholder for when SO +// client is improved. +export const findAllSOs = async ( + soClient: SavedObjectsClientContract, + options: Omit & { + type: string; + } +): Promise, 'saved_objects' | 'total'>> => { + const { type, sortField, sortOrder, kuery } = options; + let savedObjectResults: SavedObjectsFindResponse['saved_objects'] = []; + + const query = { + type, + sortField, + sortOrder, + filter: kuery, + page: 1, + perPage: SO_SEARCH_LIMIT, + }; + + const { saved_objects: initialSOs, total } = await soClient.find(query); + + savedObjectResults = initialSOs; + + // The saved object client can't actually page through more than the first 10,000 + // results, due to the same `index.max_result_window` constraint. The commented out + // code below is an example of paging through rest of results when the SO client + // offers that kind of support. + // if (total > searchLimit) { + // const remainingPages = Math.ceil((total - searchLimit) / searchLimit); + // for (let currentPage = 2; currentPage <= remainingPages + 1; currentPage++) { + // const { saved_objects: currentPageSavedObjects } = await soClient.find({ + // ...query, + // page: currentPage, + // }); + // if (currentPageSavedObjects.length) { + // savedObjectResults = savedObjectResults.concat(currentPageSavedObjects); + // } + // } + // } + + return { + saved_objects: savedObjectResults, + total, + }; +}; diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index f02057bae1598..c7ecf843d6a51 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -22,6 +22,7 @@ import { Output, DEFAULT_AGENT_POLICIES_PACKAGES, } from '../../common'; +import { SO_SEARCH_LIMIT } from '../constants'; import { getPackageInfo } from './epm/packages'; import { packagePolicyService } from './package_policy'; import { generateEnrollmentAPIKey } from './api_keys'; @@ -159,7 +160,7 @@ export async function setupFleet( }); const { items: agentPolicies } = await agentPolicyService.list(soClient, { - perPage: 10000, + perPage: SO_SEARCH_LIMIT, }); await Promise.all( diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index d00491afef72b..b43d6355c479a 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -17,6 +17,7 @@ export { AgentEventSOAttributes, AgentAction, AgentPolicyAction, + AgentPolicyActionV7_9, BaseAgentActionSOAttributes, AgentActionSOAttributes, AgentPolicyActionSOAttributes, diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index b249705fe6c2f..87e9257b7189c 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -62,7 +62,12 @@ export const AgentEventSchema = schema.object({ }); export const NewAgentActionSchema = schema.object({ - type: schema.oneOf([schema.literal('CONFIG_CHANGE'), schema.literal('UNENROLL')]), + type: schema.oneOf([ + schema.literal('POLICY_CHANGE'), + schema.literal('UNENROLL'), + schema.literal('UPGRADE'), + ]), data: schema.maybe(schema.any()), + ack_data: schema.maybe(schema.any()), sent_at: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts b/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts index c23918210114e..6673c12d51511 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/package_policy.ts @@ -9,8 +9,9 @@ import { isValidNamespace } from '../../../common'; export const NamespaceSchema = schema.string({ minLength: 1, validate: (value) => { - if (!isValidNamespace(value)) { - return 'Namespace contains invalid characters'; + const namespaceValidation = isValidNamespace(value || ''); + if (!namespaceValidation.valid && namespaceValidation.error) { + return namespaceValidation.error; } }, }); diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index 43ee0c89126e9..3866ef095563e 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -172,6 +172,23 @@ export const PostAgentUnenrollRequestSchema = { ), }; +export const PostAgentUpgradeRequestSchema = { + params: schema.object({ + agentId: schema.string(), + }), + body: schema.object({ + source_uri: schema.string(), + version: schema.string(), + }), +}; + +export const PostBulkAgentUnenrollRequestSchema = { + body: schema.object({ + agents: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]), + force: schema.maybe(schema.boolean()), + }), +}; + export const PutAgentReassignRequestSchema = { params: schema.object({ agentId: schema.string(), @@ -181,6 +198,13 @@ export const PutAgentReassignRequestSchema = { }), }; +export const PostBulkAgentReassignRequestSchema = { + body: schema.object({ + policy_id: schema.string(), + agents: schema.oneOf([schema.arrayOf(schema.string()), schema.string()]), + }), +}; + export const GetOneAgentEventsRequestSchema = { params: schema.object({ agentId: schema.string(), diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts index d7a801feec34f..5d2a078374854 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/epm.ts @@ -43,6 +43,12 @@ export const InstallPackageFromRegistryRequestSchema = { ), }; +export const BulkUpgradePackagesFromRegistryRequestSchema = { + body: schema.object({ + packages: schema.arrayOf(schema.string(), { minSize: 1 }), + }), +}; + export const InstallPackageByUploadRequestSchema = { body: schema.buffer(), }; diff --git a/x-pack/plugins/ingest_pipelines/README.md b/x-pack/plugins/ingest_pipelines/README.md index a469511bdbbd2..00d4f5a91863d 100644 --- a/x-pack/plugins/ingest_pipelines/README.md +++ b/x-pack/plugins/ingest_pipelines/README.md @@ -11,7 +11,7 @@ It requires a Basic license and the following cluster privileges: `manage_pipeli ## Development -A new app called Ingest Node Pipelines is registered in the Management section and follows a typical CRUD UI pattern. The client-side portion of this app lives in [public/application](public/application) and uses endpoints registered in [server/routes/api](server/routes/api). +A new app called Ingest Node Pipelines is registered in the Management section and follows a typical CRUD UI pattern. The client-side portion of this app lives in [public/application](public/application) and uses endpoints registered in [server/routes/api](server/routes/api). For more information on the pipeline processors editor component, check out the [component readme](public/application/components/pipeline_processors_editor/README.md). See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions on setting up your development environment. @@ -19,6 +19,83 @@ See the [kibana contributing guide](https://github.com/elastic/kibana/blob/maste The app has the following test coverage: -- Complete API integration tests +- API integration tests - Smoke-level functional test - Client-integration tests + +### Quick steps for manual testing + +You can run the following request in Console to create an ingest node pipeline: + +``` +PUT _ingest/pipeline/test_pipeline +{ + "description": "_description", + "processors": [ + { + "set": { + "field": "field1", + "value": "value1" + } + }, + { + "rename": { + "field": "dont_exist", + "target_field": "field1", + "ignore_failure": true + } + }, + { + "rename": { + "field": "foofield", + "target_field": "new_field", + "on_failure": [ + { + "set": { + "field": "field2", + "value": "value2" + } + } + ] + } + }, + { + "drop": { + "if": "false" + } + }, + { + "drop": { + "if": "true" + } + } + ] +} +``` + +Then, go to the Ingest Node Pipelines UI to edit, delete, clone, or view details of the pipeline. + +To simulate a pipeline, go to the "Edit" page of your pipeline. Click the "Add documents" link under the "Processors" section. You may add the following sample documents to test the pipeline: + +``` +// The first document in this example should trigger the on_failure processor in the pipeline, while the second one should succeed. +[ + { + "_index": "my_index", + "_id": "id1", + "_source": { + "foo": "bar" + } + }, + { + "_index": "my_index", + "_id": "id2", + "_source": { + "foo": "baz", + "foofield": "bar" + } + } +] +``` + +Alternatively, you can add a document from an existing index, or create some sample data of your own. Afterward, click the "Run the pipeline" button to view the output. diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_clone.helpers.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_clone.helpers.ts index f369bfe66f642..2559b93bd606d 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_clone.helpers.ts +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_clone.helpers.ts @@ -5,10 +5,10 @@ */ import { registerTestBed, TestBedConfig, TestBed } from '../../../../../test_utils'; -import { BASE_PATH } from '../../../common/constants'; import { PipelinesClone } from '../../../public/application/sections/pipelines_clone'; import { getFormActions, PipelineFormTestSubjects } from './pipeline_form.helpers'; import { WithAppDependencies } from './setup_environment'; +import { getClonePath, ROUTES } from '../../../public/application/services/navigation'; export type PipelinesCloneTestBed = TestBed & { actions: ReturnType; @@ -29,8 +29,8 @@ export const PIPELINE_TO_CLONE = { const testBedConfig: TestBedConfig = { memoryRouter: { - initialEntries: [`${BASE_PATH}create/${PIPELINE_TO_CLONE.name}`], - componentRoutePath: `${BASE_PATH}create/:name`, + initialEntries: [getClonePath({ clonedPipelineName: PIPELINE_TO_CLONE.name })], + componentRoutePath: ROUTES.clone, }, doMountAsync: true, }; diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_create.helpers.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_create.helpers.ts index ce5ab1faa01be..22f68f12804d6 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_create.helpers.ts +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_create.helpers.ts @@ -5,10 +5,10 @@ */ import { registerTestBed, TestBedConfig, TestBed } from '../../../../../test_utils'; -import { BASE_PATH } from '../../../common/constants'; import { PipelinesCreate } from '../../../public/application/sections/pipelines_create'; import { getFormActions, PipelineFormTestSubjects } from './pipeline_form.helpers'; import { WithAppDependencies } from './setup_environment'; +import { getCreatePath, ROUTES } from '../../../public/application/services/navigation'; export type PipelinesCreateTestBed = TestBed & { actions: ReturnType; @@ -16,8 +16,8 @@ export type PipelinesCreateTestBed = TestBed & { const testBedConfig: TestBedConfig = { memoryRouter: { - initialEntries: [`${BASE_PATH}/create`], - componentRoutePath: `${BASE_PATH}/create`, + initialEntries: [getCreatePath()], + componentRoutePath: ROUTES.create, }, doMountAsync: true, }; diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_edit.helpers.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_edit.helpers.ts index 31c9630086178..5e0739f78eecd 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_edit.helpers.ts +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_edit.helpers.ts @@ -5,10 +5,10 @@ */ import { registerTestBed, TestBedConfig, TestBed } from '../../../../../test_utils'; -import { BASE_PATH } from '../../../common/constants'; import { PipelinesEdit } from '../../../public/application/sections/pipelines_edit'; import { getFormActions, PipelineFormTestSubjects } from './pipeline_form.helpers'; import { WithAppDependencies } from './setup_environment'; +import { getEditPath, ROUTES } from '../../../public/application/services/navigation'; export type PipelinesEditTestBed = TestBed & { actions: ReturnType; @@ -29,8 +29,8 @@ export const PIPELINE_TO_EDIT = { const testBedConfig: TestBedConfig = { memoryRouter: { - initialEntries: [`${BASE_PATH}edit/${PIPELINE_TO_EDIT.name}`], - componentRoutePath: `${BASE_PATH}edit/:name`, + initialEntries: [getEditPath({ pipelineName: PIPELINE_TO_EDIT.name })], + componentRoutePath: ROUTES.edit, }, doMountAsync: true, }; diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_list.helpers.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_list.helpers.ts index 03ffe361bb5a6..43ca849e61aee 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_list.helpers.ts +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/pipelines_list.helpers.ts @@ -6,7 +6,6 @@ import { act } from 'react-dom/test-utils'; -import { BASE_PATH } from '../../../common/constants'; import { registerTestBed, TestBed, @@ -16,11 +15,12 @@ import { } from '../../../../../test_utils'; import { PipelinesList } from '../../../public/application/sections/pipelines_list'; import { WithAppDependencies } from './setup_environment'; +import { getListPath, ROUTES } from '../../../public/application/services/navigation'; const testBedConfig: TestBedConfig = { memoryRouter: { - initialEntries: [BASE_PATH], - componentRoutePath: BASE_PATH, + initialEntries: [getListPath()], + componentRoutePath: ROUTES.list, }, doMountAsync: true, }; diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/setup_environment.tsx index c380032bd9482..d9a0ac4115389 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/helpers/setup_environment.tsx @@ -44,6 +44,11 @@ const appServices = { api: apiService, notifications: notificationServiceMock.createSetupContract(), history, + urlGenerators: { + getUrlGenerator: jest.fn().mockReturnValue({ + createUrl: jest.fn(), + }), + }, }; export const setupEnvironment = () => { diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx index 6074c64d2bdb0..18ca71f2bb73a 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_create.test.tsx @@ -185,30 +185,5 @@ describe('', () => { expect(find('savePipelineError').find('li').length).toBe(8); }); }); - - describe('test pipeline', () => { - beforeEach(async () => { - await act(async () => { - testBed = await setup(); - - const { waitFor } = testBed; - - await waitFor('pipelineForm'); - }); - }); - - test('should open the test pipeline flyout', async () => { - const { actions, exists, find, waitFor } = testBed; - - await act(async () => { - actions.clickAddDocumentsButton(); - await waitFor('testPipelineFlyout'); - }); - - // Verify test pipeline flyout opens - expect(exists('testPipelineFlyout')).toBe(true); - expect(find('testPipelineFlyout.title').text()).toBe('Test pipeline'); - }); - }); }); }); diff --git a/x-pack/plugins/ingest_pipelines/common/constants.ts b/x-pack/plugins/ingest_pipelines/common/constants.ts index 4c6c6fefaad83..0d6f977bfbfed 100644 --- a/x-pack/plugins/ingest_pipelines/common/constants.ts +++ b/x-pack/plugins/ingest_pipelines/common/constants.ts @@ -9,9 +9,9 @@ const basicLicense: LicenseType = 'basic'; export const PLUGIN_ID = 'ingest_pipelines'; -export const PLUGIN_MIN_LICENSE_TYPE = basicLicense; +export const MANAGEMENT_APP_ID = 'management'; -export const BASE_PATH = '/'; +export const PLUGIN_MIN_LICENSE_TYPE = basicLicense; export const API_BASE_PATH = '/api/ingest_pipelines'; diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json index 38d28fbba20b4..2fe87c5e7a162 100644 --- a/x-pack/plugins/ingest_pipelines/kibana.json +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -3,7 +3,7 @@ "version": "8.0.0", "server": true, "ui": true, - "requiredPlugins": ["licensing", "management", "features"], + "requiredPlugins": ["licensing", "management", "features", "share"], "optionalPlugins": ["security", "usageCollection"], "configPath": ["xpack", "ingest_pipelines"], "requiredBundles": ["esUiShared", "kibanaReact"] diff --git a/x-pack/plugins/ingest_pipelines/public/application/app.tsx b/x-pack/plugins/ingest_pipelines/public/application/app.tsx index 55b59caab8d60..e78c4d3983183 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/app.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/app.tsx @@ -21,13 +21,14 @@ import { } from '../shared_imports'; import { PipelinesList, PipelinesCreate, PipelinesEdit, PipelinesClone } from './sections'; +import { ROUTES } from './services/navigation'; export const AppWithoutRouter = () => ( - - - - + + + + {/* Catch all */} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx index 5279bd718c16e..ffd82b0bbaf35 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx @@ -11,8 +11,6 @@ import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from import { useForm, Form, FormConfig } from '../../../shared_imports'; import { Pipeline, Processor } from '../../../../common/types'; -import './pipeline_form.scss'; - import { OnUpdateHandlerArg, OnUpdateHandler } from '../pipeline_processors_editor'; import { PipelineRequestFlyout } from './pipeline_request_flyout'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx index 6033f34af6825..a7ffe7ba02caa 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx @@ -6,7 +6,7 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiSpacer, EuiSwitch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiSpacer, EuiSwitch } from '@elastic/eui'; import { Processor } from '../../../../common/types'; @@ -14,15 +14,11 @@ import { getUseField, getFormRow, Field } from '../../../shared_imports'; import { ProcessorsEditorContextProvider, - GlobalOnFailureProcessorsEditor, - ProcessorsEditor, OnUpdateHandler, OnDoneLoadJsonHandler, + PipelineProcessorsEditor, } from '../pipeline_processors_editor'; -import { ProcessorsHeader } from './processors_header'; -import { OnFailureProcessorsTitle } from './on_failure_processors_title'; - interface Props { processors: Processor[]; onFailure?: Processor[]; @@ -118,28 +114,12 @@ export const PipelineFormFields: React.FunctionComponent = ({ {/* Pipeline Processors Editor */} - -
- - - - - - - - - - - - - - -
+
); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx deleted file mode 100644 index 43477affa8d94..0000000000000 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/processors_header.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { FunctionComponent } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { useKibana } from '../../../shared_imports'; - -import { - LoadFromJsonButton, - OnDoneLoadJsonHandler, - TestPipelineActions, -} from '../pipeline_processors_editor'; - -export interface Props { - onLoadJson: OnDoneLoadJsonHandler; -} - -export const ProcessorsHeader: FunctionComponent = ({ onLoadJson }) => { - const { services } = useKibana(); - - return ( - - - - - -

- {i18n.translate('xpack.ingestPipelines.pipelineEditor.processorsTreeTitle', { - defaultMessage: 'Processors', - })} -

-
-
- - - -
- - - - {i18n.translate( - 'xpack.ingestPipelines.pipelineEditor.processorsDocumentationLink', - { - defaultMessage: 'Learn more.', - } - )} - - ), - }} - /> - -
- - - -
- ); -}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/README.md b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/README.md index d29af67d3179c..4761bd9e6c70b 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/README.md +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/README.md @@ -7,18 +7,37 @@ pipeline. ## Editor components -The top-level API consists of 3 pieces that enable the maximum amount -of flexibility for consuming code to determine overall layout. +The top-level API consists of two pieces: -- PipelineProcessorsEditorContext -- ProcessorsEditor -- GlobalOnFailureProcessorsEditor +- ProcessorsEditorContextProvider +- PipelineProcessorsEditor -The editor components must be wrapped inside of the context component +The editor component must be wrapped inside of the context component as this is where the shared processors state is contained. -## Load JSON button +Example usage from the [PipelineFormFields](../pipeline_form/pipeline_form_fields.tsx) component: -This component is totally standalone. It gives users a button that +``` + + + +``` + +The editor has a dependency on `KibanaContextProvider`, which is defined in the main app's `index.tsx` file. Note that the editor also relies on imports from `public/shared_imports.ts` and `common/types.ts`. + +### ProcessorsEditorContextProvider +This component manages state for the processors, as well as state for the test pipeline functionality. + +### PipelineProcessorsEditor +This component is responsible for building the layout of the processors editor. + +It contains the processor and on-failure processor editors. It also includes the following capabilities that are rendered within the processors header: + +- **Load JSON button:** This component gives users a button that presents a modal for loading a pipeline. It does some basic validation on the JSON to ensure that it is correct. +- **Test pipeline actions:** This component presents users with a toolbar to test a pipeline. It includes a flyout where users can add sample documents. It issues a request to simulate the pipeline and displays the output. Once the request is successful, a user can use the documents dropdown to view the results for a particular document. diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/http_requests.helpers.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/http_requests.helpers.ts index 541a6853a99b3..c89b07ae0192f 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/http_requests.helpers.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/http_requests.helpers.ts @@ -21,8 +21,20 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; + const setFetchDocumentsResponse = (response?: HttpResponse, error?: any) => { + const status = error ? error.status || 400 : 200; + const body = error ? JSON.stringify(error.body) : JSON.stringify(response); + + server.respondWith('GET', '/api/ingest_pipelines/documents/:index/:id', [ + status, + { 'Content-Type': 'application/json' }, + body, + ]); + }; + return { setSimulatePipelineResponse, + setFetchDocumentsResponse, }; }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx index e46e5156e30f3..10fb73df1ce1c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.helpers.tsx @@ -11,12 +11,7 @@ import { notificationServiceMock, scopedHistoryMock } from 'src/core/public/mock import { LocationDescriptorObject } from 'history'; import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; import { registerTestBed, TestBed } from '../../../../../../../test_utils'; -import { - ProcessorsEditorContextProvider, - Props, - ProcessorsEditor, - GlobalOnFailureProcessorsEditor, -} from '../'; +import { ProcessorsEditorContextProvider, Props, PipelineProcessorsEditor } from '../'; import { breadcrumbService, @@ -90,7 +85,7 @@ const testBedSetup = registerTestBed( (props: Props) => ( - + ), @@ -210,4 +205,5 @@ type TestSubject = | 'processorSettingsFormFlyout' | 'processorTypeSelector' | 'pipelineEditorOnFailureTree' + | 'processorsEmptyPrompt' | string; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx index 74ae8b8894b9f..b80d238362118 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx @@ -55,6 +55,23 @@ describe('Pipeline Editor', () => { expect(arg.getData()).toEqual(testProcessors); }); + describe('no processors', () => { + beforeEach(async () => { + testBed = await setup({ + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + + it('displays an empty prompt if no processors are defined', () => { + const { exists } = testBed; + expect(exists('processorsEmptyPrompt')).toBe(true); + }); + }); + describe('processors', () => { it('adds a new processor', async () => { const { actions } = testBed; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx index f4c89d7a1058a..222e0a491e0d2 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.helpers.tsx @@ -25,13 +25,7 @@ import { apiService, } from '../../../services'; -import { - ProcessorsEditorContextProvider, - Props, - GlobalOnFailureProcessorsEditor, - ProcessorsEditor, -} from '../'; -import { TestPipelineActions } from '../'; +import { ProcessorsEditorContextProvider, Props, PipelineProcessorsEditor } from '../'; import { initHttpRequests } from './http_requests.helpers'; @@ -94,15 +88,18 @@ const appServices = { notifications: notificationServiceMock.createSetupContract(), history, uiSettings: {}, + urlGenerators: { + getUrlGenerator: jest.fn().mockReturnValue({ + createUrl: jest.fn(), + }), + }, }; const testBedSetup = registerTestBed( (props: Props) => ( - - - + ), @@ -174,12 +171,61 @@ const createActions = (testBed: TestBed) => { }); }, + clickDocumentsDropdown() { + act(() => { + find('documentsDropdown.documentsButton').simulate('click'); + }); + component.update(); + }, + + clickEditDocumentsButton() { + act(() => { + find('editDocumentsButton').simulate('click'); + }); + component.update(); + }, + + clickClearAllButton() { + act(() => { + find('clearAllDocumentsButton').simulate('click'); + }); + component.update(); + }, + + async clickConfirmResetButton() { + const modal = document.body.querySelector( + '[data-test-subj="resetDocumentsConfirmationModal"]' + ); + const confirmButton: HTMLButtonElement | null = modal!.querySelector( + '[data-test-subj="confirmModalConfirmButton"]' + ); + + await act(async () => { + confirmButton!.click(); + }); + component.update(); + }, + async clickProcessor(processorSelector: string) { await act(async () => { find(`${processorSelector}.manageItemButton`).simulate('click'); }); component.update(); }, + + async toggleDocumentsAccordion() { + await act(async () => { + find('addDocumentsAccordion').simulate('click'); + }); + component.update(); + }, + + async clickAddDocumentButton() { + await act(async () => { + find('addDocumentButton').simulate('click'); + }); + component.update(); + }, }; }; @@ -211,6 +257,7 @@ type TestSubject = | 'addDocumentsButton' | 'testPipelineFlyout' | 'documentsDropdown' + | 'documentsDropdown.documentsButton' | 'outputTab' | 'documentsEditor' | 'runPipelineButton' @@ -229,4 +276,10 @@ type TestSubject = | 'configurationTab' | 'outputTab' | 'processorOutputTabContent' + | 'editDocumentsButton' + | 'clearAllDocumentsButton' + | 'addDocumentsAccordion' + | 'addDocumentButton' + | 'addDocumentError' + | 'addDocumentSuccess' | string; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx index e5118a6e465af..69a1b7c2c126d 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/test_pipeline.test.tsx @@ -22,6 +22,27 @@ describe('Test pipeline', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); + // This is a hack + // We need to provide the processor id in the mocked output; + // this is generated dynamically + // As a workaround, the value is added as a data attribute in the UI + // and we retrieve it to generate the mocked output. + const addProcessorTagtoMockOutput = (output: VerboseTestOutput) => { + const { find } = testBed; + + const docs = output.docs.map((doc) => { + const results = doc.processor_results.map((result, index) => { + const tag = find(`processors>${index}`).props()['data-processor-id']; + return { + ...result, + tag, + }; + }); + return { processor_results: results }; + }); + return { docs }; + }; + beforeAll(() => { jest.useFakeTimers(); }); @@ -141,10 +162,9 @@ describe('Test pipeline', () => { const { actions, find, exists } = testBed; const error = { - status: 400, - error: 'Bad Request', - message: - '"[parse_exception] [_source] required property is missing, with { property_name="_source" }"', + status: 500, + error: 'Internal server error', + message: 'Internal server error', }; httpRequestsMockHelpers.setSimulatePipelineResponse(undefined, { body: error }); @@ -153,37 +173,161 @@ describe('Test pipeline', () => { actions.clickAddDocumentsButton(); // Add invalid sample documents array and run the pipeline - actions.addDocumentsJson(JSON.stringify([{}])); + actions.addDocumentsJson( + JSON.stringify([ + { + _index: 'test', + _id: '1', + _version: 1, + _seq_no: 0, + _primary_term: 1, + _source: { + name: 'John Doe', + }, + }, + ]) + ); await actions.clickRunPipelineButton(); // Verify error rendered expect(exists('pipelineExecutionError')).toBe(true); expect(find('pipelineExecutionError').text()).toContain(error.message); }); - }); - describe('Processors', () => { - // This is a hack - // We need to provide the processor id in the mocked output; - // this is generated dynamically and not something we can stub. - // As a workaround, the value is added as a data attribute in the UI - // and we retrieve it to generate the mocked output. - const addProcessorTagtoMockOutput = (output: VerboseTestOutput) => { - const { find } = testBed; + describe('Add indexed documents', () => { + test('should successfully add an indexed document', async () => { + const { actions, form, exists } = testBed; - const docs = output.docs.map((doc) => { - const results = doc.processor_results.map((result, index) => { - const tag = find(`processors>${index}`).props()['data-processor-id']; - return { - ...result, - tag, - }; - }); - return { processor_results: results }; + const { _index: index, _id: documentId } = DOCUMENTS[0]; + + httpRequestsMockHelpers.setFetchDocumentsResponse(DOCUMENTS[0]); + + // Open flyout + actions.clickAddDocumentsButton(); + + // Open documents accordion, click run without required fields, and verify error messages + await actions.toggleDocumentsAccordion(); + await actions.clickAddDocumentButton(); + expect(form.getErrorsMessages()).toEqual([ + 'An index name is required.', + 'A document ID is required.', + ]); + + // Add required fields, and click run + form.setInputValue('indexField.input', index); + form.setInputValue('idField.input', documentId); + await actions.clickAddDocumentButton(); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + expect(latestRequest.status).toEqual(200); + expect(latestRequest.url).toEqual(`/api/ingest_pipelines/documents/${index}/${documentId}`); + // Verify success callout + expect(exists('addDocumentSuccess')).toBe(true); + }); + + test('should surface API errors from the request', async () => { + const { actions, form, exists, find } = testBed; + + const nonExistentDoc = { + index: 'foo', + id: '1', + }; + + const error = { + status: 404, + error: 'Not found', + message: '[index_not_found_exception] no such index', + }; + + httpRequestsMockHelpers.setFetchDocumentsResponse(undefined, { body: error }); + + // Open flyout + actions.clickAddDocumentsButton(); + + // Open documents accordion, add required fields, and click run + await actions.toggleDocumentsAccordion(); + form.setInputValue('indexField.input', nonExistentDoc.index); + form.setInputValue('idField.input', nonExistentDoc.id); + await actions.clickAddDocumentButton(); + + // Verify error rendered + expect(exists('addDocumentError')).toBe(true); + expect(exists('addDocumentSuccess')).toBe(false); + expect(find('addDocumentError').text()).toContain(error.message); + }); + }); + + describe('Documents dropdown', () => { + beforeEach(async () => { + const { actions } = testBed; + + httpRequestsMockHelpers.setSimulatePipelineResponse( + addProcessorTagtoMockOutput(SIMULATE_RESPONSE) + ); + + // Open flyout + actions.clickAddDocumentsButton(); + // Add sample documents and click run + actions.addDocumentsJson(JSON.stringify(DOCUMENTS)); + await actions.clickRunPipelineButton(); + // Close flyout + actions.closeTestPipelineFlyout(); }); - return { docs }; - }; + it('should open flyout to edit documents', () => { + const { exists, actions } = testBed; + + // Dropdown should be visible + expect(exists('documentsDropdown')).toBe(true); + + // Open dropdown and edit documents + actions.clickDocumentsDropdown(); + actions.clickEditDocumentsButton(); + + // Flyout should be visible with "Documents" tab enabled + expect(exists('testPipelineFlyout')).toBe(true); + expect(exists('documentsTabContent')).toBe(true); + }); + + it('should clear all documents and stop pipeline simulation', async () => { + const { exists, actions, find } = testBed; + + // Dropdown should be visible and processor status should equal "success" + expect(exists('documentsDropdown')).toBe(true); + const initialProcessorStatusLabel = find('processors>0.processorStatusIcon').props()[ + 'aria-label' + ]; + expect(initialProcessorStatusLabel).toEqual('Success'); + + // Open flyout and click clear all button + actions.clickDocumentsDropdown(); + actions.clickEditDocumentsButton(); + actions.clickClearAllButton(); + + // Verify modal + const modal = document.body.querySelector( + '[data-test-subj="resetDocumentsConfirmationModal"]' + ); + + expect(modal).not.toBe(null); + expect(modal!.textContent).toContain('Clear documents'); + + // Confirm reset and close modal + await actions.clickConfirmResetButton(); + + // Verify documents and processors were reset + expect(exists('documentsDropdown')).toBe(false); + expect(exists('addDocumentsButton')).toBe(true); + const resetProcessorStatusIconLabel = find('processors>0.processorStatusIcon').props()[ + 'aria-label' + ]; + expect(resetProcessorStatusIconLabel).toEqual('Not run'); + }); + }); + }); + + describe('Processors', () => { it('should show "inactive" processor status by default', async () => { const { find } = testBed; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx index 4aabcc1d59d73..03b497320dfbc 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx @@ -6,30 +6,49 @@ import React, { FunctionComponent } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty, EuiButton } from '@elastic/eui'; import { usePipelineProcessorsContext } from '../context'; export interface Props { onClick: () => void; + renderButtonAsLink?: boolean; } +const addProcessorButtonLabel = i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addProcessorButtonLabel', + { + defaultMessage: 'Add a processor', + } +); + export const AddProcessorButton: FunctionComponent = (props) => { - const { onClick } = props; + const { onClick, renderButtonAsLink } = props; const { state: { editor }, } = usePipelineProcessorsContext(); + + if (renderButtonAsLink) { + return ( + + {addProcessorButtonLabel} + + ); + } + return ( - - {i18n.translate('xpack.ingestPipelines.pipelineEditor.addProcessorButtonLabel', { - defaultMessage: 'Add a processor', - })} - + {addProcessorButtonLabel} +
); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts index d476202aa43bb..2e62a81ffa153 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/index.ts @@ -19,3 +19,9 @@ export { OnDoneLoadJsonHandler, LoadFromJsonButton } from './load_from_json'; export { TestPipelineActions } from './test_pipeline'; export { PipelineProcessorsItemTooltip, Position } from './pipeline_processors_editor_item_tooltip'; + +export { ProcessorsEmptyPrompt } from './processors_empty_prompt'; + +export { ProcessorsHeader } from './processors_header'; + +export { OnFailureProcessorsTitle } from './on_failure_processors_title'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx index 21d15fc86a0ce..38700d6a7a87c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/load_from_json/button.tsx @@ -15,7 +15,7 @@ interface Props { const i18nTexts = { buttonLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.loadFromJson.buttonLabel', { - defaultMessage: 'Import', + defaultMessage: 'Import processors', }), }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx similarity index 96% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx index 0beb5657b54cb..7adc37d1897d1 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/on_failure_processors_title.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/on_failure_processors_title.tsx @@ -8,7 +8,7 @@ import React, { FunctionComponent } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useKibana } from '../../../shared_imports'; +import { useKibana } from '../../../../shared_imports'; export const OnFailureProcessorsTitle: FunctionComponent = () => { const { services } = useKibana(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx index 5231a3d17811b..b663daedd9b9c 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/add_processor_form.tsx @@ -118,6 +118,7 @@ export const AddProcessorForm: FunctionComponent = ({ { await handleSubmit(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx index e449ed75b6343..3df73b54b8cce 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/edit_processor_form.tsx @@ -102,7 +102,7 @@ export const EditProcessorForm: FunctionComponent = ({ handleSubmit, resetProcessors, }) => { - const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext(); + const { testPipelineData, testPipelineDataDispatch } = useTestPipelineContext(); const { testOutputPerProcessor, config: { selectedDocumentIndex, documents }, @@ -117,7 +117,7 @@ export const EditProcessorForm: FunctionComponent = ({ testOutputPerProcessor[selectedDocumentIndex][processor.id]; const updateSelectedDocument = (index: number) => { - setCurrentTestPipelineData({ + testPipelineDataDispatch({ type: 'updateActiveDocument', payload: { config: { @@ -234,6 +234,7 @@ export const EditProcessorForm: FunctionComponent = ({ { if (activeTab === 'output') { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.scss new file mode 100644 index 0000000000000..2f563d86a6d4a --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.scss @@ -0,0 +1,28 @@ +.pipelineProcessorsEditor__form__dragAndDropList { + &__panel { + background-color: $euiColorLightestShade; + padding: $euiSizeM; + } + + &__grabIcon { + margin-right: $euiSizeS; + } + + &__removeButton { + margin-left: $euiSizeS; + } + + &__errorIcon { + margin-left: -$euiSizeXL; + } + + &__item { + background-color: $euiColorLightestShade; + padding-top: $euiSizeS; + padding-bottom: $euiSizeS; + } + + &__labelContainer { + margin-bottom: $euiSizeXS; + } +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.tsx new file mode 100644 index 0000000000000..63e1fdaa9a8f0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/drag_and_drop_text_list.tsx @@ -0,0 +1,210 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 React, { useState, useCallback, memo } from 'react'; +import uuid from 'uuid'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiDragDropContext, + EuiDraggable, + EuiDroppable, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiFieldText, + EuiIconTip, + EuiFormRow, + EuiText, +} from '@elastic/eui'; + +import { + UseField, + ArrayItem, + ValidationFunc, + getFieldValidityAndErrorMessage, +} from '../../../../../../shared_imports'; + +import './drag_and_drop_text_list.scss'; + +interface Props { + label: string; + helpText: React.ReactNode; + error: string | null; + value: ArrayItem[]; + onMove: (sourceIdx: number, destinationIdx: number) => void; + onAdd: () => void; + onRemove: (id: number) => void; + addLabel: string; + /** + * Validation to be applied to every text item + */ + textValidation?: ValidationFunc; +} + +const i18nTexts = { + removeItemButtonAriaLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.dragAndDropList.removeItemLabel', + { defaultMessage: 'Remove item' } + ), +}; + +function DragAndDropTextListComponent({ + label, + helpText, + error, + value, + onMove, + onAdd, + onRemove, + addLabel, + textValidation, +}: Props): JSX.Element { + const [droppableId] = useState(() => uuid.v4()); + const [firstItemId] = useState(() => uuid.v4()); + + const onDragEnd = useCallback( + ({ source, destination }) => { + if (source && destination) { + onMove(source.index, destination.index); + } + }, + [onMove] + ); + return ( + + <> + {/* Label and help text. Also wire up the htmlFor so the label points to the first text field. */} + + + + + + + + +

{helpText}

+
+
+
+ + {/* The processor panel */} +
+ + + {value.map((item, idx) => { + return ( + + {(provided) => { + return ( + + +
+ +
+
+ + + path={item.path} + config={{ + validations: textValidation + ? [{ validator: textValidation }] + : undefined, + }} + readDefaultValueOnForm={!item.isNew} + > + {(field) => { + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage( + field + ); + return ( + + + + + {typeof errorMessage === 'string' && ( + +
+ +
+
+ )} +
+ ); + }} + +
+ + {value.length > 1 ? ( + onRemove(item.id)} + /> + ) : ( + // Render a no-op placeholder button + + )} + +
+ ); + }} +
+ ); + })} +
+
+ + {addLabel} + +
+ +
+ ); +} + +export const DragAndDropTextList = memo( + DragAndDropTextListComponent +) as typeof DragAndDropTextListComponent; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts index 6ce9eefd26445..605568f90ce9f 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/index.ts @@ -4,5 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ +export { DragAndDropTextList } from './drag_and_drop_text_list'; export { XJsonEditor } from './xjson_editor'; export { TextEditor } from './text_editor'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.scss new file mode 100644 index 0000000000000..f48e19fd0e635 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.scss @@ -0,0 +1,5 @@ +.pipelineProcessorsEditor__form__textEditor { + &__panel { + box-shadow: none; + } +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx index 1d0e36c0d526c..88b4a0aa2be06 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/text_editor.tsx @@ -13,6 +13,8 @@ import { getFieldValidityAndErrorMessage, } from '../../../../../../shared_imports'; +import './text_editor.scss'; + interface Props { field: FieldHook; editorProps: { [key: string]: any }; @@ -30,7 +32,11 @@ export const TextEditor: FunctionComponent = ({ field, editorProps }) => error={errorMessage} fullWidth > - + diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx index e00f9c002e5bc..f482e6f08c2c6 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/field_components/xjson_editor.tsx @@ -5,7 +5,9 @@ */ import { XJsonLang } from '@kbn/monaco'; import React, { FunctionComponent, useCallback } from 'react'; -import { FieldHook, Monaco } from '../../../../../../shared_imports'; +import { FieldHook, XJson } from '../../../../../../shared_imports'; + +const { useXJsonMode } = XJson; import { TextEditor } from './text_editor'; @@ -21,7 +23,7 @@ const defaultEditorOptions = { export const XJsonEditor: FunctionComponent = ({ field, editorProps }) => { const { value, setValue } = field; - const { xJson, setXJson, convertToJson } = Monaco.useXJsonMode(value); + const { xJson, setXJson, convertToJson } = useXJsonMode(value); const onChange = useCallback( (s) => { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx index 332908d0756f2..25c9579e3c48e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_form.container.tsx @@ -60,6 +60,7 @@ export const ProcessorFormContainer: FunctionComponent = ({ const { form } = useForm({ defaultValue: { fields: getProcessor().options }, }); + const { subscribe } = form; const handleSubmit = useCallback( async (shouldCloseFlyout: boolean = true) => { @@ -92,14 +93,9 @@ export const ProcessorFormContainer: FunctionComponent = ({ }, [onSubmit, processor]); useEffect(() => { - const subscription = form.subscribe(onFormUpdate); + const subscription = subscribe(onFormUpdate); return subscription.unsubscribe; - - // TODO: Address this issue - // For some reason adding `form` object to the dependencies array here is causing an - // infinite update loop. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [onFormUpdate]); + }, [onFormUpdate, subscribe]); if (processor) { return ( @@ -113,15 +109,15 @@ export const ProcessorFormContainer: FunctionComponent = ({ handleSubmit={handleSubmit} /> ); - } else { - return ( - - ); } + + return ( + + ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx index bd0ce6ca2cd52..f6b13c2ba7228 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processor_output/processor_output.tsx @@ -32,8 +32,7 @@ export interface Props { const i18nTexts = { tabDescription: i18n.translate('xpack.ingestPipelines.processorOutput.descriptionText', { - defaultMessage: - 'View how the processor affects the ingest document as it passes through the pipeline.', + defaultMessage: 'Preview changes to the test document.', }), skippedCalloutTitle: i18n.translate('xpack.ingestPipelines.processorOutput.skippedCalloutTitle', { defaultMessage: 'The processor was not run.', @@ -68,7 +67,7 @@ const i18nTexts = { processorIgnoredErrorTitle: i18n.translate( 'xpack.ingestPipelines.processorOutput.ignoredErrorCodeBlockLabel', { - defaultMessage: 'There was an error that was ignored', + defaultMessage: 'There was an ignored error', } ), documentsDropdownLabel: i18n.translate( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx index 3264923442886..5b3df63a11294 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/processor_type_field.tsx @@ -14,6 +14,7 @@ import { FieldConfig, UseField, fieldValidators, + useKibana, } from '../../../../../../../shared_imports'; import { getProcessorDescriptor, mapProcessorTypeToDescriptor } from '../../../shared'; @@ -64,6 +65,10 @@ const typeConfig: FieldConfig = { }; export const ProcessorTypeField: FunctionComponent = ({ initialType }) => { + const { + services: { documentation }, + } = useKibana(); + const esDocUrl = documentation.getEsDocsBasePath(); return ( config={typeConfig} defaultValue={initialType} path="type"> {(typeField) => { @@ -107,7 +112,7 @@ export const ProcessorTypeField: FunctionComponent = ({ initialType }) => {}; + (this as any).terminate = () => {}; +}; + +describe('', () => { + const setup = (props?: { defaultValue: Record }) => { + function MyComponent() { + const { form } = useForm({ defaultValue: props?.defaultValue }); + const i18n = i18nServiceMock.createStartContract(); + return ( + + + + + + + + ); + } + return mount(); + }; + + beforeAll(() => { + // disable all react-beautiful-dnd development warnings + (window as any)['__react-beautiful-dnd-disable-dev-warnings'] = true; + }); + + afterAll(() => { + // enable all react-beautiful-dnd development warnings + (window as any)['__react-beautiful-dnd-disable-dev-warnings'] = false; + }); + test('smoke', () => { + setup({ defaultValue: { type: 'grok', fields: { patterns: ['test'] } } }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx index c5c6adbe2a7a8..5df30be3407a2 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/grok.tsx @@ -10,24 +10,46 @@ import { i18n } from '@kbn/i18n'; import { FIELD_TYPES, UseField, - ComboBoxField, + UseArray, ToggleField, fieldValidators, + ValidationFunc, + ArrayItem, } from '../../../../../../shared_imports'; -import { XJsonEditor } from '../field_components'; +import { XJsonEditor, DragAndDropTextList } from '../field_components'; import { FieldNameField } from './common_fields/field_name_field'; import { IgnoreMissingField } from './common_fields/ignore_missing_field'; import { FieldsConfig, to, from, EDITOR_PX_HEIGHT } from './shared'; -const { emptyField, isJsonField } = fieldValidators; +const { isJsonField, emptyField } = fieldValidators; + +const i18nTexts = { + addPatternLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.grokForm.patternsAddPatternLabel', + { defaultMessage: 'Add pattern' } + ), +}; + +const valueRequiredMessage = i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.grokForm.patternsValueRequiredError', + { defaultMessage: 'A value is required.' } +); + +const patternsValidation: ValidationFunc = ({ value, formData }) => { + if (value.length === 0) { + return { + message: valueRequiredMessage, + }; + } +}; + +const patternValidation = emptyField(valueRequiredMessage); const fieldsConfig: FieldsConfig = { /* Required field configs */ patterns: { - type: FIELD_TYPES.COMBO_BOX, - deserializer: to.arrayOfStrings, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.grokForm.patternsFieldLabel', { defaultMessage: 'Patterns', }), @@ -37,12 +59,7 @@ const fieldsConfig: FieldsConfig = { }), validations: [ { - validator: emptyField( - i18n.translate( - 'xpack.ingestPipelines.pipelineEditor.grokForm.patternsValueRequiredError', - { defaultMessage: 'A value is required.' } - ) - ), + validator: patternsValidation as ValidationFunc, }, ], }, @@ -103,7 +120,23 @@ export const Grok: FunctionComponent = () => { )} /> - + + {({ items, addItem, removeItem, moveItem, error }) => { + return ( + + ); + }} + = ({ onLoadJson }) => { + const { onTreeAction } = usePipelineProcessorsContext(); + const { services } = useKibana(); + + return ( + {i18nTexts.emptyPromptTitle}} + data-test-subj="processorsEmptyPrompt" + body={ +

+ + {i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.processorsDocumentationLink', + { + defaultMessage: 'Learn more.', + } + )} + + ), + }} + /> +

+ } + actions={ + <> + { + onTreeAction({ type: 'addProcessor', payload: { target: ['processors'] } }); + }} + /> + + + + + + } + /> + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_header.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_header.tsx new file mode 100644 index 0000000000000..1e5c9fb8319bc --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_header.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { useKibana } from '../../../../shared_imports'; + +import { LoadFromJsonButton, OnDoneLoadJsonHandler, TestPipelineActions } from './'; + +export interface Props { + onLoadJson: OnDoneLoadJsonHandler; + hasProcessors: boolean; +} + +export const ProcessorsHeader: FunctionComponent = ({ onLoadJson, hasProcessors }) => { + const { services } = useKibana(); + + const ProcessorTitle: FunctionComponent = () => ( + +

+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.processorsTreeTitle', { + defaultMessage: 'Processors', + })} +

+
+ ); + + if (!hasProcessors) { + return ; + } + + return ( + + + + + + + + + + + + + + {i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.processorsDocumentationLink', + { + defaultMessage: 'Learn more.', + } + )} + + ), + }} + /> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx index e9008e6f5b693..3a8299c017d8d 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/components/tree_node.tsx @@ -70,6 +70,7 @@ export const TreeNode: FunctionComponent = ({ /> onAction({ type: 'addProcessor', diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx index 8b344a137f3a8..ffc0a1459b791 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx @@ -99,7 +99,7 @@ export const ProcessorsTree: FunctionComponent = memo((props) => {
- + {!processors.length && ( = memo((props) => { onClick={() => { onAction({ type: 'addProcessor', payload: { target: baseSelector } }); }} + renderButtonAsLink /> diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx index 0a094009da2c6..4c98940b0138e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx @@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n'; import React, { ReactNode } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCode, EuiLink } from '@elastic/eui'; -import { useKibana } from '../../../../../shared_imports'; import { Append, @@ -55,7 +54,7 @@ interface FieldDescriptor { * A sentence case label that can be displayed to users */ label: string; - description?: string | ReactNode; + description?: string | ((esDocUrl: string) => ReactNode); } type MapProcessorTypeToDescriptor = Record; @@ -176,11 +175,7 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { label: i18n.translate('xpack.ingestPipelines.processors.label.enrich', { defaultMessage: 'Enrich', }), - description: function Description() { - const { - services: { documentation }, - } = useKibana(); - const esDocUrl = documentation.getEsDocsBasePath(); + description: (esDocUrl) => { return ( { return ( = ({ > ({ key: index.toString(), + 'data-test-subj': 'documentListItem', checked: selectedDocumentIndex === index ? 'on' : undefined, label: i18n.translate('xpack.ingestPipelines.pipelineEditor.testPipeline.documentLabel', { defaultMessage: 'Document {documentNumber}', @@ -107,32 +106,27 @@ export const DocumentsDropdown: FunctionComponent = ({ setShowPopover(false); }} > - {(list, search) => ( -
+ {(list) => ( + <> {i18nTexts.popoverTitle} {list} -
+ )}
- - - - - { - openFlyout('documents'); - setShowPopover(false); - }} - data-test-subj="addDocumentsButton" - > - {i18nTexts.addDocumentsButtonLabel} - - - - - + + { + openFlyout('documents'); + setShowPopover(false); + }} + data-test-subj="editDocumentsButton" + > + {i18nTexts.addDocumentsButtonLabel} + + ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx index 9018042229590..d2fc9c51be699 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_output_button.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiButton } from '@elastic/eui'; -import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs'; +import { TestPipelineFlyoutTab } from './test_pipeline_tabs'; const i18nTexts = { buttonLabel: i18n.translate( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx index cec02db26729d..83a9303859d2a 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_actions.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { useTestPipelineContext, usePipelineProcessorsContext } from '../../context'; import { DocumentsDropdown } from './documents_dropdown'; -import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs'; +import { TestPipelineFlyoutTab } from './test_pipeline_tabs'; import { AddDocumentsButton } from './add_documents_button'; import { TestOutputButton } from './test_output_button'; import { TestPipelineFlyout } from './test_pipeline_flyout.container'; @@ -24,7 +24,7 @@ const i18nTexts = { }; export const TestPipelineActions: FunctionComponent = () => { - const { testPipelineData, setCurrentTestPipelineData } = useTestPipelineContext(); + const { testPipelineData, testPipelineDataDispatch } = useTestPipelineContext(); const { state: { processors }, @@ -39,7 +39,7 @@ export const TestPipelineActions: FunctionComponent = () => { const [activeFlyoutTab, setActiveFlyoutTab] = useState('documents'); const updateSelectedDocument = (index: number) => { - setCurrentTestPipelineData({ + testPipelineDataDispatch({ type: 'updateActiveDocument', payload: { config: { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.container.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.container.tsx index b49eea5b59ab0..e7ccb9d17f2b1 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.container.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout.container.tsx @@ -12,10 +12,10 @@ import { useTestPipelineContext } from '../../context'; import { serialize } from '../../serialize'; import { DeserializeResult } from '../../deserialize'; import { Document } from '../../types'; +import { useIsMounted } from '../../use_is_mounted'; import { TestPipelineFlyout as ViewComponent } from './test_pipeline_flyout'; -import { TestPipelineFlyoutTab } from './test_pipeline_flyout_tabs'; -import { documentsSchema } from './test_pipeline_flyout_tabs/documents_schema'; +import { TestPipelineFlyoutTab } from './test_pipeline_tabs'; export interface Props { activeTab: TestPipelineFlyoutTab; @@ -34,10 +34,11 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ processors, }) => { const { services } = useKibana(); + const isMounted = useIsMounted(); const { testPipelineData, - setCurrentTestPipelineData, + testPipelineDataDispatch, updateTestOutputPerProcessor, } = useTestPipelineContext(); @@ -46,7 +47,6 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ } = testPipelineData; const { form } = useForm({ - schema: documentsSchema, defaultValue: { documents: cachedDocuments || '', }, @@ -74,6 +74,10 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ pipeline: { ...serializedProcessors }, }); + if (!isMounted.current) { + return { isSuccessful: false }; + } + setIsRunningTest(false); if (error) { @@ -82,7 +86,7 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ // reset the per-processor output // this is needed in the scenario where the pipeline has already executed, // but you modified the sample documents and there was an error on re-execution - setCurrentTestPipelineData({ + testPipelineDataDispatch({ type: 'updateOutputPerProcessor', payload: { isExecutingPipeline: false, @@ -93,7 +97,7 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ return { isSuccessful: false }; } - setCurrentTestPipelineData({ + testPipelineDataDispatch({ type: 'updateConfig', payload: { config: { @@ -123,10 +127,11 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ return { isSuccessful: true }; }, [ + isMounted, processors, services.api, services.notifications.toasts, - setCurrentTestPipelineData, + testPipelineDataDispatch, updateTestOutputPerProcessor, ] ); @@ -150,6 +155,12 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ } }; + const resetTestOutput = () => { + testPipelineDataDispatch({ + type: 'reset', + }); + }; + useEffect(() => { if (cachedDocuments && activeTab === 'output') { handleTestPipeline({ documents: cachedDocuments, verbose: cachedVerbose }, true); @@ -162,6 +173,7 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ return ( void; handleTestPipeline: ( @@ -31,11 +30,14 @@ export interface Props { cachedVerbose?: boolean; cachedDocuments?: Document[]; testOutput?: any; - form: FormHook; + form: FormHook<{ + documents: string | Document[]; + }>; validateAndTestPipeline: () => Promise; selectedTab: TestPipelineFlyoutTab; setSelectedTab: (selectedTa: TestPipelineFlyoutTab) => void; testingError: any; + resetTestOutput: () => void; } export interface TestPipelineConfig { @@ -45,6 +47,7 @@ export interface TestPipelineConfig { export const TestPipelineFlyout: React.FunctionComponent = ({ handleTestPipeline, + resetTestOutput, isRunningTest, cachedVerbose, cachedDocuments, @@ -71,19 +74,12 @@ export const TestPipelineFlyout: React.FunctionComponent = ({ } else { // default to "Documents" tab tabContent = ( -
- - + validateAndTestPipeline={validateAndTestPipeline} + isRunningTest={isRunningTest} + resetTestOutput={resetTestOutput} + /> ); } diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/documents_schema.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/documents_schema.tsx deleted file mode 100644 index e8ac223d56ed9..0000000000000 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/documents_schema.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; - -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import { EuiCode } from '@elastic/eui'; - -import { FormSchema, fieldValidators, ValidationFuncArg } from '../../../../../../shared_imports'; -import { parseJson, stringifyJson } from '../../../../../lib'; - -const { emptyField, isJsonField } = fieldValidators; - -export const documentsSchema: FormSchema = { - documents: { - label: i18n.translate( - 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel', - { - defaultMessage: 'Documents', - } - ), - helpText: ( - - {JSON.stringify([ - { - _index: 'index', - _id: 'id', - _source: { - foo: 'bar', - }, - }, - ])} - - ), - }} - /> - ), - serializer: parseJson, - deserializer: stringifyJson, - validations: [ - { - validator: emptyField( - i18n.translate( - 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.noDocumentsError', - { - defaultMessage: 'Documents are required.', - } - ) - ), - }, - { - validator: isJsonField( - i18n.translate( - 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError', - { - defaultMessage: 'The documents JSON is not valid.', - } - ) - ), - }, - { - validator: ({ value }: ValidationFuncArg) => { - const parsedJSON = JSON.parse(value); - - if (!parsedJSON.length) { - return { - message: i18n.translate( - 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.oneDocumentRequiredError', - { - defaultMessage: 'At least one document is required.', - } - ), - }; - } - }, - }, - ], - }, -}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx deleted file mode 100644 index b2326644340a7..0000000000000 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_documents.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - -import { EuiSpacer, EuiText, EuiButton, EuiLink } from '@elastic/eui'; - -import { getUseField, Field, JsonEditorField, useKibana } from '../../../../../../shared_imports'; - -const UseField = getUseField({ component: Field }); - -interface Props { - validateAndTestPipeline: () => Promise; - isRunningTest: boolean; - isSubmitButtonDisabled: boolean; -} - -export const DocumentsTab: React.FunctionComponent = ({ - validateAndTestPipeline, - isSubmitButtonDisabled, - isRunningTest, -}) => { - const { services } = useKibana(); - - return ( -
- -

- - {i18n.translate( - 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink', - { - defaultMessage: 'Learn more.', - } - )} - - ), - }} - /> -

-
- - - - {/* Documents editor */} - - - - - - {isRunningTest ? ( - - ) : ( - - )} - -
- ); -}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/index.ts similarity index 100% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/index.ts rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/index.ts diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_document_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_document_form.tsx new file mode 100644 index 0000000000000..7bb860facfeb2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_document_form.tsx @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, + EuiSpacer, + EuiText, + EuiIcon, +} from '@elastic/eui'; + +import { + getUseField, + Field, + useKibana, + useForm, + Form, + TextField, + fieldValidators, + FieldConfig, +} from '../../../../../../../shared_imports'; +import { useIsMounted } from '../../../../use_is_mounted'; +import { Document } from '../../../../types'; + +const UseField = getUseField({ component: Field }); + +const { emptyField } = fieldValidators; + +const i18nTexts = { + addDocumentButton: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.addDocumentButtonLabel', + { + defaultMessage: 'Add document', + } + ), + addDocumentErrorMessage: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.addDocumentErrorMessage', + { + defaultMessage: 'Error adding document', + } + ), + addDocumentSuccessMessage: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.addDocumentSuccessMessage', + { + defaultMessage: 'Document added', + } + ), + indexField: { + fieldLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.indexFieldLabel', + { + defaultMessage: 'Index', + } + ), + validationMessage: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.indexRequiredErrorMessage', + { + defaultMessage: 'An index name is required.', + } + ), + }, + idField: { + fieldLabel: i18n.translate('xpack.ingestPipelines.pipelineEditor.addDocuments.idFieldLabel', { + defaultMessage: 'Document ID', + }), + validationMessage: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocuments.idRequiredErrorMessage', + { + defaultMessage: 'A document ID is required.', + } + ), + }, +}; + +const fieldsConfig: Record = { + index: { + label: i18nTexts.indexField.fieldLabel, + validations: [ + { + validator: emptyField(i18nTexts.indexField.validationMessage), + }, + ], + }, + id: { + label: i18nTexts.idField.fieldLabel, + validations: [ + { + validator: emptyField(i18nTexts.idField.validationMessage), + }, + ], + }, +}; + +interface Props { + onAddDocuments: (document: Document) => void; +} + +export const AddDocumentForm: FunctionComponent = ({ onAddDocuments }) => { + const { services } = useKibana(); + const isMounted = useIsMounted(); + + const [isLoadingDocument, setIsLoadingDocument] = useState(false); + const [documentError, setDocumentError] = useState(undefined); + const [isDocumentAdded, setIsDocumentAdded] = useState(false); + + const { form } = useForm({ defaultValue: { index: '', id: '' } }); + + const submitForm = async (e: React.FormEvent) => { + const { isValid, data } = await form.submit(); + + const { id, index } = data; + + if (isValid) { + setIsLoadingDocument(true); + setDocumentError(undefined); + setIsDocumentAdded(false); + + const { error, data: document } = await services.api.loadDocument(index, id); + + if (!isMounted.current) { + return; + } + + setIsLoadingDocument(false); + + if (error) { + setDocumentError(error); + return; + } + + setIsDocumentAdded(true); + onAddDocuments(document); + } + }; + + return ( +
+ {documentError && ( + <> + +

{documentError.message}

+
+ + + + )} + + + + + + + + + + + {i18nTexts.addDocumentButton} + + + + {isDocumentAdded && ( + + + + + + + + {i18nTexts.addDocumentSuccessMessage} + + + + + )} + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.scss new file mode 100644 index 0000000000000..2bf234fab2ece --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.scss @@ -0,0 +1,4 @@ +.addDocumentsAccordion { + background-color: $euiColorLightestShade; + padding: $euiSizeM; +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.tsx new file mode 100644 index 0000000000000..9519d849e5d90 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/add_documents_accordion.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, { FunctionComponent, useState, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiAccordion, EuiText, EuiSpacer, EuiLink } from '@elastic/eui'; +import { UrlGeneratorsDefinition } from 'src/plugins/share/public'; + +import { useKibana } from '../../../../../../../../shared_imports'; +import { useIsMounted } from '../../../../../use_is_mounted'; +import { AddDocumentForm } from '../add_document_form'; + +import './add_documents_accordion.scss'; + +const DISCOVER_URL_GENERATOR_ID = 'DISCOVER_APP_URL_GENERATOR'; + +const i18nTexts = { + addDocumentsButton: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocumentsAccordion.addDocumentsButtonLabel', + { + defaultMessage: 'Add a test document from an index', + } + ), + addDocumentsDescription: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.addDocumentsAccordion.contentDescriptionText', + { + defaultMessage: `Provide the document's index and document ID.`, + } + ), +}; + +interface Props { + onAddDocuments: (document: any) => void; +} + +export const AddDocumentsAccordion: FunctionComponent = ({ onAddDocuments }) => { + const { services } = useKibana(); + const isMounted = useIsMounted(); + const [discoverLink, setDiscoverLink] = useState(undefined); + + useEffect(() => { + const getDiscoverUrl = async (): Promise => { + let isDeprecated: UrlGeneratorsDefinition['isDeprecated']; + let createUrl: UrlGeneratorsDefinition['createUrl']; + + // This try/catch may not be necessary once + // https://github.com/elastic/kibana/issues/78344 is addressed + try { + ({ isDeprecated, createUrl } = services.urlGenerators.getUrlGenerator( + DISCOVER_URL_GENERATOR_ID + )); + } catch (e) { + // Discover plugin is not enabled + setDiscoverLink(undefined); + return; + } + + if (isDeprecated) { + setDiscoverLink(undefined); + return; + } + + const discoverUrl = await createUrl({ indexPatternId: undefined }); + + if (isMounted.current) { + setDiscoverLink(discoverUrl); + } + }; + + getDiscoverUrl(); + }, [isMounted, services.urlGenerators]); + + return ( + +
+ +

+ {i18nTexts.addDocumentsDescription} + {discoverLink && ( + <> + {' '} + + Discover + + ), + }} + /> + + )} +

+
+ + + + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/index.ts new file mode 100644 index 0000000000000..cb00ec640b5a6 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/add_documents_accordion/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 { AddDocumentsAccordion } from './add_documents_accordion'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/index.ts new file mode 100644 index 0000000000000..1c3b6df577f4d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/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 { DocumentsTab } from './tab_documents'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/reset_documents_modal.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/reset_documents_modal.tsx new file mode 100644 index 0000000000000..2dbc6c56849dd --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/reset_documents_modal.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; + +interface Props { + confirmResetTestOutput: () => void; + closeModal: () => void; +} + +const i18nTexts = { + modalTitle: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.testPipeline.resetDocumentsModal.title', + { + defaultMessage: 'Clear documents', + } + ), + modalDescription: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.testPipeline.resetDocumentsModal.description', + { + defaultMessage: 'This will reset the output.', + } + ), + cancelButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.testPipeline.resetDocumentsModal.cancelButtonLabel', + { + defaultMessage: 'Cancel', + } + ), + resetButtonLabel: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.testPipeline.resetDocumentsModal.resetButtonLabel', + { + defaultMessage: 'Clear documents', + } + ), +}; + +export const ResetDocumentsModal: FunctionComponent = ({ + confirmResetTestOutput, + closeModal, +}) => { + return ( + + +

{i18nTexts.modalDescription}

+
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.scss new file mode 100644 index 0000000000000..c07f58d280e2a --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.scss @@ -0,0 +1,11 @@ +.documentsTab { + &__documentField { + position: relative; + + &__button { + position: absolute; + right: 0; + top: 0; + } + } +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.tsx new file mode 100644 index 0000000000000..ae784472ebbd9 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_documents/tab_documents.tsx @@ -0,0 +1,251 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useCallback, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { EuiSpacer, EuiText, EuiButton, EuiLink, EuiCode, EuiButtonEmpty } from '@elastic/eui'; + +import { parseJson, stringifyJson } from '../../../../../../lib'; +import { + getUseField, + Field, + JsonEditorField, + useKibana, + FieldConfig, + fieldValidators, + ValidationFuncArg, + FormHook, + Form, + useFormData, +} from '../../../../../../../shared_imports'; +import { Document } from '../../../../types'; +import { AddDocumentsAccordion } from './add_documents_accordion'; +import { ResetDocumentsModal } from './reset_documents_modal'; + +import './tab_documents.scss'; + +const UseField = getUseField({ component: Field }); + +const { emptyField, isJsonField } = fieldValidators; + +interface Props { + validateAndTestPipeline: () => Promise; + resetTestOutput: () => void; + isRunningTest: boolean; + form: FormHook<{ + documents: string | Document[]; + }>; +} + +const i18nTexts = { + learnMoreLink: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink', + { + defaultMessage: 'Learn more.', + } + ), + documentsEditorAriaLabel: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.editorFieldAriaLabel', + { + defaultMessage: 'Documents JSON editor', + } + ), + documentsEditorClearAllButton: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.editorFieldClearAllButtonLabel', + { + defaultMessage: 'Clear all', + } + ), + runButton: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.runButtonLabel', + { + defaultMessage: 'Run the pipeline', + } + ), + runningButton: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.runningButtonLabel', + { + defaultMessage: 'Running', + } + ), +}; + +const documentFieldConfig: FieldConfig = { + label: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel', + { + defaultMessage: 'Documents', + } + ), + helpText: ( + + {JSON.stringify([ + { + _index: 'index', + _id: 'id', + _source: { + foo: 'bar', + }, + }, + ])} + + ), + }} + /> + ), + serializer: parseJson, + deserializer: stringifyJson, + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.testPipelineFlyout.documentsForm.noDocumentsError', { + defaultMessage: 'Documents are required.', + }) + ), + }, + { + validator: isJsonField( + i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError', + { + defaultMessage: 'The documents JSON is not valid.', + } + ) + ), + }, + { + validator: ({ value }: ValidationFuncArg) => { + const parsedJSON = JSON.parse(value); + + if (!parsedJSON.length) { + return { + message: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.oneDocumentRequiredError', + { + defaultMessage: 'At least one document is required.', + } + ), + }; + } + }, + }, + ], +}; + +export const DocumentsTab: FunctionComponent = ({ + validateAndTestPipeline, + isRunningTest, + form, + resetTestOutput, +}) => { + const { services } = useKibana(); + + const [, formatData] = useFormData({ form }); + + const onAddDocumentHandler = useCallback( + (document) => { + const { documents: existingDocuments = [] } = formatData(); + + form.reset({ defaultValue: { documents: [...existingDocuments, document] } }); + }, + [form, formatData] + ); + + const [showResetModal, setShowResetModal] = useState(false); + + return ( +
+
+ +

+ + {i18nTexts.learnMoreLink} + + ), + }} + /> +

+
+ + + + + + + + {/* Documents editor */} + + {(field) => ( +
+ setShowResetModal(true)} + data-test-subj="clearAllDocumentsButton" + className="documentsTab__documentField__button" + > + {i18nTexts.documentsEditorClearAllButton} + + +
+ )} +
+ + + + + {isRunningTest ? i18nTexts.runningButton : i18nTexts.runButton} + + + {showResetModal && ( + { + resetTestOutput(); + form.reset({ defaultValue: { documents: [] } }); + setShowResetModal(false); + }} + closeModal={() => setShowResetModal(false)} + /> + )} +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_output.tsx similarity index 100% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/tab_output.tsx rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/tab_output.tsx diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/test_pipeline_tabs.tsx similarity index 100% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_flyout_tabs/test_pipeline_tabs.tsx rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/test_pipeline/test_pipeline_tabs/test_pipeline_tabs.tsx diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx index 9aafeafa10b27..a20cde5b1fb72 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/context/test_pipeline_context.tsx @@ -15,6 +15,7 @@ import { } from '../deserialize'; import { serialize } from '../serialize'; import { Document } from '../types'; +import { useIsMounted } from '../use_is_mounted'; export interface TestPipelineData { config: { @@ -50,11 +51,14 @@ type Action = | { type: 'updateIsExecutingPipeline'; payload: Pick; + } + | { + type: 'reset'; }; export interface TestPipelineContext { testPipelineData: TestPipelineData; - setCurrentTestPipelineData: (data: Action) => void; + testPipelineDataDispatch: (data: Action) => void; updateTestOutputPerProcessor: ( documents: Document[] | undefined, processors: DeserializeResult @@ -68,7 +72,7 @@ const DEFAULT_TEST_PIPELINE_CONTEXT = { }, isExecutingPipeline: false, }, - setCurrentTestPipelineData: () => {}, + testPipelineDataDispatch: () => {}, updateTestOutputPerProcessor: () => {}, }; @@ -121,12 +125,17 @@ export const reducer: Reducer = (state, action) => { }; } + if (action.type === 'reset') { + return DEFAULT_TEST_PIPELINE_CONTEXT.testPipelineData; + } + return state; }; export const TestPipelineContextProvider = ({ children }: { children: React.ReactNode }) => { const [state, dispatch] = useReducer(reducer, DEFAULT_TEST_PIPELINE_CONTEXT.testPipelineData); const { services } = useKibana(); + const isMounted = useIsMounted(); const updateTestOutputPerProcessor = useCallback( async (documents: Document[] | undefined, processors: DeserializeResult) => { @@ -152,6 +161,10 @@ export const TestPipelineContextProvider = ({ children }: { children: React.Reac pipeline: { ...serializedProcessorsWithTag }, }); + if (!isMounted.current) { + return; + } + if (error) { dispatch({ type: 'updateOutputPerProcessor', @@ -180,14 +193,14 @@ export const TestPipelineContextProvider = ({ children }: { children: React.Reac }, }); }, - [services.api, services.notifications.toasts] + [isMounted, services.api, services.notifications.toasts] ); return ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts index c462b19c79327..ae3dd9d673ebe 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/index.ts @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -export { PipelineProcessorsContextProvider, Props } from './context'; - -export { ProcessorsEditorContextProvider } from './context'; - -export { ProcessorsEditor, GlobalOnFailureProcessorsEditor } from './editors'; +export { Props, ProcessorsEditorContextProvider } from './context'; export { OnUpdateHandlerArg, OnUpdateHandler } from './types'; export { SerializeResult } from './serialize'; -export { LoadFromJsonButton, OnDoneLoadJsonHandler, TestPipelineActions } from './components'; +export { OnDoneLoadJsonHandler } from './components'; + +export { PipelineProcessorsEditor } from './pipeline_processors_editor'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.scss similarity index 100% rename from x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.scss rename to x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.scss diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx new file mode 100644 index 0000000000000..beb165973d3cd --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/pipeline_processors_editor.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { usePipelineProcessorsContext } from './context'; +import { + ProcessorsEmptyPrompt, + OnFailureProcessorsTitle, + ProcessorsHeader, + OnDoneLoadJsonHandler, +} from './components'; +import { ProcessorsEditor, GlobalOnFailureProcessorsEditor } from './editors'; + +import './pipeline_processors_editor.scss'; + +interface Props { + onLoadJson: OnDoneLoadJsonHandler; +} + +export const PipelineProcessorsEditor: React.FunctionComponent = ({ onLoadJson }) => { + const { + state: { processors: allProcessors }, + } = usePipelineProcessorsContext(); + + const { + state: { processors, onFailure }, + } = allProcessors; + + const showEmptyPrompt = processors.length === 0 && onFailure.length === 0; + + let content: React.ReactNode; + + if (showEmptyPrompt) { + content = ; + } else { + content = ( + <> + + + + + + + + + ); + } + + return ( +
+ + + 0} /> + + + {content} + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/use_is_mounted.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/use_is_mounted.ts new file mode 100644 index 0000000000000..c0df15e8a7fb7 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/use_is_mounted.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useRef } from 'react'; + +export const useIsMounted = () => { + const isMounted = useRef(false); + + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + }; + }, []); + + return isMounted; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/index.tsx b/x-pack/plugins/ingest_pipelines/public/application/index.tsx index 6ffebd1854b78..0a71babc53315 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/index.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/index.tsx @@ -9,6 +9,7 @@ import React, { ReactNode } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { NotificationsSetup, IUiSettingsClient } from 'kibana/public'; import { ManagementAppMountParams } from 'src/plugins/management/public'; +import { SharePluginStart } from 'src/plugins/share/public'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { API_BASE_PATH } from '../../common/constants'; @@ -26,6 +27,7 @@ export interface AppServices { notifications: NotificationsSetup; history: ManagementAppMountParams['history']; uiSettings: IUiSettingsClient; + urlGenerators: SharePluginStart['urlGenerators']; } export interface CoreServices { diff --git a/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts index 16ba9f9cd7a12..f7094a71a7792 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts @@ -6,15 +6,16 @@ import { CoreSetup } from 'src/core/public'; import { ManagementAppMountParams } from 'src/plugins/management/public'; +import { StartDependencies } from '../types'; import { documentationService, uiMetricService, apiService, breadcrumbService } from './services'; import { renderApp } from '.'; export async function mountManagementSection( - { http, getStartServices, notifications }: CoreSetup, + { http, getStartServices, notifications }: CoreSetup, params: ManagementAppMountParams ) { const { element, setBreadcrumbs, history } = params; - const [coreStart] = await getStartServices(); + const [coreStart, depsStart] = await getStartServices(); const { docLinks, i18n: { Context: I18nContext }, @@ -31,6 +32,7 @@ export async function mountManagementSection( notifications, history, uiSettings: coreStart.uiSettings, + urlGenerators: depsStart.share.urlGenerators, }; return renderApp(element, I18nContext, services, { http }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx index acca1c4e03f40..d4aa11715248e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx @@ -16,7 +16,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { BASE_PATH } from '../../../../common/constants'; +import { getListPath } from '../../services/navigation'; import { Pipeline } from '../../../../common/types'; import { useKibana } from '../../../shared_imports'; import { PipelineForm } from '../../components'; @@ -50,11 +50,11 @@ export const PipelinesCreate: React.FunctionComponent { - history.push(BASE_PATH); + history.push(getListPath()); }; useEffect(() => { diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx index e09cf4820771f..35ca1635ab9c3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx @@ -17,11 +17,11 @@ import { } from '@elastic/eui'; import { EuiCallOut } from '@elastic/eui'; -import { BASE_PATH } from '../../../../common/constants'; import { Pipeline } from '../../../../common/types'; import { useKibana, SectionLoading } from '../../../shared_imports'; -import { PipelineForm } from '../../components'; +import { getListPath } from '../../services/navigation'; +import { PipelineForm } from '../../components'; import { attemptToURIDecode } from '../shared'; interface MatchParams { @@ -56,11 +56,11 @@ export const PipelinesEdit: React.FunctionComponent { - history.push(BASE_PATH); + history.push(getListPath()); }; useEffect(() => { diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx index eba69ff454911..7f4caa09b6df0 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx @@ -11,6 +11,7 @@ import { useHistory } from 'react-router-dom'; import { ScopedHistory } from 'kibana/public'; import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public'; import { useKibana } from '../../../shared_imports'; +import { getCreatePath } from '../../services/navigation'; export const EmptyList: FunctionComponent = () => { const { services } = useKibana(); @@ -44,7 +45,11 @@ export const EmptyList: FunctionComponent = () => {

} actions={ - + {i18n.translate('xpack.ingestPipelines.list.table.emptyPrompt.createButtonLabel', { defaultMessage: 'Create a pipeline', })} diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx index 88148f1bc5746..be31f86e30c27 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -24,9 +24,9 @@ import { } from '@elastic/eui'; import { Pipeline } from '../../../../common/types'; -import { BASE_PATH } from '../../../../common/constants'; import { useKibana, SectionLoading } from '../../../shared_imports'; import { UIM_PIPELINES_LIST_LOAD } from '../../constants'; +import { getEditPath, getClonePath, getListPath } from '../../services/navigation'; import { EmptyList } from './empty_list'; import { PipelineTable } from './table'; @@ -67,17 +67,17 @@ export const PipelinesList: React.FunctionComponent = ({ } }, [pipelineNameFromLocation, data]); - const goToEditPipeline = (name: string) => { - history.push(`${BASE_PATH}/edit/${encodeURIComponent(name)}`); + const goToEditPipeline = (pipelineName: string) => { + history.push(getEditPath({ pipelineName })); }; - const goToClonePipeline = (name: string) => { - history.push(`${BASE_PATH}/create/${encodeURIComponent(name)}`); + const goToClonePipeline = (clonedPipelineName: string) => { + history.push(getClonePath({ clonedPipelineName })); }; const goHome = () => { setShowFlyout(false); - history.push(BASE_PATH); + history.push(getListPath()); }; if (data && data.length === 0) { diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts index 552e0ed0c41b2..2d6ab0477a603 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts @@ -120,6 +120,15 @@ export class ApiService { return result; } + + public async loadDocument(index: string, id: string) { + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/documents/${encodeURIComponent(index)}/${encodeURIComponent(id)}`, + method: 'get', + }); + + return result; + } } export const apiService = new ApiService(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/navigation.ts b/x-pack/plugins/ingest_pipelines/public/application/services/navigation.ts new file mode 100644 index 0000000000000..3ac3de6eac710 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/navigation.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +const BASE_PATH = '/'; + +const EDIT_PATH = 'edit'; + +const CREATE_PATH = 'create'; + +const _getEditPath = (name: string, encode = true): string => { + return `${BASE_PATH}${EDIT_PATH}/${encode ? encodeURIComponent(name) : name}`; +}; + +const _getCreatePath = (): string => { + return `${BASE_PATH}${CREATE_PATH}`; +}; + +const _getClonePath = (name: string, encode = true): string => { + return `${BASE_PATH}${CREATE_PATH}/${encode ? encodeURIComponent(name) : name}`; +}; +const _getListPath = (name?: string): string => { + return `${BASE_PATH}${name ? `?pipeline=${encodeURIComponent(name)}` : ''}`; +}; + +export const ROUTES = { + list: _getListPath(), + edit: _getEditPath(':name', false), + create: _getCreatePath(), + clone: _getClonePath(':sourceName', false), +}; + +export const getListPath = ({ + inspectedPipelineName, +}: { + inspectedPipelineName?: string; +} = {}): string => _getListPath(inspectedPipelineName); +export const getEditPath = ({ pipelineName }: { pipelineName: string }): string => + _getEditPath(pipelineName, true); +export const getCreatePath = (): string => _getCreatePath(); +export const getClonePath = ({ clonedPipelineName }: { clonedPipelineName: string }): string => + _getClonePath(clonedPipelineName, true); diff --git a/x-pack/plugins/ingest_pipelines/public/index.ts b/x-pack/plugins/ingest_pipelines/public/index.ts index 7247973703804..637d4aad7264a 100644 --- a/x-pack/plugins/ingest_pipelines/public/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/index.ts @@ -9,3 +9,10 @@ import { IngestPipelinesPlugin } from './plugin'; export function plugin() { return new IngestPipelinesPlugin(); } + +export { + INGEST_PIPELINES_APP_ULR_GENERATOR, + IngestPipelinesUrlGenerator, + IngestPipelinesUrlGeneratorState, + INGEST_PIPELINES_PAGES, +} from './url_generator'; diff --git a/x-pack/plugins/ingest_pipelines/public/plugin.ts b/x-pack/plugins/ingest_pipelines/public/plugin.ts index 339068f185d1d..8b60967702742 100644 --- a/x-pack/plugins/ingest_pipelines/public/plugin.ts +++ b/x-pack/plugins/ingest_pipelines/public/plugin.ts @@ -9,11 +9,13 @@ import { CoreSetup, Plugin } from 'src/core/public'; import { PLUGIN_ID } from '../common/constants'; import { uiMetricService, apiService } from './application/services'; -import { Dependencies } from './types'; +import { SetupDependencies, StartDependencies } from './types'; +import { registerUrlGenerator } from './url_generator'; -export class IngestPipelinesPlugin implements Plugin { - public setup(coreSetup: CoreSetup, plugins: Dependencies): void { - const { management, usageCollection } = plugins; +export class IngestPipelinesPlugin + implements Plugin { + public setup(coreSetup: CoreSetup, plugins: SetupDependencies): void { + const { management, usageCollection, share } = plugins; const { http, getStartServices } = coreSetup; // Initialize services @@ -46,6 +48,8 @@ export class IngestPipelinesPlugin implements Plugin { }; }, }); + + registerUrlGenerator(coreSetup, management, share); } public start() {} diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts index abdbdf2140400..13de8a74225ab 100644 --- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -21,7 +21,7 @@ export { useRequest, UseRequestConfig, WithPrivileges, - Monaco, + XJson, JsonEditor, OnJsonEditorUpdateHandler, } from '../../../../src/plugins/es_ui_shared/public/'; @@ -36,6 +36,8 @@ export { ValidationFuncArg, FormData, UseField, + UseArray, + ArrayItem, FormHook, useFormContext, FormDataProvider, @@ -45,6 +47,7 @@ export { getFieldValidityAndErrorMessage, ValidationFunc, ValidationConfig, + useFormData, } from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; export { @@ -63,6 +66,7 @@ export { NumericField, SelectField, CheckBoxField, + TextField, } from '../../../../src/plugins/es_ui_shared/static/forms/components'; export { @@ -70,4 +74,6 @@ export { isEmptyString, } from '../../../../src/plugins/es_ui_shared/static/validators/string'; +export { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; + export const useKibana = () => _useKibana(); diff --git a/x-pack/plugins/ingest_pipelines/public/types.ts b/x-pack/plugins/ingest_pipelines/public/types.ts index 91783ea04fa9a..1638e60e98505 100644 --- a/x-pack/plugins/ingest_pipelines/public/types.ts +++ b/x-pack/plugins/ingest_pipelines/public/types.ts @@ -6,8 +6,14 @@ import { ManagementSetup } from 'src/plugins/management/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { SharePluginStart, SharePluginSetup } from 'src/plugins/share/public'; -export interface Dependencies { +export interface SetupDependencies { management: ManagementSetup; usageCollection: UsageCollectionSetup; + share: SharePluginSetup; +} + +export interface StartDependencies { + share: SharePluginStart; } diff --git a/x-pack/plugins/ingest_pipelines/public/url_generator.test.ts b/x-pack/plugins/ingest_pipelines/public/url_generator.test.ts new file mode 100644 index 0000000000000..1267d526fb7d4 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/url_generator.test.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { IngestPipelinesUrlGenerator, INGEST_PIPELINES_PAGES } from './url_generator'; + +describe('IngestPipelinesUrlGenerator', () => { + const getAppBasePath = (absolute: boolean = false) => { + if (absolute) { + return Promise.resolve('http://localhost/app/test_app'); + } + return Promise.resolve('/app/test_app'); + }; + const urlGenerator = new IngestPipelinesUrlGenerator(getAppBasePath); + + describe('Pipelines List', () => { + it('generates relative url for list without pipelineId', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.LIST, + }); + expect(url).toBe('/app/test_app/'); + }); + + it('generates absolute url for list without pipelineId', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.LIST, + absolute: true, + }); + expect(url).toBe('http://localhost/app/test_app/'); + }); + it('generates relative url for list with a pipelineId', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.LIST, + pipelineId: 'pipeline_name', + }); + expect(url).toBe('/app/test_app/?pipeline=pipeline_name'); + }); + + it('generates absolute url for list with a pipelineId', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.LIST, + pipelineId: 'pipeline_name', + absolute: true, + }); + expect(url).toBe('http://localhost/app/test_app/?pipeline=pipeline_name'); + }); + }); + + describe('Pipeline Edit', () => { + it('generates relative url for pipeline edit', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.EDIT, + pipelineId: 'pipeline_name', + }); + expect(url).toBe('/app/test_app/edit/pipeline_name'); + }); + + it('generates absolute url for pipeline edit', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.EDIT, + pipelineId: 'pipeline_name', + absolute: true, + }); + expect(url).toBe('http://localhost/app/test_app/edit/pipeline_name'); + }); + }); + + describe('Pipeline Clone', () => { + it('generates relative url for pipeline clone', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.CLONE, + pipelineId: 'pipeline_name', + }); + expect(url).toBe('/app/test_app/create/pipeline_name'); + }); + + it('generates absolute url for pipeline clone', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.CLONE, + pipelineId: 'pipeline_name', + absolute: true, + }); + expect(url).toBe('http://localhost/app/test_app/create/pipeline_name'); + }); + }); + + describe('Pipeline Create', () => { + it('generates relative url for pipeline create', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.CREATE, + pipelineId: 'pipeline_name', + }); + expect(url).toBe('/app/test_app/create'); + }); + + it('generates absolute url for pipeline create', async () => { + const url = await urlGenerator.createUrl({ + page: INGEST_PIPELINES_PAGES.CREATE, + pipelineId: 'pipeline_name', + absolute: true, + }); + expect(url).toBe('http://localhost/app/test_app/create'); + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/url_generator.ts b/x-pack/plugins/ingest_pipelines/public/url_generator.ts new file mode 100644 index 0000000000000..c53ff083ea098 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/url_generator.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup } from 'src/core/public'; +import { MANAGEMENT_APP_ID } from '../../../../src/plugins/management/public'; +import { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; +import { + getClonePath, + getCreatePath, + getEditPath, + getListPath, +} from './application/services/navigation'; +import { SetupDependencies } from './types'; +import { PLUGIN_ID } from '../common/constants'; + +export const INGEST_PIPELINES_APP_ULR_GENERATOR = 'INGEST_PIPELINES_APP_URL_GENERATOR'; + +export enum INGEST_PIPELINES_PAGES { + LIST = 'pipelines_list', + EDIT = 'pipeline_edit', + CREATE = 'pipeline_create', + CLONE = 'pipeline_clone', +} + +interface UrlGeneratorState { + pipelineId: string; + absolute?: boolean; +} +export interface PipelinesListUrlGeneratorState extends Partial { + page: INGEST_PIPELINES_PAGES.LIST; +} + +export interface PipelineEditUrlGeneratorState extends UrlGeneratorState { + page: INGEST_PIPELINES_PAGES.EDIT; +} + +export interface PipelineCloneUrlGeneratorState extends UrlGeneratorState { + page: INGEST_PIPELINES_PAGES.CLONE; +} + +export interface PipelineCreateUrlGeneratorState extends UrlGeneratorState { + page: INGEST_PIPELINES_PAGES.CREATE; +} + +export type IngestPipelinesUrlGeneratorState = + | PipelinesListUrlGeneratorState + | PipelineEditUrlGeneratorState + | PipelineCloneUrlGeneratorState + | PipelineCreateUrlGeneratorState; + +export class IngestPipelinesUrlGenerator + implements UrlGeneratorsDefinition { + constructor(private readonly getAppBasePath: (absolute: boolean) => Promise) {} + + public readonly id = INGEST_PIPELINES_APP_ULR_GENERATOR; + + public readonly createUrl = async (state: IngestPipelinesUrlGeneratorState): Promise => { + switch (state.page) { + case INGEST_PIPELINES_PAGES.EDIT: { + return `${await this.getAppBasePath(!!state.absolute)}${getEditPath({ + pipelineName: state.pipelineId, + })}`; + } + case INGEST_PIPELINES_PAGES.CREATE: { + return `${await this.getAppBasePath(!!state.absolute)}${getCreatePath()}`; + } + case INGEST_PIPELINES_PAGES.LIST: { + return `${await this.getAppBasePath(!!state.absolute)}${getListPath({ + inspectedPipelineName: state.pipelineId, + })}`; + } + case INGEST_PIPELINES_PAGES.CLONE: { + return `${await this.getAppBasePath(!!state.absolute)}${getClonePath({ + clonedPipelineName: state.pipelineId, + })}`; + } + } + }; +} + +export const registerUrlGenerator = ( + coreSetup: CoreSetup, + management: SetupDependencies['management'], + share: SetupDependencies['share'] +) => { + const getAppBasePath = async (absolute = false) => { + const [coreStart] = await coreSetup.getStartServices(); + return coreStart.application.getUrlForApp(MANAGEMENT_APP_ID, { + path: management.sections.section.ingest.getApp(PLUGIN_ID)!.basePath, + absolute: !!absolute, + }); + }; + + share.urlGenerators.registerUrlGenerator(new IngestPipelinesUrlGenerator(getAppBasePath)); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts new file mode 100644 index 0000000000000..1f19112e069d5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const paramsSchema = schema.object({ + index: schema.string(), + id: schema.string(), +}); + +export const registerDocumentsRoute = ({ + router, + license, + lib: { isEsError }, +}: RouteDependencies): void => { + router.get( + { + path: `${API_BASE_PATH}/documents/{index}/{id}`, + validate: { + params: paramsSchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.legacy.client; + const { index, id } = req.params; + + try { + const document = await callAsCurrentUser('get', { index, id }); + + const { _id, _index, _source } = document; + + return res.ok({ + body: { + _id, + _index, + _source, + }, + }); + } catch (error) { + if (isEsError(error)) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts index 58a4bf5617659..7c0ab19917d1f 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts @@ -15,3 +15,5 @@ export { registerPrivilegesRoute } from './privileges'; export { registerDeleteRoute } from './delete'; export { registerSimulateRoute } from './simulate'; + +export { registerDocumentsRoute } from './documents'; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/index.ts index f703a460143f4..5e80be4388b25 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/index.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/index.ts @@ -13,6 +13,7 @@ import { registerPrivilegesRoute, registerDeleteRoute, registerSimulateRoute, + registerDocumentsRoute, } from './api'; export class ApiRoutes { @@ -23,5 +24,6 @@ export class ApiRoutes { registerPrivilegesRoute(dependencies); registerDeleteRoute(dependencies); registerSimulateRoute(dependencies); + registerDocumentsRoute(dependencies); } } diff --git a/x-pack/plugins/lens/common/constants.ts b/x-pack/plugins/lens/common/constants.ts index ea2331a577743..d30ab5962667d 100644 --- a/x-pack/plugins/lens/common/constants.ts +++ b/x-pack/plugins/lens/common/constants.ts @@ -8,15 +8,16 @@ export const PLUGIN_ID = 'lens'; export const LENS_EMBEDDABLE_TYPE = 'lens'; export const NOT_INTERNATIONALIZED_PRODUCT_NAME = 'Lens Visualizations'; export const BASE_API_URL = '/api/lens'; +export const LENS_EDIT_BY_VALUE = 'edit_by_value'; export function getBasePath() { return `#/`; } -export function getEditPath(id: string) { - return `#/edit/${encodeURIComponent(id)}`; +export function getEditPath(id: string | undefined) { + return id ? `#/edit/${encodeURIComponent(id)}` : `#/${LENS_EDIT_BY_VALUE}`; } -export function getFullPath(id: string) { - return `/app/${PLUGIN_ID}${getEditPath(id)}`; +export function getFullPath(id?: string) { + return `/app/${PLUGIN_ID}${id ? getEditPath(id) : getBasePath()}`; } diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 67d9d5ef64483..f5fba766e60ee 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -13,7 +13,7 @@ "dashboard", "charts" ], - "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions"], + "optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions", "globalSearch"], "configPath": ["xpack", "lens"], "extraPublicDirs": ["common/constants"], "requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable"] diff --git a/x-pack/plugins/lens/public/app_plugin/_index.scss b/x-pack/plugins/lens/public/app_plugin/_index.scss deleted file mode 100644 index e72e824224956..0000000000000 --- a/x-pack/plugins/lens/public/app_plugin/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'app'; diff --git a/x-pack/plugins/lens/public/app_plugin/_app.scss b/x-pack/plugins/lens/public/app_plugin/app.scss similarity index 100% rename from x-pack/plugins/lens/public/app_plugin/_app.scss rename to x-pack/plugins/lens/public/app_plugin/app.scss diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 63c2a6b9b2f29..24114e2b31518 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -9,29 +9,36 @@ import { Observable } from 'rxjs'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { App } from './app'; +import { LensAppProps, LensAppServices } from './types'; import { EditorFrameInstance } from '../types'; -import { AppMountParameters } from 'kibana/public'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { Document, SavedObjectStore } from '../persistence'; +import { Document, DOC_TYPE } from '../persistence'; import { mount } from 'enzyme'; +import { I18nProvider } from '@kbn/i18n/react'; import { SavedObjectSaveModal, checkForDuplicateTitle, } from '../../../../../src/plugins/saved_objects/public'; -import { createMemoryHistory, History } from 'history'; +import { createMemoryHistory } from 'history'; import { + DataPublicPluginStart, esFilters, FilterManager, IFieldType, IIndexPattern, UI_SETTINGS, } from '../../../../../src/plugins/data/public'; -import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; -const dataStartMock = dataPluginMock.createStartContract(); - import { navigationPluginMock } from '../../../../../src/plugins/navigation/public/mocks'; import { TopNavMenuData } from '../../../../../src/plugins/navigation/public'; import { coreMock } from 'src/core/public/mocks'; +import { + LensByValueInput, + LensSavedObjectAttributes, + LensByReferenceInput, +} from '../editor_frame_service/embeddable/embeddable'; +import { SavedObjectReference } from '../../../../../src/core/types'; +import { mockAttributeService } from '../../../../../src/plugins/dashboard/public/mocks'; +import { LensAttributeService } from '../lens_attribute_service'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; jest.mock('../editor_frame_service/editor_frame/expression_helpers'); jest.mock('src/core/public'); @@ -120,39 +127,68 @@ function createMockTimefilter() { } describe('Lens App', () => { - let frame: jest.Mocked; let core: ReturnType; - let instance: ReactWrapper; - - function makeDefaultArgs(): jest.Mocked<{ - editorFrame: EditorFrameInstance; - data: typeof dataStartMock; - navigation: typeof navigationStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; - }> { - return ({ - navigation: navigationStartMock, + let defaultDoc: Document; + let defaultSavedObjectId: string; + + const navMenuItems = { + expectedSaveButton: { emphasize: true, testId: 'lnsApp_saveButton' }, + expectedSaveAsButton: { emphasize: false, testId: 'lnsApp_saveButton' }, + expectedSaveAndReturnButton: { emphasize: true, testId: 'lnsApp_saveAndReturnButton' }, + }; + + function makeAttributeService(): LensAttributeService { + const attributeServiceMock = mockAttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >( + DOC_TYPE, + { + customSaveMethod: jest.fn(), + customUnwrapMethod: jest.fn(), + }, + core + ); + attributeServiceMock.unwrapAttributes = jest.fn().mockResolvedValue(defaultDoc); + attributeServiceMock.wrapAttributes = jest + .fn() + .mockResolvedValue({ savedObjectId: defaultSavedObjectId }); + return attributeServiceMock; + } + + function makeDefaultProps(): jest.Mocked { + return { editorFrame: createMockFrame(), - core: { - ...core, - application: { - ...core.application, - capabilities: { - ...core.application.capabilities, - visualize: { save: true, saveQuery: true, show: true }, - }, + history: createMemoryHistory(), + redirectTo: jest.fn(), + redirectToOrigin: jest.fn(), + onAppLeave: jest.fn(), + setHeaderActionMenu: jest.fn(), + }; + } + + function makeDefaultServices(): jest.Mocked { + return { + http: core.http, + chrome: core.chrome, + overlays: core.overlays, + uiSettings: core.uiSettings, + navigation: navigationStartMock, + notifications: core.notifications, + attributeService: makeAttributeService(), + savedObjectsClient: core.savedObjects.client, + dashboardFeatureFlag: { allowByValueEmbeddables: false }, + getOriginatingAppName: jest.fn(() => 'defaultOriginatingApp'), + application: { + ...core.application, + capabilities: { + ...core.application.capabilities, + visualize: { save: true, saveQuery: true, show: true }, }, + getUrlForApp: jest.fn((appId: string) => `/testbasepath/app/${appId}#/`), }, - data: { + data: ({ query: { filterManager: createMockFilterManager(), timefilter: { @@ -166,38 +202,52 @@ describe('Lens App', () => { return new Promise((resolve) => resolve({ id })); }), }, - }, + } as unknown) as DataPublicPluginStart, storage: { get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), }, - docStorage: { - load: jest.fn(), - save: jest.fn(), - }, - redirectTo: jest.fn((id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => {}), - onAppLeave: jest.fn(), - setHeaderActionMenu: jest.fn(), - history: createMemoryHistory(), - } as unknown) as jest.Mocked<{ - navigation: typeof navigationStartMock; - editorFrame: EditorFrameInstance; - data: typeof dataStartMock; - core: typeof core; - storage: Storage; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; - }>; + }; + } + + function mountWith({ + props: incomingProps, + services: incomingServices, + }: { + props?: jest.Mocked; + services?: jest.Mocked; + }) { + const props = incomingProps ?? makeDefaultProps(); + const services = incomingServices ?? makeDefaultServices(); + const wrappingComponent: React.FC<{ + children: React.ReactNode; + }> = ({ children }) => { + return ( + + {children} + + ); + }; + const frame = props.editorFrame as ReturnType; + const component = mount(, { wrappingComponent }); + return { component, frame, props, services }; } beforeEach(() => { - frame = createMockFrame(); core = coreMock.createStart({ basePath: '/testbasepath' }); + defaultSavedObjectId = '1234'; + defaultDoc = ({ + savedObjectId: defaultSavedObjectId, + title: 'An extremely cool default document!', + expression: 'definitely a valid expression', + state: { + query: 'kuery', + filters: [{ query: { match_phrase: { src: 'test' } } }], + }, + references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], + } as unknown) as Document; core.uiSettings.get.mockImplementation( jest.fn((type) => { @@ -215,10 +265,7 @@ describe('Lens App', () => { }); it('renders the editor frame', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - mount(); + const { frame } = mountWith({}); expect(frame.mount.mock.calls).toMatchInlineSnapshot(` Array [ @@ -248,23 +295,22 @@ describe('Lens App', () => { }); it('clears app filters on load', () => { - const defaultArgs = makeDefaultArgs(); - mount(); - - expect(defaultArgs.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([]); + const { services } = mountWith({}); + expect(services.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([]); }); it('passes global filters to frame', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; + const services = makeDefaultServices(); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; const pinnedFilter = esFilters.buildExistsFilter(pinnedField, indexPattern); - args.data.query.filterManager.getFilters = jest.fn().mockImplementation(() => { + services.data.query.filterManager.getFilters = jest.fn().mockImplementation(() => { return [pinnedFilter]; }); - const component = mount(); + const { component, frame } = mountWith({ services }); + component.update(); + expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ @@ -275,103 +321,81 @@ describe('Lens App', () => { ); }); - it('sets breadcrumbs when the document title changes', async () => { - const defaultArgs = makeDefaultArgs(); - instance = mount(); - - expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Create' }, - ]); + it('displays errors from the frame in a toast', () => { + const { component, frame, services } = mountWith({}); + const onError = frame.mount.mock.calls[0][1].onError; + onError({ message: 'error' }); + component.update(); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + }); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + describe('breadcrumbs', () => { + const breadcrumbDocSavedObjectId = defaultSavedObjectId; + const breadcrumbDoc = ({ + savedObjectId: breadcrumbDocSavedObjectId, title: 'Daaaaaaadaumching!', state: { query: 'fake query', filters: [], }, references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); - }); + } as unknown) as Document; - expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Daaaaaaadaumching!' }, - ]); - }); + it('sets breadcrumbs when the document title changes', async () => { + const { component, services } = mountWith({}); - it('adds to the recently viewed list on load', async () => { - const defaultArgs = makeDefaultArgs(); - instance = mount(); + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Create' }, + ]); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'Daaaaaaadaumching!', - state: { - query: 'fake query', - filters: [], - }, - references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue(breadcrumbDoc); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: breadcrumbDocSavedObjectId } }); + }); + + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Daaaaaaadaumching!' }, + ]); }); - expect(defaultArgs.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( - '/app/lens#/edit/1234', - 'Daaaaaaadaumching!', - '1234' - ); - }); - it('sets originatingApp breadcrumb when the document title changes', async () => { - const defaultArgs = makeDefaultArgs(); - defaultArgs.originatingApp = 'ultraCoolDashboard'; - defaultArgs.getAppNameFromId = () => 'The Coolest Container Ever Made'; - instance = mount(); + it('sets originatingApp breadcrumb when the document title changes', async () => { + const props = makeDefaultProps(); + const services = makeDefaultServices(); + props.incomingState = { originatingApp: 'coolContainer' }; + services.getOriginatingAppName = jest.fn(() => 'The Coolest Container Ever Made'); + const { component } = mountWith({ props, services }); + + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Create' }, + ]); - expect(core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Create' }, - ]); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue(breadcrumbDoc); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: breadcrumbDocSavedObjectId } }); + }); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'Daaaaaaadaumching!', - state: { - query: 'fake query', - filters: [], - }, - references: [], - }); - await act(async () => { - instance.setProps({ docId: '1234' }); + expect(services.chrome.setBreadcrumbs).toHaveBeenCalledWith([ + { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, + { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, + { text: 'Daaaaaaadaumching!' }, + ]); }); - - expect(defaultArgs.core.chrome.setBreadcrumbs).toHaveBeenCalledWith([ - { text: 'The Coolest Container Ever Made', onClick: expect.anything() }, - { text: 'Visualize', href: '/testbasepath/app/visualize#/', onClick: expect.anything() }, - { text: 'Daaaaaaadaumching!' }, - ]); }); describe('persistence', () => { - it('does not load a document if there is no document id', () => { - const args = makeDefaultArgs(); - - mount(); - - expect(args.docStorage.load).not.toHaveBeenCalled(); + it('does not load a document if there is no initial input', () => { + const { services } = mountWith({}); + expect(services.attributeService.unwrapAttributes).not.toHaveBeenCalled(); }); - it('loads a document and uses query and filters if there is a document id', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + it('loads a document and uses query and filters if initial input is provided', async () => { + const { component, frame, services } = mountWith({}); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ + savedObjectId: defaultSavedObjectId, state: { query: 'fake query', filters: [{ query: { match_phrase: { src: 'test' } } }], @@ -379,15 +403,15 @@ describe('Lens App', () => { references: [{ type: 'index-pattern', id: '1', name: 'index-pattern-0' }], }); - instance = mount(); - await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - expect(args.docStorage.load).toHaveBeenCalledWith('1234'); - expect(args.data.indexPatterns.get).toHaveBeenCalledWith('1'); - expect(args.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([ + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledWith({ + savedObjectId: defaultSavedObjectId, + }); + expect(services.data.indexPatterns.get).toHaveBeenCalledWith('1'); + expect(services.data.query.filterManager.setAppFilters).toHaveBeenCalledWith([ { query: { match_phrase: { src: 'test' } } }, ]); expect(TopNavMenu).toHaveBeenCalledWith( @@ -401,7 +425,7 @@ describe('Lens App', () => { expect.any(Element), expect.objectContaining({ doc: expect.objectContaining({ - id: '1234', + savedObjectId: defaultSavedObjectId, state: expect.objectContaining({ query: 'fake query', filters: [{ query: { match_phrase: { src: 'test' } } }], @@ -412,65 +436,59 @@ describe('Lens App', () => { }); it('does not load documents on sequential renders unless the id changes', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ id: '1234' }); + const { services, component } = mountWith({}); - instance = mount(); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); await act(async () => { - instance.setProps({ docId: '9876' }); + component.setProps({ initialInput: { savedObjectId: '5678' } }); }); - expect(args.docStorage.load).toHaveBeenCalledTimes(2); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(2); }); it('handles document load errors', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockRejectedValue('failed to load'); - - instance = mount(); + const services = makeDefaultServices(); + services.attributeService.unwrapAttributes = jest.fn().mockRejectedValue('failed to load'); + const { component, props } = mountWith({ services }); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - expect(args.docStorage.load).toHaveBeenCalledWith('1234'); - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - expect(args.redirectTo).toHaveBeenCalled(); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledWith({ + savedObjectId: defaultSavedObjectId, + }); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + expect(props.redirectTo).toHaveBeenCalled(); }); - describe('save button', () => { + it('adds to the recently accessed list on load', async () => { + const { component, services } = mountWith({}); + + await act(async () => { + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); + }); + expect(services.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + '/app/lens#/edit/1234', + 'An extremely cool default document!', + '1234' + ); + }); + + describe('save buttons', () => { interface SaveProps { newCopyOnSave: boolean; returnToOrigin?: boolean; newTitle: string; } - let defaultArgs: ReturnType; - - beforeEach(() => { - defaultArgs = makeDefaultArgs(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - expression: 'valid expression', - state: { - query: 'kuery', - }, - } as jest.ResolvedValue); - }); - function getButton(inst: ReactWrapper): TopNavMenuData { return (inst .find('[data-test-subj="lnsApp_topNav"]') @@ -495,135 +513,195 @@ describe('Lens App', () => { filters: [], }, }, - initialDocId, + initialSavedObjectId, ...saveProps }: SaveProps & { lastKnownDoc?: object; - initialDocId?: string; + initialSavedObjectId?: string; }) { - const args = { - ...defaultArgs, - docId: initialDocId, + const props = { + ...makeDefaultProps(), + initialInput: initialSavedObjectId + ? { savedObjectId: initialSavedObjectId, id: '5678' } + : undefined, }; - args.editorFrame = frame; - (args.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', + + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockImplementation(async ({ savedObjectId }) => ({ + savedObjectId: savedObjectId || 'aaa', + })); + services.attributeService.unwrapAttributes = jest.fn().mockResolvedValue({ + savedObjectId: initialSavedObjectId ?? 'aaa', references: [], state: { query: 'fake query', filters: [], }, - }); - (args.docStorage.save as jest.Mock).mockImplementation(async ({ id }) => ({ - id: id || 'aaa', - })); + } as jest.ResolvedValue); + let frame: jest.Mocked = {} as jest.Mocked; + let component: ReactWrapper = {} as ReactWrapper; await act(async () => { - instance = mount(); + const { frame: newFrame, component: newComponent } = mountWith({ services, props }); + frame = newFrame; + component = newComponent; }); - if (initialDocId) { - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + if (initialSavedObjectId) { + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); } else { - expect(args.docStorage.load).not.toHaveBeenCalled(); + expect(services.attributeService.unwrapAttributes).not.toHaveBeenCalled(); } const onChange = frame.mount.mock.calls[0][1].onChange; + act(() => onChange({ filterableIndexPatterns: [], - doc: { id: initialDocId, ...lastKnownDoc } as Document, + doc: { savedObjectId: initialSavedObjectId, ...lastKnownDoc } as Document, isSaveable: true, }) ); - - instance.update(); - - expect(getButton(instance).disableButton).toEqual(false); - + component.update(); + expect(getButton(component).disableButton).toEqual(false); await act(async () => { - testSave(instance, { ...saveProps }); + testSave(component, { ...saveProps }); }); - - return { args, instance }; + return { props, services, component, frame }; } it('shows a disabled save button when the user does not have permissions', async () => { - const args = defaultArgs; - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - args.editorFrame = frame; - - instance = mount(); - - expect(getButton(instance).disableButton).toEqual(true); - + const { component, frame } = mountWith({ services }); + expect(getButton(component).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: 'will save this' } as unknown) as Document, + doc: ({ savedObjectId: 'will save this' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - expect(getButton(instance).disableButton).toEqual(true); + component.update(); + expect(getButton(component).disableButton).toEqual(true); }); - it('shows a save button that is enabled when the frame has provided its state', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - - expect(getButton(instance).disableButton).toEqual(true); - + it('shows a save button that is enabled when the frame has provided its state and does not show save and return or save as', async () => { + const { component, frame } = mountWith({}); + expect(getButton(component).disableButton).toEqual(true); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: 'will save this' } as unknown) as Document, + doc: ({ savedObjectId: 'will save this' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); + component.update(); + expect(getButton(component).disableButton).toEqual(false); - expect(getButton(instance).disableButton).toEqual(false); + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); + }); + + it('Shows Save and Return and Save As buttons in create by value mode', async () => { + const props = makeDefaultProps(); + const services = makeDefaultServices(); + services.dashboardFeatureFlag = { allowByValueEmbeddables: true }; + props.incomingState = { + originatingApp: 'ultraDashboard', + valueInput: { + id: 'whatchaGonnaDoWith', + attributes: { + title: + 'whatcha gonna do with all these references? All these references in your value Input', + references: [] as SavedObjectReference[], + }, + } as LensByValueInput, + }; + + const { component } = mountWith({ props, services }); + + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); + }); + + it('Shows Save and Return and Save As buttons in edit by reference mode', async () => { + const props = makeDefaultProps(); + props.initialInput = { savedObjectId: defaultSavedObjectId, id: '5678' }; + props.incomingState = { + originatingApp: 'ultraDashboard', + }; + + const { component } = mountWith({ props }); + + await act(async () => { + const topNavMenuConfig = component.find(TopNavMenu).prop('config'); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAndReturnButton) + ); + expect(topNavMenuConfig).toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveAsButton) + ); + expect(topNavMenuConfig).not.toContainEqual( + expect.objectContaining(navMenuItems.expectedSaveButton) + ); + }); }); it('saves new docs', async () => { - const { args, instance: inst } = await save({ - initialDocId: undefined, + const { props, services } = await save({ + initialSavedObjectId: undefined, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', undefined, true); - - inst.setProps({ docId: 'aaa' }); - - expect(args.docStorage.load).not.toHaveBeenCalled(); + expect(props.redirectTo).toHaveBeenCalledWith('aaa'); }); - it('adds to the recently viewed list on save', async () => { - const { args } = await save({ - initialDocId: undefined, + it('adds to the recently accessed list on save', async () => { + const { services } = await save({ + initialSavedObjectId: undefined, newCopyOnSave: false, newTitle: 'hello there', }); - expect(args.core.chrome.recentlyAccessed.add).toHaveBeenCalledWith( + expect(services.chrome.recentlyAccessed.add).toHaveBeenCalledWith( '/app/lens#/edit/aaa', 'hello there', 'aaa' @@ -631,54 +709,53 @@ describe('Lens App', () => { }); it('saves the latest doc as a copy', async () => { - const { args, instance: inst } = await save({ - initialDocId: '1234', + const { props, services, component } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: true, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', undefined, true); - - inst.setProps({ docId: 'aaa' }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(props.redirectTo).toHaveBeenCalledWith('aaa'); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: 'aaa' } }); + }); + expect(services.attributeService.wrapAttributes).toHaveBeenCalledTimes(1); }); it('saves existing docs', async () => { - const { args, instance: inst } = await save({ - initialDocId: '1234', + const { props, services, component } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: '1234', + savedObjectId: defaultSavedObjectId, title: 'hello there', - }) + }), + true, + { id: '5678', savedObjectId: defaultSavedObjectId } ); - - expect(args.redirectTo).not.toHaveBeenCalled(); - - inst.setProps({ docId: '1234' }); - - expect(args.docStorage.load).toHaveBeenCalledTimes(1); + expect(props.redirectTo).not.toHaveBeenCalled(); + await act(async () => { + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); + }); + expect(services.attributeService.unwrapAttributes).toHaveBeenCalledTimes(1); }); it('handles save failure by showing a warning, but still allows another save', async () => { - const args = defaultArgs; - args.editorFrame = frame; - (args.docStorage.save as jest.Mock).mockRejectedValue({ message: 'failed' }); - - instance = mount(); - + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockRejectedValue({ message: 'failed' }); + const { component, props, frame } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ @@ -687,51 +764,48 @@ describe('Lens App', () => { isSaveable: true, }) ); - - instance.update(); + component.update(); await act(async () => { - testSave(instance, { newCopyOnSave: false, newTitle: 'hello there' }); + testSave(component, { newCopyOnSave: false, newTitle: 'hello there' }); }); - - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - expect(args.redirectTo).not.toHaveBeenCalled(); - - expect(getButton(instance).disableButton).toEqual(false); + expect(services.notifications.toasts.addDanger).toHaveBeenCalled(); + expect(props.redirectTo).not.toHaveBeenCalled(); + expect(getButton(component).disableButton).toEqual(false); }); it('saves new doc and redirects to originating app', async () => { - const { args } = await save({ - initialDocId: undefined, + const { props, services } = await save({ + initialSavedObjectId: undefined, returnToOrigin: true, newCopyOnSave: false, newTitle: 'hello there', }); - - expect(args.docStorage.save).toHaveBeenCalledWith( + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( expect.objectContaining({ - id: undefined, + savedObjectId: undefined, title: 'hello there', - }) + }), + true, + undefined ); - - expect(args.redirectTo).toHaveBeenCalledWith('aaa', true, true); + expect(props.redirectToOrigin).toHaveBeenCalledWith({ + input: { savedObjectId: 'aaa' }, + isCopied: false, + }); }); it('saves app filters and does not save pinned filters', async () => { const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; - const unpinned = esFilters.buildExistsFilter(field, indexPattern); const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); - await act(async () => { FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); }); - - const { args } = await save({ - initialDocId: '1234', + const { services } = await save({ + initialSavedObjectId: defaultSavedObjectId, newCopyOnSave: false, newTitle: 'hello there2', lastKnownDoc: { @@ -741,42 +815,42 @@ describe('Lens App', () => { }, }, }); - - expect(args.docStorage.save).toHaveBeenCalledWith({ - id: '1234', - title: 'hello there2', - expression: 'kibana 3', - state: { - filters: [unpinned], + expect(services.attributeService.wrapAttributes).toHaveBeenCalledWith( + { + savedObjectId: defaultSavedObjectId, + title: 'hello there2', + expression: 'kibana 3', + state: { + filters: [unpinned], + }, }, - }); + true, + { id: '5678', savedObjectId: defaultSavedObjectId } + ); }); it('checks for duplicate title before saving', async () => { - const args = defaultArgs; - args.editorFrame = frame; - (args.docStorage.save as jest.Mock).mockReturnValue(Promise.resolve({ id: '123' })); - - instance = mount(); - + const services = makeDefaultServices(); + services.attributeService.wrapAttributes = jest + .fn() + .mockReturnValue(Promise.resolve({ savedObjectId: '123' })); + const { component, frame } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; await act(async () => onChange({ filterableIndexPatterns: [], - doc: ({ id: '123' } as unknown) as Document, + doc: ({ savedObjectId: '123' } as unknown) as Document, isSaveable: true, }) ); - instance.update(); + component.update(); await act(async () => { - getButton(instance).run(instance.getDOMNode()); + getButton(component).run(component.getDOMNode()); }); - instance.update(); - + component.update(); const onTitleDuplicate = jest.fn(); - await act(async () => { - instance.find(SavedObjectSaveModal).prop('onSave')({ + component.find(SavedObjectSaveModal).prop('onSave')({ onTitleDuplicate, isTitleDuplicateConfirmed: false, newCopyOnSave: false, @@ -784,9 +858,8 @@ describe('Lens App', () => { newTitle: 'test', }); }); - expect(checkForDuplicateTitle).toHaveBeenCalledWith( - expect.objectContaining({ id: '123' }), + expect.objectContaining({ savedObjectId: '123' }), false, onTitleDuplicate, expect.anything() @@ -794,11 +867,7 @@ describe('Lens App', () => { }); it('does not show the copy button on first save', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame } = mountWith({}); const onChange = frame.mount.mock.calls[0][1].onChange; await act(async () => onChange({ @@ -807,36 +876,17 @@ describe('Lens App', () => { isSaveable: true, }) ); - instance.update(); - await act(async () => getButton(instance).run(instance.getDOMNode())); - instance.update(); - - expect(instance.find(SavedObjectSaveModal).prop('showCopyOnSave')).toEqual(false); + component.update(); + await act(async () => getButton(component).run(component.getDOMNode())); + component.update(); + expect(component.find(SavedObjectSaveModal).prop('showCopyOnSave')).toEqual(false); }); }); }); describe('query bar state management', () => { - let defaultArgs: ReturnType; - - beforeEach(() => { - defaultArgs = makeDefaultArgs(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - expression: 'valid expression', - state: { - query: 'kuery', - }, - } as jest.ResolvedValue); - }); - it('uses the default time and query language settings', () => { - const args = defaultArgs; - args.editorFrame = frame; - - mount(); - + const { frame } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ query: { query: '', language: 'kuery' }, @@ -855,20 +905,14 @@ describe('Lens App', () => { }); it('updates the index patterns when the editor frame is changed', async () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ indexPatterns: [], }), {} ); - const onChange = frame.mount.mock.calls[0][1].onChange; - await act(async () => { onChange({ filterableIndexPatterns: ['1'], @@ -876,18 +920,14 @@ describe('Lens App', () => { isSaveable: true, }); }); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ indexPatterns: [{ id: '1' }], }), {} ); - // Do it again to verify that the dirty checking is done right - await act(async () => { onChange({ filterableIndexPatterns: ['2'], @@ -895,9 +935,7 @@ describe('Lens App', () => { isSaveable: true, }); }); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenLastCalledWith( expect.objectContaining({ indexPatterns: [{ id: '2' }], @@ -905,21 +943,16 @@ describe('Lens App', () => { {} ); }); - it('updates the editor frame when the user changes query or time in the search bar', () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); + it('updates the editor frame when the user changes query or time in the search bar', () => { + const { component, frame } = mountWith({}); act(() => - instance.find(TopNavMenu).prop('onQuerySubmit')!({ + component.find(TopNavMenu).prop('onQuerySubmit')!({ dateRange: { from: 'now-14d', to: 'now-7d' }, query: { query: 'new', language: 'lucene' }, }) ); - - instance.update(); - + component.update(); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ query: { query: 'new', language: 'lucene' }, @@ -938,19 +971,15 @@ describe('Lens App', () => { }); it('updates the filters when the user changes them', () => { - const args = defaultArgs; - args.editorFrame = frame; - - instance = mount(); + const { component, frame, services } = mountWith({}); const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; - act(() => - args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]) + services.data.query.filterManager.setFilters([ + esFilters.buildExistsFilter(field, indexPattern), + ]) ); - - instance.update(); - + component.update(); expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ @@ -962,17 +991,15 @@ describe('Lens App', () => { describe('saved query handling', () => { it('does not allow saving when the user is missing the saveQuery permission', () => { - const args = makeDefaultArgs(); - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - - mount(); - + mountWith({ services }); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ showSaveQuery: false }), {} @@ -980,11 +1007,7 @@ describe('Lens App', () => { }); it('persists the saved query ID when the query is saved', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component } = mountWith({}); expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ showSaveQuery: true, @@ -995,9 +1018,8 @@ describe('Lens App', () => { }), {} ); - act(() => { - instance.find(TopNavMenu).prop('onSaved')!({ + component.find(TopNavMenu).prop('onSaved')!({ id: '1', attributes: { title: '', @@ -1006,7 +1028,6 @@ describe('Lens App', () => { }, }); }); - expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ savedQuery: { @@ -1023,13 +1044,9 @@ describe('Lens App', () => { }); it('changes the saved query ID when the query is updated', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component } = mountWith({}); act(() => { - instance.find(TopNavMenu).prop('onSaved')!({ + component.find(TopNavMenu).prop('onSaved')!({ id: '1', attributes: { title: '', @@ -1038,9 +1055,8 @@ describe('Lens App', () => { }, }); }); - act(() => { - instance.find(TopNavMenu).prop('onSavedQueryUpdated')!({ + component.find(TopNavMenu).prop('onSavedQueryUpdated')!({ id: '2', attributes: { title: 'new title', @@ -1049,7 +1065,6 @@ describe('Lens App', () => { }, }); }); - expect(TopNavMenu).toHaveBeenCalledWith( expect.objectContaining({ savedQuery: { @@ -1066,32 +1081,23 @@ describe('Lens App', () => { }); it('clears all existing unpinned filters when the active saved query is cleared', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - + const { component, frame, services } = mountWith({}); act(() => - instance.find(TopNavMenu).prop('onQuerySubmit')!({ + component.find(TopNavMenu).prop('onQuerySubmit')!({ dateRange: { from: 'now-14d', to: 'now-7d' }, query: { query: 'new', language: 'lucene' }, }) ); - const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; const field = ({ name: 'myfield' } as unknown) as IFieldType; const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; - const unpinned = esFilters.buildExistsFilter(field, indexPattern); const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); - - act(() => args.data.query.filterManager.setFilters([pinned, unpinned])); - instance.update(); - - act(() => instance.find(TopNavMenu).prop('onClearSavedQuery')!()); - instance.update(); - + act(() => services.data.query.filterManager.setFilters([pinned, unpinned])); + component.update(); + act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); + component.update(); expect(frame.mount).toHaveBeenLastCalledWith( expect.any(Element), expect.objectContaining({ @@ -1101,191 +1107,127 @@ describe('Lens App', () => { }); }); - it('displays errors from the frame in a toast', () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - - instance = mount(); - - const onError = frame.mount.mock.calls[0][1].onError; - onError({ message: 'error' }); - - instance.update(); - - expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); - }); - describe('showing a confirm message when leaving', () => { - let defaultArgs: ReturnType; let defaultLeave: jest.Mock; let confirmLeave: jest.Mock; beforeEach(() => { - defaultArgs = makeDefaultArgs(); defaultLeave = jest.fn(); confirmLeave = jest.fn(); - (defaultArgs.docStorage.load as jest.Mock).mockResolvedValue({ - id: '1234', - title: 'My cool doc', - state: { - query: 'kuery', - filters: [], - }, - references: [], - } as jest.ResolvedValue); }); it('should not show a confirm message if there is no expression to save', () => { - instance = mount(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + const { props } = mountWith({}); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('does not confirm if the user is missing save permissions', () => { - const args = defaultArgs; - args.core.application = { - ...args.core.application, + const services = makeDefaultServices(); + services.application = { + ...services.application, capabilities: { - ...args.core.application.capabilities, + ...services.application.capabilities, visualize: { save: false, saveQuery: false, show: true }, }, }; - args.editorFrame = frame; - - instance = mount(); - + const { component, frame, props } = mountWith({ services }); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], doc: ({ - id: undefined, - + savedObjectId: undefined, references: [], } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('should confirm when leaving with an unsaved doc', () => { - defaultArgs.editorFrame = frame; - instance = mount(); - + const { component, frame, props } = mountWith({}); const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: undefined, state: {} } as unknown) as Document, + doc: ({ savedObjectId: undefined, state: {} } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); it('should confirm when leaving with unsaved changes to an existing doc', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], doc: ({ - id: '1234', - + savedObjectId: defaultSavedObjectId, references: [], } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); it('should not confirm when changes are saved', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ - id: '1234', - title: 'My cool doc', - references: [], - state: { - query: 'kuery', - filters: [], - }, - } as unknown) as Document, + doc: defaultDoc, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(defaultLeave).toHaveBeenCalled(); expect(confirmLeave).not.toHaveBeenCalled(); }); it('should confirm when the latest doc is invalid', async () => { - defaultArgs.editorFrame = frame; - instance = mount(); + const { component, frame, props } = mountWith({}); await act(async () => { - instance.setProps({ docId: '1234' }); + component.setProps({ initialInput: { savedObjectId: defaultSavedObjectId } }); }); - const onChange = frame.mount.mock.calls[0][1].onChange; act(() => onChange({ filterableIndexPatterns: [], - doc: ({ id: '1234', references: [] } as unknown) as Document, + doc: ({ savedObjectId: defaultSavedObjectId, references: [] } as unknown) as Document, isSaveable: true, }) ); - instance.update(); - - const lastCall = - defaultArgs.onAppLeave.mock.calls[defaultArgs.onAppLeave.mock.calls.length - 1][0]; + component.update(); + const lastCall = props.onAppLeave.mock.calls[props.onAppLeave.mock.calls.length - 1][0]; lastCall({ default: defaultLeave, confirm: confirmLeave }); - expect(confirmLeave).toHaveBeenCalled(); expect(defaultLeave).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index bfdf4ceaaabd3..e4af2a33ec68b 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -4,110 +4,86 @@ * you may not use this file except in compliance with the Elastic License. */ +import './app.scss'; + import _ from 'lodash'; import React, { useState, useEffect, useCallback } from 'react'; -import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { AppMountContext, AppMountParameters, NotificationsStart } from 'kibana/public'; -import { History } from 'history'; +import { NotificationsStart } from 'kibana/public'; import { EuiBreadcrumb } from '@elastic/eui'; -import { - Query, - DataPublicPluginStart, - syncQueryStateWithUrl, -} from '../../../../../src/plugins/data/public'; import { createKbnUrlStateStorage, - IStorageWrapper, withNotifyOnErrors, } from '../../../../../src/plugins/kibana_utils/public'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { - SavedObjectSaveModalOrigin, OnSaveProps, checkForDuplicateTitle, + SavedObjectSaveModalOrigin, } from '../../../../../src/plugins/saved_objects/public'; -import { Document, SavedObjectStore, injectFilterReferences } from '../persistence'; -import { EditorFrameInstance } from '../types'; +import { injectFilterReferences } from '../persistence'; import { NativeRenderer } from '../native_renderer'; import { trackUiEvent } from '../lens_ui_telemetry'; import { esFilters, - Filter, IndexPattern as IndexPatternInstance, IndexPatternsContract, - SavedQuery, + syncQueryStateWithUrl, } from '../../../../../src/plugins/data/public'; -import { getFullPath } from '../../common'; - -interface State { - indicateNoData: boolean; - isLoading: boolean; - isSaveModalVisible: boolean; - indexPatternsForTopNav: IndexPatternInstance[]; - originatingApp?: string; - persistedDoc?: Document; - lastKnownDoc?: Document; - - // Properties needed to interface with TopNav - dateRange: { - fromDate: string; - toDate: string; - }; - query: Query; - filters: Filter[]; - savedQuery?: SavedQuery; - isSaveable: boolean; -} +import { LENS_EMBEDDABLE_TYPE, getFullPath } from '../../common'; +import { LensAppProps, LensAppServices, LensAppState } from './types'; +import { getLensTopNavConfig } from './lens_top_nav'; +import { + LensByReferenceInput, + LensEmbeddableInput, +} from '../editor_frame_service/embeddable/embeddable'; export function App({ - editorFrame, - data, - core, - storage, - docId, - docStorage, - redirectTo, - originatingApp, - navigation, + history, onAppLeave, + redirectTo, + editorFrame, + initialInput, + incomingState, + redirectToOrigin, setHeaderActionMenu, - history, - getAppNameFromId, -}: { - editorFrame: EditorFrameInstance; - data: DataPublicPluginStart; - navigation: NavigationPublicPluginStart; - core: AppMountContext['core']; - storage: IStorageWrapper; - docId?: string; - docStorage: SavedObjectStore; - redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void; - originatingApp?: string | undefined; - onAppLeave: AppMountParameters['onAppLeave']; - setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; - history: History; - getAppNameFromId?: (appId: string) => string | undefined; -}) { - const [state, setState] = useState(() => { +}: LensAppProps) { + const { + data, + chrome, + overlays, + navigation, + uiSettings, + application, + notifications, + attributeService, + savedObjectsClient, + getOriginatingAppName, + + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag, + } = useKibana().services; + + const [state, setState] = useState(() => { const currentRange = data.query.timefilter.timefilter.getTime(); return { - isLoading: !!docId, - isSaveModalVisible: false, - indexPatternsForTopNav: [], query: data.query.queryString.getDefaultQuery(), + filters: data.query.filterManager.getFilters(), + isLoading: Boolean(initialInput), + indexPatternsForTopNav: [], dateRange: { fromDate: currentRange.from, toDate: currentRange.to, }, - originatingApp, - filters: data.query.filterManager.getFilters(), + isLinkedToOriginatingApp: Boolean(incomingState?.originatingApp), + isSaveModalVisible: false, indicateNoData: false, isSaveable: false, }; }); + const { lastKnownDoc } = state; + const showNoDataPopover = useCallback(() => { setState((prevState) => ({ ...prevState, indicateNoData: true })); }, [setState]); @@ -125,9 +101,44 @@ export function App({ state.indexPatternsForTopNav, ]); - const { lastKnownDoc } = state; + const onError = useCallback( + (e: { message: string }) => + notifications.toasts.addDanger({ + title: e.message, + }), + [notifications.toasts] + ); + + const getLastKnownDocWithoutPinnedFilters = useCallback( + function () { + if (!lastKnownDoc) return undefined; + const [pinnedFilters, appFilters] = _.partition( + injectFilterReferences(lastKnownDoc.state?.filters || [], lastKnownDoc.references), + esFilters.isFilterPinned + ); + return pinnedFilters?.length + ? { + ...lastKnownDoc, + state: { + ...lastKnownDoc.state, + filters: appFilters, + }, + } + : lastKnownDoc; + }, + [lastKnownDoc] + ); - const savingPermitted = state.isSaveable && core.application.capabilities.visualize.save; + const getIsByValueMode = useCallback( + () => + Boolean( + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag.allowByValueEmbeddables && + state.isLinkedToOriginatingApp && + !(initialInput as LensByReferenceInput)?.savedObjectId + ), + [dashboardFeatureFlag.allowByValueEmbeddables, state.isLinkedToOriginatingApp, initialInput] + ); useEffect(() => { // Clear app-specific filters when navigating to Lens. Necessary because Lens @@ -156,8 +167,8 @@ export function App({ const kbnUrlStateStorage = createKbnUrlStateStorage({ history, - useHash: core.uiSettings.get('state:storeInSessionStorage'), - ...withNotifyOnErrors(core.notifications.toasts), + useHash: uiSettings.get('state:storeInSessionStorage'), + ...withNotifyOnErrors(notifications.toasts), }); const { stop: stopSyncingQueryServiceStateWithUrl } = syncQueryStateWithUrl( data.query, @@ -172,38 +183,18 @@ export function App({ }, [ data.query.filterManager, data.query.timefilter.timefilter, - core.notifications.toasts, - core.uiSettings, + notifications.toasts, + uiSettings, data.query, history, ]); - const getLastKnownDocWithoutPinnedFilters = useCallback( - function () { - if (!lastKnownDoc) return undefined; - const [pinnedFilters, appFilters] = _.partition( - injectFilterReferences(lastKnownDoc.state?.filters || [], lastKnownDoc.references), - esFilters.isFilterPinned - ); - return pinnedFilters?.length - ? { - ...lastKnownDoc, - state: { - ...lastKnownDoc.state, - filters: appFilters, - }, - } - : lastKnownDoc; - }, - [lastKnownDoc] - ); - useEffect(() => { onAppLeave((actions) => { // Confirm when the user has made any changes to an existing doc // or when the user has configured something without saving if ( - core.application.capabilities.visualize.save && + application.capabilities.visualize.save && !_.isEqual(state.persistedDoc?.state, getLastKnownDocWithoutPinnedFilters()?.state) && (state.isSaveable || state.persistedDoc) ) { @@ -220,379 +211,430 @@ export function App({ } }); }, [ - lastKnownDoc, onAppLeave, - state.persistedDoc, + lastKnownDoc, state.isSaveable, - core.application.capabilities.visualize.save, + state.persistedDoc, getLastKnownDocWithoutPinnedFilters, + application.capabilities.visualize.save, ]); // Sync Kibana breadcrumbs any time the saved document's title changes useEffect(() => { - core.chrome.setBreadcrumbs([ - ...(originatingApp && getAppNameFromId - ? [ - { - onClick: (e) => { - core.application.navigateToApp(originatingApp); - }, - text: getAppNameFromId(originatingApp), - } as EuiBreadcrumb, - ] - : []), - { - href: core.http.basePath.prepend(`/app/visualize#/`), + const isByValueMode = getIsByValueMode(); + const breadcrumbs: EuiBreadcrumb[] = []; + if (state.isLinkedToOriginatingApp && getOriginatingAppName() && redirectToOrigin) { + breadcrumbs.push({ + onClick: () => { + redirectToOrigin(); + }, + text: getOriginatingAppName(), + }); + } + if (!isByValueMode) { + breadcrumbs.push({ + href: application.getUrlForApp('visualize'), onClick: (e) => { - core.application.navigateToApp('visualize', { path: '/' }); + application.navigateToApp('visualize', { path: '/' }); e.preventDefault(); }, text: i18n.translate('xpack.lens.breadcrumbsTitle', { defaultMessage: 'Visualize', }), - }, - { - text: state.persistedDoc - ? state.persistedDoc.title - : i18n.translate('xpack.lens.breadcrumbsCreate', { defaultMessage: 'Create' }), - }, - ]); + }); + } + let currentDocTitle = i18n.translate('xpack.lens.breadcrumbsCreate', { + defaultMessage: 'Create', + }); + if (state.persistedDoc) { + currentDocTitle = isByValueMode + ? i18n.translate('xpack.lens.breadcrumbsByValue', { defaultMessage: 'Edit visualization' }) + : state.persistedDoc.title; + } + breadcrumbs.push({ text: currentDocTitle }); + chrome.setBreadcrumbs(breadcrumbs); }, [ - core.application, - core.chrome, - core.http.basePath, + dashboardFeatureFlag.allowByValueEmbeddables, + state.isLinkedToOriginatingApp, + getOriginatingAppName, state.persistedDoc, - originatingApp, - redirectTo, - getAppNameFromId, + redirectToOrigin, + getIsByValueMode, + initialInput, + application, + chrome, ]); - useEffect( - () => { - if (docId && (!state.persistedDoc || state.persistedDoc.id !== docId)) { - setState((s) => ({ ...s, isLoading: true })); - docStorage - .load(docId) - .then((doc) => { - core.chrome.recentlyAccessed.add(getFullPath(docId), doc.title, docId); - getAllIndexPatterns( - _.uniq( - doc.references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id) - ), - data.indexPatterns, - core.notifications - ) - .then((indexPatterns) => { - // Don't overwrite any pinned filters - data.query.filterManager.setAppFilters( - injectFilterReferences(doc.state.filters, doc.references) - ); - setState((s) => ({ - ...s, - isLoading: false, - persistedDoc: doc, - lastKnownDoc: doc, - query: doc.state.query, - indexPatternsForTopNav: indexPatterns, - })); - }) - .catch((e) => { - setState((s) => ({ ...s, isLoading: false })); - - redirectTo(); - }); + useEffect(() => { + if ( + !initialInput || + (attributeService.inputIsRefType(initialInput) && + initialInput.savedObjectId === state.persistedDoc?.savedObjectId) + ) { + return; + } + + setState((s) => ({ ...s, isLoading: true })); + attributeService + .unwrapAttributes(initialInput) + .then((attributes) => { + if (!initialInput) { + return; + } + const doc = { + ...initialInput, + ...attributes, + type: LENS_EMBEDDABLE_TYPE, + }; + + if (attributeService.inputIsRefType(initialInput)) { + chrome.recentlyAccessed.add( + getFullPath(initialInput.savedObjectId), + attributes.title, + initialInput.savedObjectId + ); + } + getAllIndexPatterns( + _.uniq(doc.references.filter(({ type }) => type === 'index-pattern').map(({ id }) => id)), + data.indexPatterns, + notifications + ) + .then((indexPatterns) => { + // Don't overwrite any pinned filters + data.query.filterManager.setAppFilters( + injectFilterReferences(doc.state.filters, doc.references) + ); + setState((s) => ({ + ...s, + isLoading: false, + persistedDoc: doc, + lastKnownDoc: doc, + query: doc.state.query, + indexPatternsForTopNav: indexPatterns, + })); }) .catch((e) => { setState((s) => ({ ...s, isLoading: false })); - - core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.app.docLoadingError', { - defaultMessage: 'Error loading saved document', - }) - ); - redirectTo(); }); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - core.notifications, - data.indexPatterns, - data.query.filterManager, - docId, - // TODO: These dependencies are changing too often - // docStorage, - // redirectTo, - // state.persistedDoc, - ] - ); + }) + .catch((e) => { + setState((s) => ({ ...s, isLoading: false })); + notifications.toasts.addDanger( + i18n.translate('xpack.lens.app.docLoadingError', { + defaultMessage: 'Error loading saved document', + }) + ); + + redirectTo(); + }); + }, [ + notifications, + data.indexPatterns, + data.query.filterManager, + initialInput, + attributeService, + redirectTo, + chrome.recentlyAccessed, + state.persistedDoc?.savedObjectId, + state.persistedDoc?.state, + ]); const runSave = async ( saveProps: Omit & { returnToOrigin: boolean; onTitleDuplicate?: OnSaveProps['onTitleDuplicate']; newDescription?: string; - } + }, + options: { saveToLibrary: boolean } ) => { if (!lastKnownDoc) { return; } - - const doc = { + const docToSave = { ...getLastKnownDocWithoutPinnedFilters()!, description: saveProps.newDescription, - id: saveProps.newCopyOnSave ? undefined : lastKnownDoc.id, + savedObjectId: saveProps.newCopyOnSave ? undefined : lastKnownDoc.savedObjectId, title: saveProps.newTitle, }; - await checkForDuplicateTitle( - { - ...doc, - copyOnSave: saveProps.newCopyOnSave, - lastSavedTitle: lastKnownDoc?.title, - getEsType: () => 'lens', - getDisplayName: () => - i18n.translate('xpack.lens.app.saveModalType', { - defaultMessage: 'Lens visualization', - }), - }, - saveProps.isTitleDuplicateConfirmed, - saveProps.onTitleDuplicate, - { - savedObjectsClient: core.savedObjects.client, - overlays: core.overlays, + // Required to serialize filters in by value mode until + // https://github.com/elastic/kibana/issues/77588 is fixed + if (getIsByValueMode()) { + docToSave.state.filters.forEach((filter) => { + if (typeof filter.meta.value === 'function') { + delete filter.meta.value; + } + }); + } + + const originalInput = saveProps.newCopyOnSave ? undefined : initialInput; + const originalSavedObjectId = (originalInput as LensByReferenceInput)?.savedObjectId; + if (options.saveToLibrary && !originalInput) { + await checkForDuplicateTitle( + { + ...docToSave, + copyOnSave: saveProps.newCopyOnSave, + lastSavedTitle: lastKnownDoc.title, + getEsType: () => 'lens', + getDisplayName: () => + i18n.translate('xpack.lens.app.saveModalType', { + defaultMessage: 'Lens visualization', + }), + }, + saveProps.isTitleDuplicateConfirmed, + saveProps.onTitleDuplicate, + { + savedObjectsClient, + overlays, + } + ); + } + try { + const newInput = (await attributeService.wrapAttributes( + docToSave, + options.saveToLibrary, + originalInput + )) as LensEmbeddableInput; + + if (saveProps.returnToOrigin && redirectToOrigin) { + // disabling the validation on app leave because the document has been saved. + onAppLeave((actions) => { + return actions.default(); + }); + redirectToOrigin({ input: newInput, isCopied: saveProps.newCopyOnSave }); + return; } - ); - const newlyCreated: boolean = saveProps.newCopyOnSave || !lastKnownDoc?.id; - docStorage - .save(doc) - .then(({ id }) => { - core.chrome.recentlyAccessed.add(getFullPath(id), doc.title, id); - // Prevents unnecessary network request and disables save button - const newDoc = { ...doc, id }; - const currentOriginatingApp = state.originatingApp; + if ( + attributeService.inputIsRefType(newInput) && + newInput.savedObjectId !== originalSavedObjectId + ) { + chrome.recentlyAccessed.add( + getFullPath(newInput.savedObjectId), + docToSave.title, + newInput.savedObjectId + ); setState((s) => ({ ...s, isSaveModalVisible: false, - originatingApp: - newlyCreated && !saveProps.returnToOrigin ? undefined : currentOriginatingApp, - persistedDoc: newDoc, - lastKnownDoc: newDoc, + isLinkedToOriginatingApp: false, })); - if (docId !== id || saveProps.returnToOrigin) { - redirectTo(id, saveProps.returnToOrigin, newlyCreated); - } - }) - .catch((e) => { - // eslint-disable-next-line no-console - console.dir(e); - trackUiEvent('save_failed'); - core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.app.docSavingError', { - defaultMessage: 'Error saving document', - }) - ); - setState((s) => ({ ...s, isSaveModalVisible: false })); - }); - }; + redirectTo(newInput.savedObjectId); + return; + } - const onError = useCallback( - (e: { message: string }) => - core.notifications.toasts.addDanger({ - title: e.message, - }), - [core.notifications.toasts] - ); + const newDoc = { + ...docToSave, + ...newInput, + }; + setState((s) => ({ + ...s, + persistedDoc: newDoc, + lastKnownDoc: newDoc, + isSaveModalVisible: false, + isLinkedToOriginatingApp: false, + })); + } catch (e) { + // eslint-disable-next-line no-console + console.dir(e); + trackUiEvent('save_failed'); + notifications.toasts.addDanger( + i18n.translate('xpack.lens.app.docSavingError', { + defaultMessage: 'Error saving document', + }) + ); + setState((s) => ({ ...s, isSaveModalVisible: false })); + } + }; const { TopNavMenu } = navigation.ui; + const savingPermitted = Boolean(state.isSaveable && application.capabilities.visualize.save); + const topNavConfig = getLensTopNavConfig({ + showSaveAndReturn: Boolean( + state.isLinkedToOriginatingApp && + // Temporarily required until the 'by value' paradigm is default. + (dashboardFeatureFlag.allowByValueEmbeddables || Boolean(initialInput)) + ), + isByValueMode: getIsByValueMode(), + showCancel: Boolean(state.isLinkedToOriginatingApp), + savingPermitted, + actions: { + saveAndReturn: () => { + if (savingPermitted && lastKnownDoc) { + // disabling the validation on app leave because the document has been saved. + onAppLeave((actions) => { + return actions.default(); + }); + runSave( + { + newTitle: lastKnownDoc.title, + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + returnToOrigin: true, + }, + { + saveToLibrary: + (initialInput && attributeService.inputIsRefType(initialInput)) ?? false, + } + ); + } + }, + showSaveModal: () => { + if (savingPermitted) { + setState((s) => ({ ...s, isSaveModalVisible: true })); + } + }, + cancel: () => { + if (redirectToOrigin) { + redirectToOrigin(); + } + }, + }, + }); + return ( - - -
-
- { - if (savingPermitted) { - runSave({ - newTitle: lastKnownDoc.title, - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - returnToOrigin: true, - }); - } - }, - testId: 'lnsApp_saveAndReturnButton', - disableButton: !savingPermitted, - }, - ] - : []), - { - label: - lastKnownDoc?.id && !!state.originatingApp - ? i18n.translate('xpack.lens.app.saveAs', { - defaultMessage: 'Save as', - }) - : i18n.translate('xpack.lens.app.save', { - defaultMessage: 'Save', - }), - emphasize: !state.originatingApp || !lastKnownDoc?.id, - run: () => { - if (savingPermitted) { - setState((s) => ({ ...s, isSaveModalVisible: true })); - } - }, - testId: 'lnsApp_saveButton', - disableButton: !savingPermitted, + <> +
+
+ { + const { dateRange, query } = payload; + if ( + dateRange.from !== state.dateRange.fromDate || + dateRange.to !== state.dateRange.toDate + ) { + data.query.timefilter.timefilter.setTime(dateRange); + trackUiEvent('app_date_change'); + } else { + trackUiEvent('app_query_change'); + } + setState((s) => ({ + ...s, + dateRange: { + fromDate: dateRange.from, + toDate: dateRange.to, }, - ]} - data-test-subj="lnsApp_topNav" - screenTitle={'lens'} - onQuerySubmit={(payload) => { - const { dateRange, query } = payload; + query: query || s.query, + })); + }} + onSaved={(savedQuery) => { + setState((s) => ({ ...s, savedQuery })); + }} + onSavedQueryUpdated={(savedQuery) => { + const savedQueryFilters = savedQuery.attributes.filters || []; + const globalFilters = data.query.filterManager.getGlobalFilters(); + data.query.filterManager.setFilters([...globalFilters, ...savedQueryFilters]); + setState((s) => ({ + ...s, + savedQuery: { ...savedQuery }, // Shallow query for reference issues + dateRange: savedQuery.attributes.timefilter + ? { + fromDate: savedQuery.attributes.timefilter.from, + toDate: savedQuery.attributes.timefilter.to, + } + : s.dateRange, + })); + }} + onClearSavedQuery={() => { + data.query.filterManager.setFilters(data.query.filterManager.getGlobalFilters()); + setState((s) => ({ + ...s, + savedQuery: undefined, + filters: data.query.filterManager.getGlobalFilters(), + query: data.query.queryString.getDefaultQuery(), + })); + }} + query={state.query} + dateRangeFrom={state.dateRange.fromDate} + dateRangeTo={state.dateRange.toDate} + indicateNoData={state.indicateNoData} + /> +
+ {(!state.isLoading || state.persistedDoc) && ( + { + if (isSaveable !== state.isSaveable) { + setState((s) => ({ ...s, isSaveable })); + } + if (!_.isEqual(state.persistedDoc, doc)) { + setState((s) => ({ ...s, lastKnownDoc: doc })); + } + // Update the cached index patterns if the user made a change to any of them if ( - dateRange.from !== state.dateRange.fromDate || - dateRange.to !== state.dateRange.toDate + state.indexPatternsForTopNav.length !== filterableIndexPatterns.length || + filterableIndexPatterns.some( + (id) => + !state.indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) + ) ) { - data.query.timefilter.timefilter.setTime(dateRange); - trackUiEvent('app_date_change'); - } else { - trackUiEvent('app_query_change'); + getAllIndexPatterns( + filterableIndexPatterns, + data.indexPatterns, + notifications + ).then((indexPatterns) => { + if (indexPatterns) { + setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); + } + }); } - - setState((s) => ({ - ...s, - dateRange: { - fromDate: dateRange.from, - toDate: dateRange.to, - }, - query: query || s.query, - })); - }} - appName={'lens'} - indexPatterns={state.indexPatternsForTopNav} - showSearchBar={true} - showDatePicker={true} - showQueryBar={true} - showFilterBar={true} - showSaveQuery={core.application.capabilities.visualize.saveQuery as boolean} - savedQuery={state.savedQuery} - onSaved={(savedQuery) => { - setState((s) => ({ ...s, savedQuery })); - }} - onSavedQueryUpdated={(savedQuery) => { - const savedQueryFilters = savedQuery.attributes.filters || []; - const globalFilters = data.query.filterManager.getGlobalFilters(); - data.query.filterManager.setFilters([...globalFilters, ...savedQueryFilters]); - setState((s) => ({ - ...s, - savedQuery: { ...savedQuery }, // Shallow query for reference issues - dateRange: savedQuery.attributes.timefilter - ? { - fromDate: savedQuery.attributes.timefilter.from, - toDate: savedQuery.attributes.timefilter.to, - } - : s.dateRange, - })); - }} - onClearSavedQuery={() => { - data.query.filterManager.setFilters(data.query.filterManager.getGlobalFilters()); - setState((s) => ({ - ...s, - savedQuery: undefined, - filters: data.query.filterManager.getGlobalFilters(), - query: data.query.queryString.getDefaultQuery(), - })); - }} - query={state.query} - dateRangeFrom={state.dateRange.fromDate} - dateRangeTo={state.dateRange.toDate} - indicateNoData={state.indicateNoData} - /> -
- - {(!state.isLoading || state.persistedDoc) && ( - { - if (isSaveable !== state.isSaveable) { - setState((s) => ({ ...s, isSaveable })); - } - if (!_.isEqual(state.persistedDoc, doc)) { - setState((s) => ({ ...s, lastKnownDoc: doc })); - } - - // Update the cached index patterns if the user made a change to any of them - if ( - state.indexPatternsForTopNav.length !== filterableIndexPatterns.length || - filterableIndexPatterns.some( - (id) => - !state.indexPatternsForTopNav.find((indexPattern) => indexPattern.id === id) - ) - ) { - getAllIndexPatterns( - filterableIndexPatterns, - data.indexPatterns, - core.notifications - ).then((indexPatterns) => { - if (indexPatterns) { - setState((s) => ({ ...s, indexPatternsForTopNav: indexPatterns })); - } - }); - } - }, - }} - /> - )} -
- {lastKnownDoc && state.isSaveModalVisible && ( - runSave(props)} - onClose={() => setState((s) => ({ ...s, isSaveModalVisible: false }))} - getAppNameFromId={getAppNameFromId} - documentInfo={{ - id: lastKnownDoc.id, - title: lastKnownDoc.title || '', - description: lastKnownDoc.description || '', + }, }} - objectType={i18n.translate('xpack.lens.app.saveModalType', { - defaultMessage: 'Lens visualization', - })} - data-test-subj="lnsApp_saveModalOrigin" /> )} - - +
+ {lastKnownDoc && state.isSaveModalVisible && ( + runSave(props, { saveToLibrary: true })} + onClose={() => { + setState((s) => ({ ...s, isSaveModalVisible: false })); + }} + getAppNameFromId={() => getOriginatingAppName()} + documentInfo={{ + id: lastKnownDoc.savedObjectId, + title: lastKnownDoc.title || '', + description: lastKnownDoc.description || '', + }} + returnToOriginSwitchLabel={ + getIsByValueMode() && initialInput + ? i18n.translate('xpack.lens.app.updatePanel', { + defaultMessage: 'Update panel on {originatingAppName}', + values: { originatingAppName: getOriginatingAppName() }, + }) + : undefined + } + objectType={i18n.translate('xpack.lens.app.saveModalType', { + defaultMessage: 'Lens visualization', + })} + data-test-subj="lnsApp_saveModalOrigin" + /> + )} + ); } diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx new file mode 100644 index 0000000000000..f6234d063d8cd --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { TopNavMenuData } from '../../../../../src/plugins/navigation/public'; +import { LensTopNavActions } from './types'; + +export function getLensTopNavConfig(options: { + showSaveAndReturn: boolean; + showCancel: boolean; + isByValueMode: boolean; + actions: LensTopNavActions; + savingPermitted: boolean; +}): TopNavMenuData[] { + const { showSaveAndReturn, showCancel, actions, savingPermitted } = options; + const topNavMenu: TopNavMenuData[] = []; + + const saveButtonLabel = options.isByValueMode + ? i18n.translate('xpack.lens.app.addToLibrary', { + defaultMessage: 'Save to library', + }) + : options.showSaveAndReturn + ? i18n.translate('xpack.lens.app.saveAs', { + defaultMessage: 'Save as', + }) + : i18n.translate('xpack.lens.app.save', { + defaultMessage: 'Save', + }); + + if (showSaveAndReturn) { + topNavMenu.push({ + label: i18n.translate('xpack.lens.app.saveAndReturn', { + defaultMessage: 'Save and return', + }), + emphasize: true, + iconType: 'check', + run: actions.saveAndReturn, + testId: 'lnsApp_saveAndReturnButton', + disableButton: !savingPermitted, + description: i18n.translate('xpack.lens.app.saveAndReturnButtonAriaLabel', { + defaultMessage: 'Save the current lens visualization and return to the last app', + }), + }); + } + + topNavMenu.push({ + label: saveButtonLabel, + emphasize: !showSaveAndReturn, + run: actions.showSaveModal, + testId: 'lnsApp_saveButton', + description: i18n.translate('xpack.lens.app.saveButtonAriaLabel', { + defaultMessage: 'Save the current lens visualization', + }), + disableButton: !savingPermitted, + }); + + if (showCancel) { + topNavMenu.push({ + label: i18n.translate('xpack.lens.app.cancel', { + defaultMessage: 'cancel', + }), + run: actions.cancel, + testId: 'lnsApp_cancelButton', + description: i18n.translate('xpack.lens.app.cancelButtonAriaLabel', { + defaultMessage: 'Return to the last app without saving changes', + }), + }); + } + return topNavMenu; +} diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index ebc38e4929f6c..0d50e541d3e48 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -11,6 +11,7 @@ import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; +import { DashboardFeatureFlagConfig } from 'src/plugins/dashboard/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { LensReportManager, setReportManager, trackUiEvent } from '../lens_ui_telemetry'; @@ -18,76 +19,116 @@ import { LensReportManager, setReportManager, trackUiEvent } from '../lens_ui_te import { App } from './app'; import { EditorFrameStart } from '../types'; import { addHelpMenuToAppChrome } from '../help_menu_util'; -import { SavedObjectIndexStore } from '../persistence'; import { LensPluginStartDependencies } from '../plugin'; -import { LENS_EMBEDDABLE_TYPE } from '../../common'; +import { LENS_EMBEDDABLE_TYPE, LENS_EDIT_BY_VALUE } from '../../common'; +import { + LensEmbeddableInput, + LensByReferenceInput, + LensByValueInput, +} from '../editor_frame_service/embeddable/embeddable'; +import { LensAttributeService } from '../lens_attribute_service'; +import { LensAppServices, RedirectToOriginProps } from './types'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; export async function mountApp( core: CoreSetup, params: AppMountParameters, - createEditorFrame: EditorFrameStart['createInstance'] + mountProps: { + createEditorFrame: EditorFrameStart['createInstance']; + getByValueFeatureFlag: () => Promise; + attributeService: LensAttributeService; + } ) { + const { createEditorFrame, getByValueFeatureFlag, attributeService } = mountProps; const [coreStart, startDependencies] = await core.getStartServices(); - const { data: dataStart, navigation, embeddable } = startDependencies; - const savedObjectsClient = coreStart.savedObjects.client; - addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); + const { data, navigation, embeddable } = startDependencies; + + const instance = await createEditorFrame(); + const storage = new Storage(localStorage); + const stateTransfer = embeddable?.getStateTransfer(params.history); + const embeddableEditorIncomingState = stateTransfer?.getIncomingEditorState(); + + const lensServices: LensAppServices = { + data, + storage, + navigation, + attributeService, + http: coreStart.http, + chrome: coreStart.chrome, + overlays: coreStart.overlays, + uiSettings: coreStart.uiSettings, + application: coreStart.application, + notifications: coreStart.notifications, + savedObjectsClient: coreStart.savedObjects.client, + getOriginatingAppName: () => { + return embeddableEditorIncomingState?.originatingApp + ? stateTransfer?.getAppNameFromId(embeddableEditorIncomingState.originatingApp) + : undefined; + }, + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag: await getByValueFeatureFlag(), + }; + + addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); coreStart.chrome.docTitle.change( i18n.translate('xpack.lens.pageTitle', { defaultMessage: 'Lens' }) ); - const stateTransfer = embeddable?.getStateTransfer(params.history); - const { originatingApp } = - stateTransfer?.getIncomingEditorState({ keysToRemoveAfterFetch: ['originatingApp'] }) || {}; - - const instance = await createEditorFrame(); - setReportManager( new LensReportManager({ - storage: new Storage(localStorage), http: core.http, + storage, }) ); - const redirectTo = ( - routeProps: RouteComponentProps<{ id?: string }>, - id?: string, - returnToOrigin?: boolean, - newlyCreated?: boolean - ) => { - if (!id) { + + const getInitialInput = ( + routeProps: RouteComponentProps<{ id?: string }> + ): LensEmbeddableInput | undefined => { + if (routeProps.match.params.id) { + return { savedObjectId: routeProps.match.params.id } as LensByReferenceInput; + } + if (embeddableEditorIncomingState?.valueInput) { + return embeddableEditorIncomingState?.valueInput as LensByValueInput; + } + }; + + const redirectTo = (routeProps: RouteComponentProps<{ id?: string }>, savedObjectId?: string) => { + if (!savedObjectId) { routeProps.history.push('/'); - } else if (!originatingApp) { - routeProps.history.push(`/edit/${id}`); - } else if (!!originatingApp && id && returnToOrigin) { - routeProps.history.push(`/edit/${id}`); - - if (newlyCreated && stateTransfer) { - stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { id, type: LENS_EMBEDDABLE_TYPE }, - }); - } else { - coreStart.application.navigateToApp(originatingApp); - } + } else { + routeProps.history.push(`/edit/${savedObjectId}`); } }; + const redirectToOrigin = (props?: RedirectToOriginProps) => { + if (!embeddableEditorIncomingState?.originatingApp) { + throw new Error('redirectToOrigin called without an originating app'); + } + if (stateTransfer && props?.input) { + const { input, isCopied } = props; + stateTransfer.navigateToWithEmbeddablePackage(embeddableEditorIncomingState?.originatingApp, { + state: { + embeddableId: isCopied ? undefined : embeddableEditorIncomingState.embeddableId, + type: LENS_EMBEDDABLE_TYPE, + input, + }, + }); + } else { + coreStart.application.navigateToApp(embeddableEditorIncomingState?.originatingApp); + } + }; + + // const featureFlagConfig = await getByValueFeatureFlag(); const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { trackUiEvent('loaded'); - return ( - redirectTo(routeProps, id, returnToOrigin, newlyCreated) - } - originatingApp={originatingApp} - getAppNameFromId={stateTransfer.getAppNameFromId} + initialInput={getInitialInput(routeProps)} + redirectTo={(savedObjectId?: string) => redirectTo(routeProps, savedObjectId)} + redirectToOrigin={redirectToOrigin} onAppLeave={params.onAppLeave} setHeaderActionMenu={params.setHeaderActionMenu} history={routeProps.history} @@ -103,13 +144,16 @@ export async function mountApp( params.element.classList.add('lnsAppWrapper'); render( - - - - - - - + + + + + + + + + + , params.element ); diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts new file mode 100644 index 0000000000000..fcdd0b20f8d27 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { History } from 'history'; +import { + ApplicationStart, + AppMountParameters, + ChromeStart, + HttpStart, + IUiSettingsClient, + NotificationsStart, + OverlayStart, + SavedObjectsStart, +} from '../../../../../src/core/public'; +import { + DataPublicPluginStart, + Filter, + IndexPattern, + Query, + SavedQuery, +} from '../../../../../src/plugins/data/public'; +import { Document } from '../persistence'; +import { LensEmbeddableInput } from '../editor_frame_service/embeddable/embeddable'; +import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; +import { LensAttributeService } from '../lens_attribute_service'; +import { IStorageWrapper } from '../../../../../src/plugins/kibana_utils/public'; +import { DashboardFeatureFlagConfig } from '../../../../../src/plugins/dashboard/public'; +import { EmbeddableEditorState } from '../../../../../src/plugins/embeddable/public'; +import { EditorFrameInstance } from '..'; + +export interface LensAppState { + isLoading: boolean; + persistedDoc?: Document; + lastKnownDoc?: Document; + isSaveModalVisible: boolean; + + // Used to show a popover that guides the user towards changing the date range when no data is available. + indicateNoData: boolean; + + // index patterns used to determine which filters are available in the top nav. + indexPatternsForTopNav: IndexPattern[]; + + // Determines whether the lens editor shows the 'save and return' button, and the originating app breadcrumb. + isLinkedToOriginatingApp?: boolean; + + // Properties needed to interface with TopNav + dateRange: { + fromDate: string; + toDate: string; + }; + query: Query; + filters: Filter[]; + savedQuery?: SavedQuery; + isSaveable: boolean; +} + +export interface RedirectToOriginProps { + input?: LensEmbeddableInput; + isCopied?: boolean; +} + +export interface LensAppProps { + history: History; + editorFrame: EditorFrameInstance; + onAppLeave: AppMountParameters['onAppLeave']; + setHeaderActionMenu: AppMountParameters['setHeaderActionMenu']; + redirectTo: (savedObjectId?: string) => void; + redirectToOrigin?: (props?: RedirectToOriginProps) => void; + + // The initial input passed in by the container when editing. Can be either by reference or by value. + initialInput?: LensEmbeddableInput; + + // State passed in by the container which is used to determine the id of the Originating App. + incomingState?: EmbeddableEditorState; +} + +export interface LensAppServices { + http: HttpStart; + chrome: ChromeStart; + overlays: OverlayStart; + storage: IStorageWrapper; + data: DataPublicPluginStart; + uiSettings: IUiSettingsClient; + application: ApplicationStart; + notifications: NotificationsStart; + navigation: NavigationPublicPluginStart; + attributeService: LensAttributeService; + savedObjectsClient: SavedObjectsStart['client']; + getOriginatingAppName: () => string | undefined; + + // Temporarily required until the 'by value' paradigm is default. + dashboardFeatureFlag: DashboardFeatureFlagConfig; +} + +export interface LensTopNavActions { + saveAndReturn: () => void; + showSaveModal: () => void; + cancel: () => void; +} diff --git a/x-pack/plugins/lens/public/assets/drop_illustration.tsx b/x-pack/plugins/lens/public/assets/drop_illustration.tsx new file mode 100644 index 0000000000000..1076f4875d60c --- /dev/null +++ b/x-pack/plugins/lens/public/assets/drop_illustration.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { EuiIconProps } from '@elastic/eui'; + +export const DropIllustration = ({ title, titleId, ...props }: Omit) => ( + + {title ? {title} : null} + + + + + + + + +); diff --git a/x-pack/plugins/lens/public/async_services.ts b/x-pack/plugins/lens/public/async_services.ts new file mode 100644 index 0000000000000..5a88b47c0e894 --- /dev/null +++ b/x-pack/plugins/lens/public/async_services.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. + */ + +/** + * This file re-exports all parts of visualizations and datasources which can be loaded lazily + * (to reduce page load bundle size) when Lens is actually accessed via editor or embeddable. + * + * It's also possible for each visualization and datasource to resolve this locally, but this causes + * a burst of bundles being loaded on Lens startup at once (and in some scenarios cascading bundle loads). + * This file causes all of them to be served in a single request. + */ + +export * from './datatable_visualization/datatable_visualization'; +export * from './metric_visualization/metric_visualization'; +export * from './pie_visualization/pie_visualization'; +export * from './xy_visualization/xy_visualization'; + +export * from './indexpattern_datasource/indexpattern'; + +export * from './editor_frame_service/editor_frame'; +export * from './app_plugin/mounter'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/_index.scss b/x-pack/plugins/lens/public/datatable_visualization/_index.scss deleted file mode 100644 index 532e8106b023f..0000000000000 --- a/x-pack/plugins/lens/public/datatable_visualization/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'visualization'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/datatable_visualization.ts b/x-pack/plugins/lens/public/datatable_visualization/datatable_visualization.ts new file mode 100644 index 0000000000000..c6fe54a82e2d1 --- /dev/null +++ b/x-pack/plugins/lens/public/datatable_visualization/datatable_visualization.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 * from './expression'; +export * from './visualization'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/_visualization.scss b/x-pack/plugins/lens/public/datatable_visualization/expression.scss similarity index 100% rename from x-pack/plugins/lens/public/datatable_visualization/_visualization.scss rename to x-pack/plugins/lens/public/datatable_visualization/expression.scss diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index dac3b23b98e3b..5ed693d2ead86 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './expression.scss'; + import React, { useMemo } from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 5cc3c40591c3f..5d9be46db7fb5 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -5,9 +5,7 @@ */ import { CoreSetup } from 'kibana/public'; -import { datatableVisualization } from './visualization'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { datatable, datatableColumns, getDatatableRenderer } from './expression'; import { EditorFrameSetup, FormatFactory } from '../types'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; @@ -29,16 +27,24 @@ export class DatatableVisualization { core: CoreSetup, { expressions, formatFactory, editorFrame }: DatatableVisualizationPluginSetupPlugins ) { - expressions.registerFunction(() => datatableColumns); - expressions.registerFunction(() => datatable); - expressions.registerRenderer(() => - getDatatableRenderer({ - formatFactory, - getType: core - .getStartServices() - .then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get), - }) - ); - editorFrame.registerVisualization(datatableVisualization); + editorFrame.registerVisualization(async () => { + const { + datatable, + datatableColumns, + getDatatableRenderer, + datatableVisualization, + } = await import('../async_services'); + expressions.registerFunction(() => datatableColumns); + expressions.registerFunction(() => datatable); + expressions.registerRenderer(() => + getDatatableRenderer({ + formatFactory, + getType: core + .getStartServices() + .then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get), + }) + ); + return datatableVisualization; + }); } } diff --git a/x-pack/plugins/lens/public/drag_drop/_index.scss b/x-pack/plugins/lens/public/drag_drop/_index.scss deleted file mode 100644 index ddf9b9aa3e429..0000000000000 --- a/x-pack/plugins/lens/public/drag_drop/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'drag_drop'; diff --git a/x-pack/plugins/lens/public/drag_drop/_drag_drop.scss b/x-pack/plugins/lens/public/drag_drop/drag_drop.scss similarity index 100% rename from x-pack/plugins/lens/public/drag_drop/_drag_drop.scss rename to x-pack/plugins/lens/public/drag_drop/drag_drop.scss diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx index 85bdd24bd4f80..6941974a63cd3 100644 --- a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx +++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './drag_drop.scss'; + import React, { useState, useContext } from 'react'; import classNames from 'classnames'; import { DragContext } from './providers'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/_index.scss b/x-pack/plugins/lens/public/editor_frame_service/_index.scss deleted file mode 100644 index 199cbe35e25fa..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'editor_frame/index'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_expression_renderer.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_expression_renderer.scss deleted file mode 100644 index 9519544ece575..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_expression_renderer.scss +++ /dev/null @@ -1,12 +0,0 @@ -.lnsExpressionRenderer { - @include euiScrollBar; - position: relative; - width: 100%; - height: 100%; - display: flex; - overflow: auto; - - .lnsExpressionRenderer__component { - position: static; // Let the progress indicator position itself against the outer parent - } -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss deleted file mode 100644 index 9367e59b11717..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_frame_layout.scss +++ /dev/null @@ -1,54 +0,0 @@ -.lnsFrameLayout { - padding: 0; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: hidden; - flex-direction: column; -} - -.lnsFrameLayout__pageContent { - display: flex; - overflow: hidden; - flex-grow: 1; -} - -.lnsFrameLayout__pageBody { - @include euiScrollBar; - min-width: $lnsPanelMinWidth + $euiSizeXL; - overflow: hidden; - // Leave out bottom padding so the suggestions scrollbar stays flush to window edge - // Leave out left padding so the left sidebar's focus states are visible outside of content bounds - // This also means needing to add same amount of margin to page content and suggestion items - padding: $euiSize $euiSize 0; - - &:first-child { - padding-left: $euiSize; - } -} - -.lnsFrameLayout__sidebar { - margin: 0; - flex: 1 0 18%; - min-width: $lnsPanelMinWidth + $euiSize; - display: flex; - flex-direction: column; - position: relative; -} - -.lnsFrameLayout__sidebar--right { - flex-basis: 25%; - background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk); - min-width: $lnsPanelMinWidth + $euiSizeXL; - max-width: $euiFormMaxWidth + $euiSizeXXL; - max-height: 100%; - - .lnsConfigPanel { - @include euiScrollBar; - padding: $euiSize 0 $euiSize $euiSize; - overflow-x: hidden; - overflow-y: scroll; - } -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_suggestion_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_suggestion_panel.scss deleted file mode 100644 index 9d018076dc320..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_suggestion_panel.scss +++ /dev/null @@ -1,68 +0,0 @@ -.lnsSuggestionPanel__title { - margin-left: $euiSizeXS / 2; -} - -.lnsSuggestionPanel__suggestions { - @include euiScrollBar; - @include lnsOverflowShadowHorizontal; - padding-top: $euiSizeXS; - overflow-x: scroll; - overflow-y: hidden; - display: flex; - - // Padding / negative margins to make room for overflow shadow - padding-left: $euiSizeXS; - margin-left: -$euiSizeXS; -} - -.lnsSuggestionPanel__button { - position: relative; // Let the expression progress indicator position itself against the button - flex: 0 0 auto; - width: $lnsSuggestionWidth !important; // sass-lint:disable-line no-important - height: $lnsSuggestionHeight; - margin-right: $euiSizeS; - margin-left: $euiSizeXS / 2; - margin-bottom: $euiSizeXS / 2; - - .lnsSuggestionPanel__expressionRenderer { - position: static; // Let the progress indicator position itself against the button - } -} - -.lnsSuggestionPanel__button-isSelected { - @include euiFocusRing; -} - -.lnsSuggestionPanel__suggestionIcon { - color: $euiColorDarkShade; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: $euiSizeS; - - &:not(:only-child) { - height: calc(100% - #{$euiSizeL}); - } -} - -.lnsSuggestionPanel__chartWrapper { - display: flex; - height: 100%; - width: 100%; - pointer-events: none; -} - -.lnsSuggestionPanel__chartWrapper--withLabel { - height: calc(100% - #{$euiSizeL}); -} - -.lnsSuggestionPanel__buttonLabel { - @include euiTextTruncate; - @include euiFontSizeXS; - display: block; - font-weight: $euiFontWeightBold; - text-align: center; - flex-grow: 0; -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss deleted file mode 100644 index a4d8288d5e600..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_workspace_panel_wrapper.scss +++ /dev/null @@ -1,42 +0,0 @@ -.lnsWorkspacePanelWrapper { - @include euiScrollBar; - overflow: hidden; - // Override panel size padding - padding: 0 !important; // sass-lint:disable-line no-important - margin-bottom: $euiSize; - display: flex; - flex-direction: column; - - .lnsWorkspacePanelWrapper__pageContentHeader { - @include euiTitle('xs'); - padding: $euiSizeM; - // override EuiPage - margin-bottom: 0 !important; // sass-lint:disable-line no-important - } - - .lnsWorkspacePanelWrapper__pageContentHeader--unsaved { - color: $euiTextSubduedColor; - } - - .lnsWorkspacePanelWrapper__pageContentBody { - @include euiScrollBar; - flex-grow: 1; - display: flex; - align-items: stretch; - justify-content: stretch; - overflow: auto; - position: relative; - - > * { - flex: 1 1 100%; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; - } - } -} - -.lnsWorkspacePanelWrapper__toolbar { - margin-bottom: 0; -} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/_data_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.scss similarity index 100% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/_data_panel_wrapper.scss rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.scss diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx index 5a92f7b5ed524..d00357058bb57 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/data_panel_wrapper.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './data_panel_wrapper.scss'; + import React, { useMemo, memo, useContext, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiPopover, EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss new file mode 100644 index 0000000000000..bad0563f16f1f --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss @@ -0,0 +1,56 @@ +@import '../../variables'; + +.lnsFrameLayout { + padding: 0; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + flex-direction: column; +} + +.lnsFrameLayout__pageContent { + display: flex; + overflow: hidden; + flex-grow: 1; +} + +.lnsFrameLayout__pageBody { + @include euiScrollBar; + min-width: $lnsPanelMinWidth + $euiSizeXL; + overflow: hidden; + // Leave out bottom padding so the suggestions scrollbar stays flush to window edge + // Leave out left padding so the left sidebar's focus states are visible outside of content bounds + // This also means needing to add same amount of margin to page content and suggestion items + padding: $euiSize $euiSize 0; + + &:first-child { + padding-left: $euiSize; + } +} + +.lnsFrameLayout__sidebar { + margin: 0; + flex: 1 0 18%; + min-width: $lnsPanelMinWidth + $euiSize; + display: flex; + flex-direction: column; + position: relative; +} + +.lnsFrameLayout__sidebar--right { + flex-basis: 25%; + background-color: lightOrDarkTheme($euiColorLightestShade, $euiColorInk); + min-width: $lnsPanelMinWidth + $euiSizeXL; + max-width: $euiFormMaxWidth + $euiSizeXXL; + max-height: 100%; + + .lnsConfigPanel { + @include euiScrollBar; + padding: $euiSize 0 $euiSize $euiSize; + overflow-x: hidden; + overflow-y: scroll; + } +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx index 56afe3ed69a73..6a0b2c3301119 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './frame_layout.scss'; + import React from 'react'; import { EuiPage, EuiPageSideBar, EuiPageBody } from '@elastic/eui'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss deleted file mode 100644 index ea58a51073d53..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import 'data_panel_wrapper'; -@import 'expression_renderer'; -@import 'frame_layout'; -@import 'suggestion_panel'; -@import 'workspace_panel_wrapper'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts index 6da6d5a8c118f..4cb523f128a8c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/save.ts @@ -59,7 +59,7 @@ export function getSavedObjectFormat({ return { doc: { - id: state.persistedId, + savedObjectId: state.persistedId, title: state.title, description: state.description, type: 'lens', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index c7f505aeca517..80d007e17f711 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -376,7 +376,7 @@ describe('editor_frame state management', () => { { type: 'VISUALIZATION_LOADED', doc: { - id: 'b', + savedObjectId: 'b', state: { datasourceStates: { a: { foo: 'c' } }, visualization: { bar: 'd' }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts index 09674ebf2ade2..fc8daaed059dd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts @@ -156,7 +156,7 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta case 'VISUALIZATION_LOADED': return { ...state, - persistedId: action.doc.id, + persistedId: action.doc.savedObjectId, title: action.doc.title, description: action.doc.description, datasourceStates: Object.entries(action.doc.state.datasourceStates).reduce( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss new file mode 100644 index 0000000000000..007d833e97e9d --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.scss @@ -0,0 +1,71 @@ +@import '../../mixins'; +@import '../../variables'; + +.lnsSuggestionPanel__title { + margin-left: $euiSizeXS / 2; +} + +.lnsSuggestionPanel__suggestions { + @include euiScrollBar; + @include lnsOverflowShadowHorizontal; + padding-top: $euiSizeXS; + overflow-x: scroll; + overflow-y: hidden; + display: flex; + + // Padding / negative margins to make room for overflow shadow + padding-left: $euiSizeXS; + margin-left: -$euiSizeXS; +} + +.lnsSuggestionPanel__button { + position: relative; // Let the expression progress indicator position itself against the button + flex: 0 0 auto; + width: $lnsSuggestionWidth !important; // sass-lint:disable-line no-important + height: $lnsSuggestionHeight; + margin-right: $euiSizeS; + margin-left: $euiSizeXS / 2; + margin-bottom: $euiSizeXS / 2; + + .lnsSuggestionPanel__expressionRenderer { + position: static; // Let the progress indicator position itself against the button + } +} + +.lnsSuggestionPanel__button-isSelected { + @include euiFocusRing; +} + +.lnsSuggestionPanel__suggestionIcon { + color: $euiColorDarkShade; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding: $euiSizeS; + + &:not(:only-child) { + height: calc(100% - #{$euiSizeL}); + } +} + +.lnsSuggestionPanel__chartWrapper { + display: flex; + height: 100%; + width: 100%; + pointer-events: none; +} + +.lnsSuggestionPanel__chartWrapper--withLabel { + height: calc(100% - #{$euiSizeL}); +} + +.lnsSuggestionPanel__buttonLabel { + @include euiTextTruncate; + @include euiFontSizeXS; + display: block; + font-weight: $euiFontWeightBold; + text-align: center; + flex-grow: 0; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index e6503cb793a8e..5e5e9cda954ee 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './suggestion_panel.scss'; + import _, { camelCase } from 'lodash'; import React, { useState, useEffect, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 06cd858eda210..e56e55fdd5d6c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -5,17 +5,10 @@ */ import React, { useState, useEffect, useMemo, useContext, useCallback } from 'react'; +import classNames from 'classnames'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiImage, - EuiText, - EuiButtonEmpty, - EuiLink, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText, EuiButtonEmpty, EuiLink } from '@elastic/eui'; import { CoreStart, CoreSetup } from 'kibana/public'; import { ExecutionContextSearch } from 'src/plugins/expressions'; import { @@ -39,6 +32,7 @@ import { UiActionsStart } from '../../../../../../../src/plugins/ui_actions/publ import { VIS_EVENT_TO_TRIGGER } from '../../../../../../../src/plugins/visualizations/public'; import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; import { WorkspacePanelWrapper } from './workspace_panel_wrapper'; +import { DropIllustration } from '../../../assets/drop_illustration'; export interface WorkspacePanelProps { activeVisualizationId: string | null; @@ -78,11 +72,6 @@ export function InnerWorkspacePanel({ ExpressionRenderer: ExpressionRendererComponent, title, }: WorkspacePanelProps) { - const IS_DARK_THEME = core.uiSettings.get('theme:darkMode'); - const emptyStateGraphicURL = IS_DARK_THEME - ? '/plugins/lens/assets/lens_app_graphic_dark_2x.png' - : '/plugins/lens/assets/lens_app_graphic_light_2x.png'; - const dragDropContext = useContext(DragContext); const suggestionForDraggedField = useMemo( @@ -210,41 +199,54 @@ export function InnerWorkspacePanel({ function renderEmptyWorkspace() { return ( -
- -

- -

- -

- -

-

- - - - - -

-
-
+ +

+ + {expression === null ? ( + + ) : ( + + )} + +

+ + {expression === null && ( + <> +

+ +

+

+ + + + + +

+ + )} +
); } @@ -330,12 +332,14 @@ export function InnerWorkspacePanel({ visualizationMap={visualizationMap} > {renderVisualization()} + {Boolean(suggestionForDraggedField) && expression !== null && renderEmptyWorkspace()} ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss new file mode 100644 index 0000000000000..7f7385f029ed4 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss @@ -0,0 +1,127 @@ +.lnsWorkspacePanelWrapper { + @include euiScrollBar; + overflow: hidden; + // Override panel size padding + padding: 0 !important; // sass-lint:disable-line no-important + margin-bottom: $euiSize; + display: flex; + flex-direction: column; + position: relative; // For positioning the dnd overlay + + .lnsWorkspacePanelWrapper__pageContentHeader { + @include euiTitle('xs'); + padding: $euiSizeM; + // override EuiPage + margin-bottom: 0 !important; // sass-lint:disable-line no-important + } + + .lnsWorkspacePanelWrapper__pageContentHeader--unsaved { + color: $euiTextSubduedColor; + } + + .lnsWorkspacePanelWrapper__pageContentBody { + @include euiScrollBar; + flex-grow: 1; + display: flex; + align-items: stretch; + justify-content: stretch; + overflow: hidden; + + > * { + flex: 1 1 100%; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + } + } +} + +.lnsWorkspacePanel__dragDrop { + // Disable the coloring of the DnD for this element as we'll + // Color the whole panel instead + background-color: transparent !important; // sass-lint:disable-line no-important +} + +.lnsExpressionRenderer { + .lnsDragDrop-isDropTarget & { + transition: filter $euiAnimSpeedNormal ease-in-out, opacity $euiAnimSpeedNormal ease-in-out; + filter: blur($euiSizeXS); + opacity: .25; + } +} + +.lnsWorkspacePanel__emptyContent { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + transition: background-color $euiAnimSpeedNormal ease-in-out; + + .lnsDragDrop-isDropTarget & { + background-color: transparentize($euiColorSecondary, .9); + + p { + transition: filter $euiAnimSpeedNormal ease-in-out; + filter: blur(5px); + } + } + + .lnsDragDrop-isActiveDropTarget & { + background-color: transparentize($euiColorSecondary, .75); + + .lnsDropIllustration__hand { + animation: pulseArrowContinuous 1.5s ease-in-out 0s infinite normal forwards; + } + } + + &.lnsWorkspacePanel__emptyContent-onTop p { + display: none; + } +} + +.lnsWorkspacePanelWrapper__toolbar { + margin-bottom: 0; +} + +.lnsDropIllustration__adjustFill { + fill: $euiColorFullShade; +} + +.lnsWorkspacePanel__dropIllustration { + overflow: visible; // Shows arrow animation when it gets out of bounds + margin-top: $euiSizeL; + margin-bottom: $euiSizeXXL; + // Drop shadow values is a dupe of @euiBottomShadowMedium but used as a filter + // Hard-coded px values OK (@cchaos) + // sass-lint:disable-block indentation + filter: + drop-shadow(0 6px 12px transparentize($euiShadowColor, .8)) + drop-shadow(0 4px 4px transparentize($euiShadowColor, .8)) + drop-shadow(0 2px 2px transparentize($euiShadowColor, .8)); +} + +.lnsDropIllustration__hand { + animation: pulseArrow 5s ease-in-out 0s infinite normal forwards; +} + +@keyframes pulseArrow { + 0% { transform: translateY(0%); } + 65% { transform: translateY(0%); } + 72% { transform: translateY(10%); } + 79% { transform: translateY(7%); } + 86% { transform: translateY(10%); } + 95% { transform: translateY(0); } +} + +@keyframes pulseArrowContinuous { + 0% { transform: translateY(10%); } + 25% { transform: translateY(15%); } + 50% { transform: translateY(10%); } + 75% { transform: translateY(15%); } + 100% { transform: translateY(10%); } +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index 8e7d504ff7677..fa63cd3c6f1e0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './workspace_panel_wrapper.scss'; + import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index 1e2df28cad7b1..d48f9ed713caf 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -5,12 +5,29 @@ */ import { Subject } from 'rxjs'; -import { Embeddable } from './embeddable'; +import { + Embeddable, + LensByValueInput, + LensByReferenceInput, + LensSavedObjectAttributes, + LensEmbeddableInput, +} from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; -import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; +import { + Query, + TimeRange, + Filter, + TimefilterContract, + IndexPatternsContract, +} from 'src/plugins/data/public'; import { Document } from '../../persistence'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable'; +import { coreMock, httpServiceMock } from '../../../../../../src/core/public/mocks'; +import { IBasePath } from '../../../../../../src/core/public'; +import { AttributeService } from '../../../../../../src/plugins/dashboard/public'; +import { Ast } from '@kbn/interpreter/common'; +import { LensAttributeService } from '../../lens_attribute_service'; jest.mock('../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -29,61 +46,95 @@ const savedVis: Document = { visualizationType: '', }; +const attributeServiceMockFromSavedVis = (document: Document): LensAttributeService => { + const core = coreMock.createStart(); + const service = new AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >( + 'lens', + jest.fn(), + core.savedObjects.client, + core.overlays, + core.i18n.Context, + core.notifications.toasts + ); + service.unwrapAttributes = jest.fn((input: LensByValueInput | LensByReferenceInput) => { + return Promise.resolve({ ...document } as LensSavedObjectAttributes); + }); + service.wrapAttributes = jest.fn(); + return service; +}; + describe('embeddable', () => { let mountpoint: HTMLDivElement; let expressionRenderer: jest.Mock; let getTrigger: jest.Mock; let trigger: { exec: jest.Mock }; + let basePath: IBasePath; + let attributeService: AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >; beforeEach(() => { mountpoint = document.createElement('div'); expressionRenderer = jest.fn((_props) => null); trigger = { exec: jest.fn() }; getTrigger = jest.fn(() => trigger); + attributeService = attributeServiceMockFromSavedVis(savedVis); + const http = httpServiceMock.createSetupContract({ basePath: '/test' }); + basePath = http.basePath; }); afterEach(() => { mountpoint.remove(); }); - it('should render expression with expression renderer', () => { + it('should render expression with expression renderer', async () => { const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + {} as LensEmbeddableInput ); + await embeddable.initializeSavedVis({} as LensEmbeddableInput); embeddable.render(mountpoint); expect(expressionRenderer).toHaveBeenCalledTimes(1); expect(expressionRenderer.mock.calls[0][0]!.expression).toEqual('my | expression'); }); - it('should re-render if new input is pushed', () => { + it('should re-render if new input is pushed', async () => { 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 embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + { id: '123' } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); embeddable.updateInput({ @@ -95,61 +146,74 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(2); }); - it('should pass context to embeddable', () => { + it('should pass context to embeddable', async () => { 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 input = { savedObjectId: '123', timeRange, query, filters } as LensEmbeddableInput; + const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + input ); + await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); - expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual({ - timeRange, - query: [query, savedVis.state.query], - filters, - }); + expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual( + expect.objectContaining({ + timeRange, + query: [query, savedVis.state.query], + filters, + }) + ); }); - it('should merge external context with query and filters of the saved object', () => { + it('should merge external context with query and filters of the saved object', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: 'external filter' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; + const newSavedVis = { + ...savedVis, + state: { + ...savedVis.state, + query: { language: 'kquery', query: 'saved filter' }, + filters: [ + { meta: { alias: 'test', negate: false, disabled: false, indexRefName: 'filter-0' } }, + ], + }, + references: [{ type: 'index-pattern', name: 'filter-0', id: 'my-index-pattern-id' }], + }; + attributeService = attributeServiceMockFromSavedVis(newSavedVis); + + const input = { savedObjectId: '123', timeRange, query, filters } as LensEmbeddableInput; + const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis: { - ...savedVis, - state: { - ...savedVis.state, - query: { language: 'kquery', query: 'saved filter' }, - filters: [ - { meta: { alias: 'test', negate: false, disabled: false, indexRefName: 'filter-0' } }, - ], - }, - references: [{ type: 'index-pattern', name: 'filter-0', id: 'my-index-pattern-id' }], - }, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + input ); + await embeddable.initializeSavedVis(input); embeddable.render(mountpoint); expect(expressionRenderer.mock.calls[0][0].searchContext).toEqual({ @@ -163,20 +227,22 @@ describe('embeddable', () => { }); }); - it('should execute trigger on event from expression renderer', () => { + it('should execute trigger on event from expression renderer', async () => { const embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123' } + { id: '123' } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); embeddable.render(mountpoint); const onEvent = expressionRenderer.mock.calls[0][0].onEvent!; @@ -190,24 +256,31 @@ describe('embeddable', () => { ); }); - it('should not re-render if only change is in disabled filter', () => { + it('should not re-render if only change is in disabled filter', async () => { 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 embeddable = new Embeddable( - dataPluginMock.createSetupContract().query.timefilter.timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + { id: '123', timeRange, query, filters } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ + id: '123', + timeRange, + query, + filters, + } as LensEmbeddableInput); embeddable.render(mountpoint); embeddable.updateInput({ @@ -219,7 +292,7 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); }); - it('should re-render on auto refresh fetch observable', () => { + it('should re-render on auto refresh fetch observable', async () => { const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; const query: Query = { language: 'kquery', query: '' }; const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; @@ -230,18 +303,25 @@ describe('embeddable', () => { } as unknown) as TimefilterContract; const embeddable = new Embeddable( - timefilter, - expressionRenderer, - getTrigger, { - editPath: '', - editUrl: '', + timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, editable: true, - savedVis, - expression: 'my | expression', + getTrigger, + documentToExpression: () => Promise.resolve({} as Ast), + toExpressionString: () => 'my | expression', }, - { id: '123', timeRange, query, filters } + { id: '123', timeRange, query, filters } as LensEmbeddableInput ); + await embeddable.initializeSavedVis({ + id: '123', + timeRange, + query, + filters, + } as LensEmbeddableInput); embeddable.render(mountpoint); autoRefreshFetchSubject.next(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 4df218a3e94e9..61a5d8cacdc4f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -13,10 +13,12 @@ import { Query, TimefilterContract, TimeRange, + IndexPattern, } from 'src/plugins/data/public'; import { ExecutionContextSearch } from 'src/plugins/expressions'; import { Subscription } from 'rxjs'; +import { Ast } from '@kbn/interpreter/common'; import { ExpressionRendererEvent, ReactExpressionRendererType, @@ -28,41 +30,56 @@ import { EmbeddableInput, EmbeddableOutput, IContainer, + SavedObjectEmbeddableInput, + ReferenceOrValueEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; import { DOC_TYPE, Document, injectFilterReferences } from '../../persistence'; import { ExpressionWrapper } from './expression_wrapper'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; import { isLensBrushEvent, isLensFilterEvent } from '../../types'; -export interface LensEmbeddableConfiguration { - expression: string | null; - savedVis: Document; - editUrl: string; - editPath: string; - editable: boolean; - indexPatterns?: IIndexPattern[]; -} +import { IndexPatternsContract } from '../../../../../../src/plugins/data/public'; +import { getEditPath } from '../../../common'; +import { IBasePath } from '../../../../../../src/core/public'; +import { LensAttributeService } from '../../lens_attribute_service'; -export interface LensEmbeddableInput extends EmbeddableInput { - timeRange?: TimeRange; - query?: Query; - filters?: Filter[]; -} +export type LensSavedObjectAttributes = Omit; + +export type LensByValueInput = { + attributes: LensSavedObjectAttributes; +} & EmbeddableInput; + +export type LensByReferenceInput = SavedObjectEmbeddableInput & EmbeddableInput; +export type LensEmbeddableInput = LensByValueInput | LensByReferenceInput; export interface LensEmbeddableOutput extends EmbeddableOutput { indexPatterns?: IIndexPattern[]; } -export class Embeddable extends AbstractEmbeddable { +export interface LensEmbeddableDeps { + attributeService: LensAttributeService; + documentToExpression: (doc: Document) => Promise; + toExpressionString: (astObj: Ast, type?: string) => string; + editable: boolean; + indexPatternService: IndexPatternsContract; + expressionRenderer: ReactExpressionRendererType; + timefilter: TimefilterContract; + basePath: IBasePath; + getTrigger?: UiActionsStart['getTrigger'] | undefined; +} + +export class Embeddable + extends AbstractEmbeddable + implements ReferenceOrValueEmbeddable { type = DOC_TYPE; private expressionRenderer: ReactExpressionRendererType; - private getTrigger: UiActionsStart['getTrigger'] | undefined; - private expression: string | null; - private savedVis: Document; + private savedVis: Document | undefined; + private expression: string | undefined | null; private domNode: HTMLElement | Element | undefined; private subscription: Subscription; private autoRefreshFetchSubscription: Subscription; + private isInitialized = false; private externalSearchContext: { timeRange?: TimeRange; @@ -72,50 +89,32 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(initialInput)); this.subscription = this.getInput$().subscribe((input) => this.onContainerStateChanged(input)); - this.onContainerStateChanged(initialInput); - this.autoRefreshFetchSubscription = timefilter + this.autoRefreshFetchSubscription = deps.timefilter .getAutoRefreshFetch$() .subscribe(this.reload.bind(this)); } public supportedTriggers() { + if (!this.savedVis) { + return []; + } switch (this.savedVis.visualizationType) { case 'lnsXY': return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush]; @@ -128,6 +127,22 @@ export class Embeddable extends AbstractEmbeddable !filter.meta.disabled) @@ -144,9 +159,7 @@ export class Embeddable extends AbstractEmbeddable, @@ -173,6 +189,9 @@ export class Embeddable extends AbstractEmbeddable { - if (!this.getTrigger || this.input.disableTriggers) { + if (!this.deps.getTrigger || this.input.disableTriggers) { return; } if (isLensBrushEvent(event)) { - this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: event.data, embeddable: this, }); } if (isLensFilterEvent(event)) { - this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ + this.deps.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({ data: event.data, embeddable: this, }); } }; - destroy() { - super.destroy(); - if (this.domNode) { - unmountComponentAtNode(this.domNode); - } - if (this.subscription) { - this.subscription.unsubscribe(); - } - this.autoRefreshFetchSubscription.unsubscribe(); - } - - reload() { + async reload() { const currentTime = Date.now(); if (this.externalSearchContext.lastReloadRequestTime !== currentTime) { this.externalSearchContext = { @@ -233,4 +241,68 @@ export class Embeddable extends AbstractEmbeddable type === 'index-pattern') + .map(async ({ id }) => { + try { + return await this.deps.indexPatternService.get(id); + } catch (error) { + // Unable to load index pattern, ignore error as the index patterns are only used to + // configure the filter and query bar - there is still a good chance to get the visualization + // to show. + return null; + } + }) + .filter((promise): promise is Promise => Boolean(promise)); + const indexPatterns = await Promise.all(promises); + // passing edit url and index patterns to the output of this embeddable for + // the container to pick them up and use them to configure filter bar and + // config dropdown correctly. + const input = this.getInput(); + const title = input.hidePanelTitles ? '' : input.title || this.savedVis.title; + const savedObjectId = (input as LensByReferenceInput).savedObjectId; + this.updateOutput({ + ...this.getOutput(), + defaultTitle: this.savedVis.title, + title, + editPath: getEditPath(savedObjectId), + editUrl: this.deps.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), + indexPatterns, + }); + } + + public inputIsRefType = ( + input: LensByValueInput | LensByReferenceInput + ): input is LensByReferenceInput => { + return this.deps.attributeService.inputIsRefType(input); + }; + + public getInputAsRefType = async (): Promise => { + const input = this.deps.attributeService.getExplicitInputFromEmbeddable(this); + return this.deps.attributeService.getInputAsRefType(input, { + showSaveModal: true, + saveModalTitle: this.getTitle(), + }); + }; + + public getInputAsValueType = async (): Promise => { + const input = this.deps.attributeService.getExplicitInputFromEmbeddable(this); + return this.deps.attributeService.getInputAsValueType(input); + }; + + destroy() { + super.destroy(); + if (this.domNode) { + unmountComponentAtNode(this.domNode); + } + if (this.subscription) { + this.subscription.unsubscribe(); + } + this.autoRefreshFetchSubscription.unsubscribe(); + } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index b8f9f8de1d286..8771d1ebaddb1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -4,33 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Capabilities, HttpSetup, SavedObjectsClientContract } from 'kibana/public'; +import { Capabilities, HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; import { toExpression, Ast } from '@kbn/interpreter/target/common'; import { IndexPatternsContract, - IndexPattern, TimefilterContract, } from '../../../../../../src/plugins/data/public'; import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public'; import { EmbeddableFactoryDefinition, - ErrorEmbeddable, - EmbeddableInput, IContainer, } from '../../../../../../src/plugins/embeddable/public'; -import { Embeddable } from './embeddable'; -import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence'; -import { getEditPath } from '../../../common'; +import { Embeddable, LensByReferenceInput, LensEmbeddableInput } from './embeddable'; +import { DOC_TYPE } from '../../persistence'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; import { Document } from '../../persistence/saved_object_store'; +import { LensAttributeService } from '../../lens_attribute_service'; -interface StartServices { +export interface LensEmbeddableStartServices { timefilter: TimefilterContract; coreHttp: HttpSetup; + attributeService: LensAttributeService; capabilities: RecursiveReadonly; - savedObjectsClient: SavedObjectsClientContract; expressionRenderer: ReactExpressionRendererType; indexPatternService: IndexPatternsContract; uiActions?: UiActionsStart; @@ -47,7 +44,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { getIconForSavedObject: () => 'lensApp', }; - constructor(private getStartServices: () => Promise) {} + constructor(private getStartServices: () => Promise) {} public isEditable = async () => { const { capabilities } = await this.getStartServices(); @@ -66,59 +63,40 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { createFromSavedObject = async ( savedObjectId: string, - input: Partial & { id: string }, + input: LensEmbeddableInput, parent?: IContainer ) => { + if (!(input as LensByReferenceInput).savedObjectId) { + (input as LensByReferenceInput).savedObjectId = savedObjectId; + } + return this.create(input, parent); + }; + + async create(input: LensEmbeddableInput, parent?: IContainer) { const { - savedObjectsClient, - coreHttp, - indexPatternService, timefilter, expressionRenderer, documentToExpression, uiActions, + coreHttp, + attributeService, + indexPatternService, } = await this.getStartServices(); - const store = new SavedObjectIndexStore(savedObjectsClient); - const savedVis = await store.load(savedObjectId); - - const promises = savedVis.references - .filter(({ type }) => type === 'index-pattern') - .map(async ({ id }) => { - try { - return await indexPatternService.get(id); - } catch (error) { - // Unable to load index pattern, ignore error as the index patterns are only used to - // configure the filter and query bar - there is still a good chance to get the visualization - // to show. - return null; - } - }); - const indexPatterns = ( - await Promise.all(promises) - ).filter((indexPattern: IndexPattern | null): indexPattern is IndexPattern => - Boolean(indexPattern) - ); - - const expression = await documentToExpression(savedVis); return new Embeddable( - timefilter, - expressionRenderer, - uiActions?.getTrigger, { - savedVis, - editPath: getEditPath(savedObjectId), - editUrl: coreHttp.basePath.prepend(`/app/lens${getEditPath(savedObjectId)}`), + attributeService, + indexPatternService, + timefilter, + expressionRenderer, editable: await this.isEditable(), - indexPatterns, - expression: expression ? toExpression(expression) : null, + basePath: coreHttp.basePath, + getTrigger: uiActions?.getTrigger, + documentToExpression, + toExpressionString: toExpression, }, input, parent ); - }; - - async create(input: EmbeddableInput) { - return new ErrorEmbeddable('Lens can only be created from a saved object', input); } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx index 7b1d091c1c8fe..c1b6d74bb49c0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.test.tsx @@ -41,7 +41,8 @@ describe('editor_frame service', () => { (async () => { pluginInstance.setup( coreMock.createSetup() as CoreSetup, - pluginSetupDependencies + pluginSetupDependencies, + jest.fn() ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance(); @@ -61,7 +62,8 @@ describe('editor_frame service', () => { it('should not have child nodes after unmount', async () => { pluginInstance.setup( coreMock.createSetup() as CoreSetup, - pluginSetupDependencies + pluginSetupDependencies, + jest.fn() ); const publicAPI = pluginInstance.start(coreMock.createStart(), pluginStartDependencies); const instance = await publicAPI.createInstance(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 5fc347179a032..8892217f5d51d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -22,13 +22,14 @@ import { EditorFrameStart, } from '../types'; import { Document } from '../persistence/saved_object_store'; -import { EditorFrame } from './editor_frame'; import { mergeTables } from './merge_tables'; import { formatColumn } from './format_column'; -import { EmbeddableFactory } from './embeddable/embeddable_factory'; +import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; import { persistedStateToExpression } from './editor_frame/state_helpers'; +import { LensAttributeService } from '../lens_attribute_service'; export interface EditorFrameSetupPlugins { data: DataPublicPluginSetup; @@ -39,14 +40,17 @@ export interface EditorFrameSetupPlugins { export interface EditorFrameStartPlugins { data: DataPublicPluginStart; embeddable?: EmbeddableStart; + dashboard?: DashboardStart; expressions: ExpressionsStart; uiActions?: UiActionsStart; } async function collectAsyncDefinitions( - definitions: Array> + definitions: Array Promise)> ) { - const resolvedDefinitions = await Promise.all(definitions); + const resolvedDefinitions = await Promise.all( + definitions.map((definition) => (typeof definition === 'function' ? definition() : definition)) + ); const definitionMap: Record = {}; resolvedDefinitions.forEach((definition) => { definitionMap[definition.id] = definition; @@ -58,8 +62,8 @@ async function collectAsyncDefinitions( export class EditorFrameService { constructor() {} - private readonly datasources: Array> = []; - private readonly visualizations: Array> = []; + private readonly datasources: Array Promise)> = []; + private readonly visualizations: Array Promise)> = []; /** * This method takes a Lens saved object as returned from the persistence helper, @@ -78,16 +82,17 @@ export class EditorFrameService { public setup( core: CoreSetup, - plugins: EditorFrameSetupPlugins + plugins: EditorFrameSetupPlugins, + getAttributeService: () => LensAttributeService ): EditorFrameSetup { plugins.expressions.registerFunction(() => mergeTables); plugins.expressions.registerFunction(() => formatColumn); - const getStartServices = async () => { + const getStartServices = async (): Promise => { const [coreStart, deps] = await core.getStartServices(); return { + attributeService: getAttributeService(), capabilities: coreStart.application.capabilities, - savedObjectsClient: coreStart.savedObjects.client, coreHttp: coreStart.http, timefilter: deps.data.query.timefilter.timefilter, expressionRenderer: deps.expressions.ReactExpressionRenderer, @@ -120,7 +125,7 @@ export class EditorFrameService { ]); return { - mount: ( + mount: async ( element, { doc, onError, dateRange, query, filters, savedQuery, onChange, showNoDataPopover } ) => { @@ -128,6 +133,8 @@ export class EditorFrameService { const firstDatasourceId = Object.keys(resolvedDatasources)[0]; const firstVisualizationId = Object.keys(resolvedVisualizations)[0]; + const { EditorFrame } = await import('../async_services'); + render( { ).toEqual(['client', 'source', 'timestampLabel']); }); + it('should show meta fields accordion', async () => { + const wrapper = mountWithIntl( + + ); + wrapper + .find('[data-test-subj="lnsIndexPatternMetaFields"]') + .find('button') + .first() + .simulate('click'); + expect( + wrapper + .find('[data-test-subj="lnsIndexPatternMetaFields"]') + .find(FieldItem) + .first() + .prop('field').name + ).toEqual('_id'); + }); + it('should display NoFieldsCallout when all fields are empty', async () => { const wrapper = mountWithIntl( ); - expect(wrapper.find(NoFieldsCallout).length).toEqual(1); + expect(wrapper.find(NoFieldsCallout).length).toEqual(2); expect( wrapper .find('[data-test-subj="lnsIndexPatternAvailableFields"]') @@ -654,7 +683,7 @@ describe('IndexPattern Data Panel', () => { .length ).toEqual(1); wrapper.setProps({ existingFields: { idx1: {} } }); - expect(wrapper.find(NoFieldsCallout).length).toEqual(1); + expect(wrapper.find(NoFieldsCallout).length).toEqual(2); }); it('should filter down by name', () => { @@ -699,7 +728,7 @@ describe('IndexPattern Data Panel', () => { expect(wrapper.find(FieldItem).map((fieldItem) => fieldItem.prop('field').name)).toEqual([ 'Records', ]); - expect(wrapper.find(NoFieldsCallout).length).toEqual(2); + expect(wrapper.find(NoFieldsCallout).length).toEqual(3); }); it('should toggle type if clicked again', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx index f7adf91e307da..4e85cb5b5d46c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.tsx @@ -5,14 +5,13 @@ */ import './datapanel.scss'; -import { uniq, keyBy, groupBy, throttle } from 'lodash'; -import React, { useState, useEffect, memo, useCallback, useMemo } from 'react'; +import { uniq, keyBy, groupBy } from 'lodash'; +import React, { useState, memo, useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiContextMenuPanel, EuiContextMenuItem, - EuiContextMenuPanelProps, EuiPopover, EuiCallOut, EuiFormControlLayout, @@ -25,8 +24,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { DataPublicPluginStart, EsQueryConfig, Query, Filter } from 'src/plugins/data/public'; import { DatasourceDataPanelProps, DataType, StateSetter } from '../types'; import { ChildDragDropProvider, DragContextState } from '../drag_drop'; -import { FieldItem } from './field_item'; -import { NoFieldsCallout } from './no_fields_callout'; import { IndexPattern, IndexPatternPrivateState, @@ -37,7 +34,6 @@ import { trackUiEvent } from '../lens_ui_telemetry'; import { syncExistingFields } from './loader'; import { fieldExists } from './pure_helpers'; import { Loader } from '../loader'; -import { FieldsAccordion } from './fields_accordion'; import { esQuery, IIndexPattern } from '../../../../../src/plugins/data/public'; export type Props = DatasourceDataPanelProps & { @@ -52,18 +48,13 @@ export type Props = DatasourceDataPanelProps & { import { LensFieldIcon } from './lens_field_icon'; import { ChangeIndexPattern } from './change_indexpattern'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; - -// TODO the typings for EuiContextMenuPanel are incorrect - watchedItemProps is missing. This can be removed when the types are adjusted -const FixedEuiContextMenuPanel = (EuiContextMenuPanel as unknown) as React.FunctionComponent< - EuiContextMenuPanelProps & { watchedItemProps: string[] } ->; +import { FieldGroups, FieldList } from './field_list'; function sortFields(fieldA: IndexPatternField, fieldB: IndexPatternField) { return fieldA.displayName.localeCompare(fieldB.displayName, undefined, { sensitivity: 'base' }); } const supportedFieldTypes = new Set(['string', 'number', 'boolean', 'date', 'ip', 'document']); -const PAGINATION_SIZE = 50; const fieldTypeNames: Record = { document: i18n.translate('xpack.lens.datatypes.record', { defaultMessage: 'record' }), @@ -212,18 +203,19 @@ interface DataPanelState { isTypeFilterOpen: boolean; isAvailableAccordionOpen: boolean; isEmptyAccordionOpen: boolean; + isMetaAccordionOpen: boolean; } -export interface FieldsGroup { +const defaultFieldGroups: { specialFields: IndexPatternField[]; availableFields: IndexPatternField[]; emptyFields: IndexPatternField[]; -} - -const defaultFieldGroups = { + metaFields: IndexPatternField[]; +} = { specialFields: [], availableFields: [], emptyFields: [], + metaFields: [], }; const fieldFiltersLabel = i18n.translate('xpack.lens.indexPatterns.fieldFiltersLabel', { @@ -261,9 +253,8 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ isTypeFilterOpen: false, isAvailableAccordionOpen: true, isEmptyAccordionOpen: false, + isMetaAccordionOpen: false, }); - const [pageSize, setPageSize] = useState(PAGINATION_SIZE); - const [scrollContainer, setScrollContainer] = useState(undefined); const currentIndexPattern = indexPatterns[currentIndexPatternId]; const allFields = currentIndexPattern.fields; const clearLocalState = () => setLocalState((s) => ({ ...s, nameFilter: '', typeFilter: [] })); @@ -272,17 +263,11 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ (type) => type in fieldTypeNames ); - useEffect(() => { - // Reset the scroll if we have made material changes to the field list - if (scrollContainer) { - scrollContainer.scrollTop = 0; - setPageSize(PAGINATION_SIZE); - } - }, [localState.nameFilter, localState.typeFilter, currentIndexPatternId, scrollContainer]); + const fieldInfoUnavailable = existenceFetchFailed || currentIndexPattern.hasRestrictions; - const fieldGroups: FieldsGroup = useMemo(() => { + const unfilteredFieldGroups: FieldGroups = useMemo(() => { + const fieldByName = keyBy(allFields, 'name'); const containsData = (field: IndexPatternField) => { - const fieldByName = keyBy(allFields, 'name'); const overallField = fieldByName[field.name]; return ( @@ -294,32 +279,105 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ supportedFieldTypes.has(field.type) ); const sorted = allSupportedTypesFields.sort(sortFields); + let groupedFields; // optimization before existingFields are synced if (!hasSyncedExistingFields) { - return { + groupedFields = { ...defaultFieldGroups, ...groupBy(sorted, (field) => { if (field.type === 'document') { return 'specialFields'; + } else if (field.meta) { + return 'metaFields'; } else { return 'emptyFields'; } }), }; } - return { + groupedFields = { ...defaultFieldGroups, ...groupBy(sorted, (field) => { if (field.type === 'document') { return 'specialFields'; + } else if (field.meta) { + return 'metaFields'; } else if (containsData(field)) { return 'availableFields'; } else return 'emptyFields'; }), }; - }, [allFields, existingFields, currentIndexPattern, hasSyncedExistingFields]); - const filteredFieldGroups: FieldsGroup = useMemo(() => { + const fieldGroupDefinitions: FieldGroups = { + SpecialFields: { + fields: groupedFields.specialFields, + fieldCount: 1, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: false, + title: '', + hideDetails: true, + }, + AvailableFields: { + fields: groupedFields.availableFields, + fieldCount: groupedFields.availableFields.length, + isInitiallyOpen: true, + showInAccordion: true, + title: fieldInfoUnavailable + ? i18n.translate('xpack.lens.indexPattern.allFieldsLabel', { + defaultMessage: 'All fields', + }) + : i18n.translate('xpack.lens.indexPattern.availableFieldsLabel', { + defaultMessage: 'Available fields', + }), + + isAffectedByGlobalFilter: !!filters.length, + isAffectedByTimeFilter: true, + hideDetails: fieldInfoUnavailable, + }, + EmptyFields: { + fields: groupedFields.emptyFields, + fieldCount: groupedFields.emptyFields.length, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: true, + hideDetails: false, + title: i18n.translate('xpack.lens.indexPattern.emptyFieldsLabel', { + defaultMessage: 'Empty fields', + }), + }, + MetaFields: { + fields: groupedFields.metaFields, + fieldCount: groupedFields.metaFields.length, + isAffectedByGlobalFilter: false, + isAffectedByTimeFilter: false, + isInitiallyOpen: false, + showInAccordion: true, + hideDetails: false, + title: i18n.translate('xpack.lens.indexPattern.metaFieldsLabel', { + defaultMessage: 'Meta fields', + }), + }, + }; + + // do not show empty field accordion if there is no existence information + if (fieldInfoUnavailable) { + delete fieldGroupDefinitions.EmptyFields; + } + + return fieldGroupDefinitions; + }, [ + allFields, + existingFields, + currentIndexPattern, + hasSyncedExistingFields, + fieldInfoUnavailable, + filters.length, + ]); + + const fieldGroups: FieldGroups = useMemo(() => { const filterFieldGroup = (fieldGroup: IndexPatternField[]) => fieldGroup.filter((field) => { if ( @@ -329,76 +387,18 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ ) { return false; } - if (localState.typeFilter.length > 0) { return localState.typeFilter.includes(field.type as DataType); } return true; }); - - return Object.entries(fieldGroups).reduce((acc, [name, fields]) => { - return { - ...acc, - [name]: filterFieldGroup(fields), - }; - }, defaultFieldGroups); - }, [fieldGroups, localState.nameFilter, localState.typeFilter]); - - const lazyScroll = useCallback(() => { - if (scrollContainer) { - const nearBottom = - scrollContainer.scrollTop + scrollContainer.clientHeight > - scrollContainer.scrollHeight * 0.9; - if (nearBottom) { - const displayedFieldsLength = - (localState.isAvailableAccordionOpen ? filteredFieldGroups.availableFields.length : 0) + - (localState.isEmptyAccordionOpen ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max( - PAGINATION_SIZE, - Math.min(pageSize + PAGINATION_SIZE * 0.5, displayedFieldsLength) - ) - ); - } - } - }, [ - scrollContainer, - localState.isAvailableAccordionOpen, - localState.isEmptyAccordionOpen, - filteredFieldGroups, - pageSize, - setPageSize, - ]); - - const [paginatedAvailableFields, paginatedEmptyFields]: [ - IndexPatternField[], - IndexPatternField[] - ] = useMemo(() => { - const { availableFields, emptyFields } = filteredFieldGroups; - const isAvailableAccordionOpen = localState.isAvailableAccordionOpen; - const isEmptyAccordionOpen = localState.isEmptyAccordionOpen; - - if (isAvailableAccordionOpen && isEmptyAccordionOpen) { - if (availableFields.length > pageSize) { - return [availableFields.slice(0, pageSize), []]; - } else { - return [availableFields, emptyFields.slice(0, pageSize - availableFields.length)]; - } - } - if (isAvailableAccordionOpen && !isEmptyAccordionOpen) { - return [availableFields.slice(0, pageSize), []]; - } - - if (!isAvailableAccordionOpen && isEmptyAccordionOpen) { - return [[], emptyFields.slice(0, pageSize)]; - } - return [[], []]; - }, [ - localState.isAvailableAccordionOpen, - localState.isEmptyAccordionOpen, - filteredFieldGroups, - pageSize, - ]); + return Object.fromEntries( + Object.entries(unfilteredFieldGroups).map(([name, group]) => [ + name, + { ...group, fields: filterFieldGroup(group.fields) }, + ]) + ); + }, [unfilteredFieldGroups, localState.nameFilter, localState.typeFilter]); const fieldProps = useMemo( () => ({ @@ -423,8 +423,6 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ ] ); - const fieldInfoUnavailable = existenceFetchFailed || currentIndexPattern.hasRestrictions; - return ( } > - ( @@ -545,115 +543,21 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({
-
{ - if (el && !el.dataset.dynamicScroll) { - el.dataset.dynamicScroll = 'true'; - setScrollContainer(el); - } + + field.type === 'document' || + fieldExists(existingFields, currentIndexPattern.title, field.name) + } + fieldProps={fieldProps} + fieldGroups={fieldGroups} + hasSyncedExistingFields={!!hasSyncedExistingFields} + filter={{ + nameFilter: localState.nameFilter, + typeFilter: localState.typeFilter, }} - onScroll={throttle(lazyScroll, 100)} - > -
- {filteredFieldGroups.specialFields.map((field: IndexPatternField) => ( - - ))} - - - { - setLocalState((s) => ({ - ...s, - isAvailableAccordionOpen: open, - })); - const displayedFieldLength = - (open ? filteredFieldGroups.availableFields.length : 0) + - (localState.isEmptyAccordionOpen ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) - ); - }} - showExistenceFetchError={existenceFetchFailed} - renderCallout={ - - } - /> - - {!fieldInfoUnavailable && ( - { - setLocalState((s) => ({ - ...s, - isEmptyAccordionOpen: open, - })); - const displayedFieldLength = - (localState.isAvailableAccordionOpen - ? filteredFieldGroups.availableFields.length - : 0) + (open ? filteredFieldGroups.emptyFields.length : 0); - setPageSize( - Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) - ); - }} - renderCallout={ - - } - /> - )} - -
-
+ currentIndexPatternId={currentIndexPatternId} + existenceFetchFailed={existenceFetchFailed} + />
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx index 325f18ee9833a..3d692b1f7f5a8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx @@ -75,6 +75,10 @@ export function BucketNestingEditor({ defaultMessage: 'Top values for each {field}', values: { field: fieldName }, }), + range: i18n.translate('xpack.lens.indexPattern.groupingOverallRanges', { + defaultMessage: 'Top values for each {field}', + values: { field: fieldName }, + }), }; const bottomLevelCopy: Record = { @@ -90,6 +94,10 @@ export function BucketNestingEditor({ defaultMessage: 'Overall top {target}', values: { target: target.fieldName }, }), + range: i18n.translate('xpack.lens.indexPattern.groupingSecondRanges', { + defaultMessage: 'Overall top {target}', + values: { target: target.fieldName }, + }), }; return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 153757ac37da1..bd99bd16a63a8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -17,7 +17,7 @@ import { } from '@elastic/eui'; import { EuiFormLabel } from '@elastic/eui'; import { IndexPatternColumn, OperationType } from '../indexpattern'; -import { IndexPatternDimensionEditorProps, OperationFieldSupportMatrix } from './dimension_panel'; +import { IndexPatternDimensionEditorProps, OperationSupportMatrix } from './dimension_panel'; import { operationDefinitionMap, getOperationDisplay, @@ -36,23 +36,10 @@ const operationPanels = getOperationDisplay(); export interface DimensionEditorProps extends IndexPatternDimensionEditorProps { selectedColumn?: IndexPatternColumn; - operationFieldSupportMatrix: OperationFieldSupportMatrix; + operationSupportMatrix: OperationSupportMatrix; currentIndexPattern: IndexPattern; } -function asOperationOptions(operationTypes: OperationType[], compatibleWithCurrentField: boolean) { - return [...operationTypes] - .sort((opType1, opType2) => { - return operationPanels[opType1].displayName.localeCompare( - operationPanels[opType2].displayName - ); - }) - .map((operationType) => ({ - operationType, - compatibleWithCurrentField, - })); -} - const LabelInput = ({ value, onChange }: { value: string; onChange: (value: string) => void }) => { const [inputValue, setInputValue] = useState(value); @@ -90,7 +77,7 @@ const LabelInput = ({ value, onChange }: { value: string; onChange: (value: stri export function DimensionEditor(props: DimensionEditorProps) { const { selectedColumn, - operationFieldSupportMatrix, + operationSupportMatrix, state, columnId, setState, @@ -98,14 +85,16 @@ export function DimensionEditor(props: DimensionEditorProps) { currentIndexPattern, hideGrouping, } = props; - const { operationByField, fieldByOperation } = operationFieldSupportMatrix; + const { fieldByOperation, operationWithoutField } = operationSupportMatrix; const [ incompatibleSelectedOperationType, setInvalidOperationType, ] = useState(null); - const ParamEditor = - selectedColumn && operationDefinitionMap[selectedColumn.operationType].paramEditor; + const selectedOperationDefinition = + selectedColumn && operationDefinitionMap[selectedColumn.operationType]; + + const ParamEditor = selectedOperationDefinition?.paramEditor; const fieldMap: Record = useMemo(() => { const fields: Record = {}; @@ -115,26 +104,35 @@ export function DimensionEditor(props: DimensionEditorProps) { return fields; }, [currentIndexPattern]); - function getOperationTypes() { - const possibleOperationTypes = Object.keys(fieldByOperation) as OperationType[]; - const validOperationTypes: OperationType[] = []; + const possibleOperations = useMemo(() => { + return Object.values(operationDefinitionMap) + .sort((op1, op2) => { + return op1.displayName.localeCompare(op2.displayName); + }) + .map((def) => def.type) + .filter( + (type) => fieldByOperation[type]?.length || operationWithoutField.indexOf(type) !== -1 + ); + }, [fieldByOperation, operationWithoutField]); - if (!selectedColumn) { - validOperationTypes.push(...(Object.keys(fieldByOperation) as OperationType[])); - } else if (hasField(selectedColumn) && operationByField[selectedColumn.sourceField]) { - validOperationTypes.push(...operationByField[selectedColumn.sourceField]!); - } + // Operations are compatible if they match inputs. They are always compatible in + // the empty state. Field-based operations are not compatible with field-less operations. + const operationsWithCompatibility = [...possibleOperations].map((operationType) => { + const definition = operationDefinitionMap[operationType]; - return _.uniqBy( - [ - ...asOperationOptions(validOperationTypes, true), - ...asOperationOptions(possibleOperationTypes, false), - ], - 'operationType' - ); - } + return { + operationType, + compatibleWithCurrentField: + !selectedColumn || + (selectedColumn && + hasField(selectedColumn) && + definition.input === 'field' && + fieldByOperation[operationType]?.indexOf(selectedColumn.sourceField) !== -1) || + (selectedColumn && !hasField(selectedColumn) && definition.input !== 'field'), + }; + }); - const sideNavItems: EuiListGroupItemProps[] = getOperationTypes().map( + const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map( ({ operationType, compatibleWithCurrentField }) => { const isActive = Boolean( incompatibleSelectedOperationType === operationType || @@ -166,12 +164,30 @@ export function DimensionEditor(props: DimensionEditorProps) { compatibleWithCurrentField ? '' : ' incompatible' }`, onClick() { - // todo: when moving from terms agg to filters, we want to create a filter `$field.name : *` - // it probably has to be re-thought when removing the field name. - const isTermsToFilters = - selectedColumn?.operationType === 'terms' && operationType === 'filters'; - - if (!selectedColumn || !compatibleWithCurrentField) { + if (operationDefinitionMap[operationType].input === 'none') { + // Clear invalid state because we are creating a valid column + setInvalidOperationType(null); + if (selectedColumn?.operationType === operationType) { + return; + } + setState( + changeColumn({ + state, + layerId, + columnId, + newColumn: buildColumn({ + columns: props.state.layers[props.layerId].columns, + suggestedPriority: props.suggestedPriority, + layerId: props.layerId, + op: operationType, + indexPattern: currentIndexPattern, + previousColumn: selectedColumn, + }), + }) + ); + trackUiEvent(`indexpattern_dimension_operation_${operationType}`); + return; + } else if (!selectedColumn || !compatibleWithCurrentField) { const possibleFields = fieldByOperation[operationType] || []; if (possibleFields.length === 1) { @@ -197,19 +213,20 @@ export function DimensionEditor(props: DimensionEditorProps) { trackUiEvent(`indexpattern_dimension_operation_${operationType}`); return; } - if (incompatibleSelectedOperationType && !isTermsToFilters) { - setInvalidOperationType(null); - } - if (selectedColumn.operationType === operationType) { + + setInvalidOperationType(null); + + if (selectedColumn?.operationType === operationType) { return; } + const newColumn: IndexPatternColumn = buildColumn({ columns: props.state.layers[props.layerId].columns, suggestedPriority: props.suggestedPriority, layerId: props.layerId, op: operationType, indexPattern: currentIndexPattern, - field: fieldMap[selectedColumn.sourceField], + field: hasField(selectedColumn) ? fieldMap[selectedColumn.sourceField] : undefined, previousColumn: selectedColumn, }); @@ -244,95 +261,102 @@ export function DimensionEditor(props: DimensionEditorProps) {
- - { - setState( - deleteColumn({ - state, - layerId, - columnId, - }) - ); - }} - onChoose={(choice) => { - let column: IndexPatternColumn; - if ( - !incompatibleSelectedOperationType && - selectedColumn && - 'field' in choice && - choice.operationType === selectedColumn.operationType - ) { - // If we just changed the field are not in an error state and the operation didn't change, - // we use the operations onFieldChange method to calculate the new column. - column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]); - } else { - // Otherwise we'll use the buildColumn method to calculate a new column - const compatibleOperations = - ('field' in choice && - operationFieldSupportMatrix.operationByField[choice.field]) || - []; - let operation; - if (compatibleOperations.length > 0) { - operation = - incompatibleSelectedOperationType && - compatibleOperations.includes(incompatibleSelectedOperationType) - ? incompatibleSelectedOperationType - : compatibleOperations[0]; - } else if ('field' in choice) { - operation = choice.operationType; - } - column = buildColumn({ - columns: props.state.layers[props.layerId].columns, - field: fieldMap[choice.field], - indexPattern: currentIndexPattern, - layerId: props.layerId, - suggestedPriority: props.suggestedPriority, - op: operation as OperationType, - previousColumn: selectedColumn, - }); + > + { + setState( + deleteColumn({ + state, + layerId, + columnId, + }) + ); + }} + onChoose={(choice) => { + let column: IndexPatternColumn; + if ( + !incompatibleSelectedOperationType && + selectedColumn && + 'field' in choice && + choice.operationType === selectedColumn.operationType + ) { + // If we just changed the field are not in an error state and the operation didn't change, + // we use the operations onFieldChange method to calculate the new column. + column = changeField(selectedColumn, currentIndexPattern, fieldMap[choice.field]); + } else { + // Otherwise we'll use the buildColumn method to calculate a new column + const compatibleOperations = + ('field' in choice && operationSupportMatrix.operationByField[choice.field]) || + []; + let operation; + if (compatibleOperations.length > 0) { + operation = + incompatibleSelectedOperationType && + compatibleOperations.includes(incompatibleSelectedOperationType) + ? incompatibleSelectedOperationType + : compatibleOperations[0]; + } else if ('field' in choice) { + operation = choice.operationType; + } + column = buildColumn({ + columns: props.state.layers[props.layerId].columns, + field: fieldMap[choice.field], + indexPattern: currentIndexPattern, + layerId: props.layerId, + suggestedPriority: props.suggestedPriority, + op: operation as OperationType, + previousColumn: selectedColumn, + }); + } - setState( - changeColumn({ - state, - layerId, - columnId, - newColumn: column, - keepParams: false, - }) - ); - setInvalidOperationType(null); - }} - /> - + setState( + changeColumn({ + state, + layerId, + columnId, + newColumn: column, + keepParams: false, + }) + ); + setInvalidOperationType(null); + }} + /> + + ) : null} - {!incompatibleSelectedOperationType && ParamEditor && ( + {!incompatibleSelectedOperationType && selectedColumn && ParamEditor && ( <> - { let state: IndexPatternPrivateState; let setState: jest.Mock; let defaultProps: IndexPatternDimensionEditorProps; let dragDropContext: DragContextState; + function getStateWithColumns(columns: Record) { + return { ...state, layers: { first: { ...state.layers.first, columns } } }; + } + beforeEach(() => { state = { indexPatternRefs: [], @@ -179,7 +207,7 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(filterOperations).toBeCalled(); }); - it('should show field select combo box on click', () => { + it('should show field select', () => { wrapper = mount(); expect( @@ -187,6 +215,29 @@ describe('IndexPatternDimensionEditorPanel', () => { ).toHaveLength(1); }); + it('should not show field select on fieldless operation', () => { + wrapper = mount( + + ); + + expect( + wrapper.find(EuiComboBox).filter('[data-test-subj="indexPattern-dimension-field"]') + ).toHaveLength(0); + }); + it('should not show any choices if the filter returns false', () => { wrapper = mount( { wrapper = mount( ); @@ -292,26 +324,7 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper = mount( ); @@ -324,30 +337,15 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(items.find(({ label }) => label === 'Date histogram')!['data-test-subj']).toContain( 'incompatible' ); + + // Fieldless operation is compatible with field + expect(items.find(({ label }) => label === 'Filters')!['data-test-subj']).toContain( + 'compatible' + ); }); it('should keep the operation when switching to another field compatible with this operation', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - label: 'Max of bytes', - dataType: 'number', - isBucketed: false, - - // Private - operationType: 'max', - sourceField: 'bytes', - params: { format: { id: 'bytes' } }, - }, - }, - }, - }, - }; + const initialState: IndexPatternPrivateState = getStateWithColumns({ col1: bytesColumn }); wrapper = mount( @@ -415,27 +413,7 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper = mount( ); @@ -505,27 +483,7 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper = mount( ); @@ -553,28 +511,13 @@ describe('IndexPatternDimensionEditorPanel', () => { wrapper = mount( ); @@ -640,6 +583,62 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); }); + it('should leave error state if the original operation is re-selected', () => { + wrapper = mount(); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') + .simulate('click'); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-date_histogram"]') + .simulate('click'); + + expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); + }); + + it('should leave error state when switching from incomplete state to fieldless operation', () => { + wrapper = mount(); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') + .simulate('click'); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-filters incompatible"]') + .simulate('click'); + + expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); + }); + + it('should leave error state when re-selecting the original fieldless function', () => { + wrapper = mount( + + ); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-terms incompatible"]') + .simulate('click'); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-filters"]') + .simulate('click'); + + expect(wrapper.find('[data-test-subj="indexPattern-invalid-operation"]')).toHaveLength(0); + }); + it('should indicate fields compatible with selected operation', () => { wrapper = mount(); @@ -701,28 +700,18 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('should select the Records field when count is selected', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: { - dataType: 'number', - isBucketed: false, - label: '', - operationType: 'avg', - sourceField: 'bytes', - }, - }, - }, - }, - }; wrapper = mount( ); @@ -737,28 +726,18 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('should indicate document and field compatibility with selected document operation', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: { - dataType: 'number', - isBucketed: false, - label: '', - operationType: 'count', - sourceField: 'Records', - }, - }, - }, - }, - }; wrapper = mount( ); @@ -942,28 +921,18 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('should indicate document compatibility when document operation is selected', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: { - dataType: 'number', - isBucketed: false, - label: '', - operationType: 'count', - sourceField: 'Records', - }, - }, - }, - }, - }; wrapper = mount( ); @@ -991,12 +960,12 @@ describe('IndexPatternDimensionEditorPanel', () => { const items: EuiListGroupItemProps[] = wrapper.find(EuiListGroup).prop('listItems') || []; expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([ - 'Unique count', 'Average', 'Count', 'Maximum', 'Minimum', 'Sum', + 'Unique count', ]); }); @@ -1031,26 +1000,9 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('should use helper function when changing the function', () => { - const initialState: IndexPatternPrivateState = { - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - label: 'Max of bytes', - dataType: 'number', - isBucketed: false, - - // Private - operationType: 'max', - sourceField: 'bytes', - }, - }, - }, - }, - }; + const initialState: IndexPatternPrivateState = getStateWithColumns({ + col1: bytesColumn, + }); wrapper = mount( ); @@ -1095,25 +1047,16 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('allows custom format', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of memory', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'memory', - }, - }, - }, + const stateWithNumberCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Average of memory', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'memory', }, - }; + }); wrapper = mount( @@ -1145,29 +1088,19 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('keeps decimal places while switching', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of memory', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'memory', - params: { - format: { id: 'bytes', params: { decimals: 0 } }, - }, - }, - }, + const stateWithNumberCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Average of memory', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'memory', + params: { + format: { id: 'bytes', params: { decimals: 0 } }, }, }, - }; - + }); wrapper = mount( ); @@ -1195,28 +1128,19 @@ describe('IndexPatternDimensionEditorPanel', () => { }); it('allows custom format with number of decimal places', () => { - const stateWithNumberCol: IndexPatternPrivateState = { - ...state, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Average of memory', - dataType: 'number', - isBucketed: false, - // Private - operationType: 'avg', - sourceField: 'memory', - params: { - format: { id: 'bytes', params: { decimals: 2 } }, - }, - }, - }, + const stateWithNumberCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Average of memory', + dataType: 'number', + isBucketed: false, + // Private + operationType: 'avg', + sourceField: 'memory', + params: { + format: { id: 'bytes', params: { decimals: 2 } }, }, }, - }; + }); wrapper = mount( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 923f7145d1c64..c4d8300722f83 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -46,8 +46,9 @@ export type IndexPatternDimensionEditorProps = DatasourceDimensionEditorProps< dateRange: DateRange; }; -export interface OperationFieldSupportMatrix { +export interface OperationSupportMatrix { operationByField: Partial>; + operationWithoutField: OperationType[]; fieldByOperation: Partial>; } @@ -58,7 +59,7 @@ type Props = Pick< // TODO: This code has historically been memoized, as a potentially performance // sensitive task. If we can add memoization without breaking the behavior, we should. -const getOperationFieldSupportMatrix = (props: Props): OperationFieldSupportMatrix => { +const getOperationSupportMatrix = (props: Props): OperationSupportMatrix => { const layerId = props.layerId; const currentIndexPattern = props.state.indexPatterns[props.state.layers[layerId].indexPatternId]; @@ -67,37 +68,43 @@ const getOperationFieldSupportMatrix = (props: Props): OperationFieldSupportMatr ).filter((operation) => props.filterOperations(operation.operationMetaData)); const supportedOperationsByField: Partial> = {}; + const supportedOperationsWithoutField: OperationType[] = []; const supportedFieldsByOperation: Partial> = {}; filteredOperationsByMetadata.forEach(({ operations }) => { operations.forEach((operation) => { - if (supportedOperationsByField[operation.field]) { - supportedOperationsByField[operation.field]!.push(operation.operationType); - } else { - supportedOperationsByField[operation.field] = [operation.operationType]; - } - - if (supportedFieldsByOperation[operation.operationType]) { - supportedFieldsByOperation[operation.operationType]!.push(operation.field); - } else { - supportedFieldsByOperation[operation.operationType] = [operation.field]; + if (operation.type === 'field') { + if (supportedOperationsByField[operation.field]) { + supportedOperationsByField[operation.field]!.push(operation.operationType); + } else { + supportedOperationsByField[operation.field] = [operation.operationType]; + } + + if (supportedFieldsByOperation[operation.operationType]) { + supportedFieldsByOperation[operation.operationType]!.push(operation.field); + } else { + supportedFieldsByOperation[operation.operationType] = [operation.field]; + } + } else if (operation.type === 'none') { + supportedOperationsWithoutField.push(operation.operationType); } }); }); return { operationByField: _.mapValues(supportedOperationsByField, _.uniq), + operationWithoutField: _.uniq(supportedOperationsWithoutField), fieldByOperation: _.mapValues(supportedFieldsByOperation, _.uniq), }; }; export function canHandleDrop(props: DatasourceDimensionDropProps) { - const operationFieldSupportMatrix = getOperationFieldSupportMatrix(props); + const operationSupportMatrix = getOperationSupportMatrix(props); const { dragging } = props.dragDropContext; const layerIndexPatternId = props.state.layers[props.layerId].indexPatternId; function hasOperationForField(field: IndexPatternField) { - return Boolean(operationFieldSupportMatrix.operationByField[field.name]); + return Boolean(operationSupportMatrix.operationByField[field.name]); } if (isDraggedField(dragging)) { @@ -119,11 +126,11 @@ export function canHandleDrop(props: DatasourceDimensionDropProps) { - const operationFieldSupportMatrix = getOperationFieldSupportMatrix(props); + const operationSupportMatrix = getOperationSupportMatrix(props); const droppedItem = props.droppedItem; function hasOperationForField(field: IndexPatternField) { - return Boolean(operationFieldSupportMatrix.operationByField[field.name]); + return Boolean(operationSupportMatrix.operationByField[field.name]); } if (isDraggedOperation(droppedItem) && droppedItem.layerId === props.layerId) { @@ -167,8 +174,7 @@ export function onDrop(props: DatasourceDimensionDropHandlerProps ); }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 60f60d7cb80c1..de472cb09cdfe 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -20,7 +20,7 @@ import { EuiHighlight } from '@elastic/eui'; import { OperationType } from '../indexpattern'; import { LensFieldIcon } from '../lens_field_icon'; import { DataType } from '../../types'; -import { OperationFieldSupportMatrix } from './dimension_panel'; +import { OperationSupportMatrix } from './dimension_panel'; import { IndexPattern, IndexPatternField, IndexPatternPrivateState } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { fieldExists } from '../pure_helpers'; @@ -37,7 +37,7 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> { incompatibleSelectedOperationType: OperationType | null; selectedColumnOperationType?: OperationType; selectedColumnSourceField?: string; - operationFieldSupportMatrix: OperationFieldSupportMatrix; + operationSupportMatrix: OperationSupportMatrix; onChoose: (choice: FieldChoice) => void; onDeleteColumn: () => void; existingFields: IndexPatternPrivateState['existingFields']; @@ -49,13 +49,13 @@ export function FieldSelect({ incompatibleSelectedOperationType, selectedColumnOperationType, selectedColumnSourceField, - operationFieldSupportMatrix, + operationSupportMatrix, onChoose, onDeleteColumn, existingFields, ...rest }: FieldSelectProps) { - const { operationByField } = operationFieldSupportMatrix; + const { operationByField } = operationSupportMatrix; const memoizedFieldOptions = useMemo(() => { const fields = Object.keys(operationByField).sort(); @@ -116,7 +116,8 @@ export function FieldSelect({ })); } - const [availableFields, emptyFields] = _.partition(normalFields, containsData); + const [metaFields, nonMetaFields] = _.partition(normalFields, (field) => fieldMap[field].meta); + const [availableFields, emptyFields] = _.partition(nonMetaFields, containsData); const constructFieldsOptions = (fieldsArr: string[], label: string) => fieldsArr.length > 0 && { @@ -138,10 +139,18 @@ export function FieldSelect({ }) ); + const metaFieldsOptions = constructFieldsOptions( + metaFields, + i18n.translate('xpack.lens.indexPattern.metaFieldsLabel', { + defaultMessage: 'Meta fields', + }) + ); + return [ ...fieldNamesToOptions(specialFields), availableFieldsOptions, emptyFieldsOptions, + metaFieldsOptions, ].filter(Boolean); }, [ incompatibleSelectedOperationType, @@ -164,15 +173,13 @@ export function FieldSelect({ options={(memoizedFieldOptions as unknown) as EuiComboBoxOptionOption[]} isInvalid={Boolean(incompatibleSelectedOperationType)} selectedOptions={ - ((selectedColumnOperationType - ? selectedColumnSourceField - ? [ - { - label: fieldMap[selectedColumnSourceField].displayName, - value: { type: 'field', field: selectedColumnSourceField }, - }, - ] - : [memoizedFieldOptions[0]] + ((selectedColumnOperationType && selectedColumnSourceField + ? [ + { + label: fieldMap[selectedColumnSourceField].displayName, + value: { type: 'field', field: selectedColumnSourceField }, + }, + ] : []) as unknown) as EuiComboBoxOptionOption[] } singleSelection={{ asPlainText: true }} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/_field_item.scss b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss similarity index 100% rename from x-pack/plugins/lens/public/indexpattern_datasource/_field_item.scss rename to x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 1f6d7911b3a33..7377d15bca6d7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './field_item.scss'; + import React, { useState } from 'react'; import DateMath from '@elastic/datemath'; import { @@ -184,7 +186,8 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { defaultMessage: 'Click for a field preview, or drag and drop to visualize.', }) : i18n.translate('xpack.lens.indexPattern.fieldStatsButtonEmptyLabel', { - defaultMessage: "This field doesn't have data. Drag and drop to visualize.", + defaultMessage: + 'This field doesn’t have any data but you can still drag and drop to visualize.', }) } type="iInCircle" @@ -195,7 +198,6 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { return ( ('.application') || undefined} @@ -307,7 +309,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { {i18n.translate('xpack.lens.indexPattern.fieldStatsNoData', { defaultMessage: - 'This field is empty because it doesn’t exist in the 500 sampled documents.', + 'This field is empty because it doesn’t exist in the 500 sampled documents. Adding this field to the configuration may result in a blank chart.', })} ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss new file mode 100644 index 0000000000000..f28581b835b07 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.scss @@ -0,0 +1,20 @@ +/** + * 1. Don't cut off the shadow of the field items + */ + +.lnsIndexPatternFieldList { + @include euiOverflowShadow; + @include euiScrollBar; + margin-left: -$euiSize; /* 1 */ + position: relative; + flex-grow: 1; + overflow: auto; +} + +.lnsIndexPatternFieldList__accordionContainer { + padding-top: $euiSizeS; + position: absolute; + top: 0; + left: $euiSize; /* 1 */ + right: $euiSizeXS; /* 1 */ +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx new file mode 100644 index 0000000000000..4a9b3a0c63e3f --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_list.tsx @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 './field_list.scss'; +import { throttle } from 'lodash'; +import React, { useState, Fragment, useCallback, useMemo, useEffect } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { FieldItem } from './field_item'; +import { NoFieldsCallout } from './no_fields_callout'; +import { IndexPatternField } from './types'; +import { FieldItemSharedProps, FieldsAccordion } from './fields_accordion'; +const PAGINATION_SIZE = 50; + +export interface FieldsGroup { + specialFields: IndexPatternField[]; + availableFields: IndexPatternField[]; + emptyFields: IndexPatternField[]; + metaFields: IndexPatternField[]; +} + +export type FieldGroups = Record< + string, + { + fields: IndexPatternField[]; + fieldCount: number; + showInAccordion: boolean; + isInitiallyOpen: boolean; + title: string; + isAffectedByGlobalFilter: boolean; + isAffectedByTimeFilter: boolean; + hideDetails?: boolean; + } +>; + +function getDisplayedFieldsLength( + fieldGroups: FieldGroups, + accordionState: Partial> +) { + return Object.entries(fieldGroups) + .filter(([key]) => accordionState[key]) + .reduce((allFieldCount, [, { fields }]) => allFieldCount + fields.length, 0); +} + +export function FieldList({ + exists, + fieldGroups, + existenceFetchFailed, + fieldProps, + hasSyncedExistingFields, + filter, + currentIndexPatternId, +}: { + exists: (field: IndexPatternField) => boolean; + fieldGroups: FieldGroups; + fieldProps: FieldItemSharedProps; + hasSyncedExistingFields: boolean; + existenceFetchFailed?: boolean; + filter: { + nameFilter: string; + typeFilter: string[]; + }; + currentIndexPatternId: string; +}) { + const [pageSize, setPageSize] = useState(PAGINATION_SIZE); + const [scrollContainer, setScrollContainer] = useState(undefined); + const [accordionState, setAccordionState] = useState>>(() => + Object.fromEntries( + Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, { isInitiallyOpen }]) => [key, isInitiallyOpen]) + ) + ); + + const isAffectedByFieldFilter = !!(filter.typeFilter.length || filter.nameFilter.length); + + useEffect(() => { + // Reset the scroll if we have made material changes to the field list + if (scrollContainer) { + scrollContainer.scrollTop = 0; + setPageSize(PAGINATION_SIZE); + } + }, [filter.nameFilter, filter.typeFilter, currentIndexPatternId, scrollContainer]); + + const lazyScroll = useCallback(() => { + if (scrollContainer) { + const nearBottom = + scrollContainer.scrollTop + scrollContainer.clientHeight > + scrollContainer.scrollHeight * 0.9; + if (nearBottom) { + setPageSize( + Math.max( + PAGINATION_SIZE, + Math.min( + pageSize + PAGINATION_SIZE * 0.5, + getDisplayedFieldsLength(fieldGroups, accordionState) + ) + ) + ); + } + } + }, [scrollContainer, pageSize, setPageSize, fieldGroups, accordionState]); + + const paginatedFields = useMemo(() => { + let remainingItems = pageSize; + return Object.fromEntries( + Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, fieldGroup]) => { + if (!accordionState[key] || remainingItems <= 0) { + return [key, []]; + } + const slicedFieldList = fieldGroup.fields.slice(0, remainingItems); + remainingItems = remainingItems - slicedFieldList.length; + return [key, slicedFieldList]; + }) + ); + }, [pageSize, fieldGroups, accordionState]); + + return ( +
{ + if (el && !el.dataset.dynamicScroll) { + el.dataset.dynamicScroll = 'true'; + setScrollContainer(el); + } + }} + onScroll={throttle(lazyScroll, 100)} + > +
+ {Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => !showInAccordion) + .flatMap(([, { fields }]) => + fields.map((field) => ( + + )) + )} + + {Object.entries(fieldGroups) + .filter(([, { showInAccordion }]) => showInAccordion) + .map(([key, fieldGroup]) => ( + + { + setAccordionState((s) => ({ + ...s, + [key]: open, + })); + const displayedFieldLength = getDisplayedFieldsLength(fieldGroups, { + ...accordionState, + [key]: open, + }); + setPageSize( + Math.max(PAGINATION_SIZE, Math.min(pageSize * 1.5, displayedFieldLength)) + ); + }} + showExistenceFetchError={existenceFetchFailed} + renderCallout={ + + } + /> + + + ))} +
+
+ ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx index b0604efff7b89..7d1c80e5a7f6a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.test.tsx @@ -71,11 +71,19 @@ describe('Fields Accordion', () => { paginatedFields: indexPattern.fields, fieldProps, renderCallout:
Callout
, - exists: true, + exists: () => true, }; }); it('renders correct number of Field Items', () => { + const wrapper = mountWithIntl( + field.name === 'timestamp'} /> + ); + expect(wrapper.find(FieldItem).at(0).prop('exists')).toEqual(true); + expect(wrapper.find(FieldItem).at(1).prop('exists')).toEqual(false); + }); + + it('passed correct exists flag to each field', () => { const wrapper = mountWithIntl(); expect(wrapper.find(FieldItem).length).toEqual(2); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx index 30a92c21ff661..e531eb72f94ca 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/fields_accordion.tsx @@ -45,7 +45,7 @@ export interface FieldsAccordionProps { paginatedFields: IndexPatternField[]; fieldProps: FieldItemSharedProps; renderCallout: JSX.Element; - exists: boolean; + exists: (field: IndexPatternField) => boolean; showExistenceFetchError?: boolean; hideDetails?: boolean; } @@ -71,7 +71,7 @@ export const InnerFieldsAccordion = function InnerFieldsAccordion({ {...fieldProps} key={field.name} field={field} - exists={exists} + exists={exists(field)} hideDetails={hideDetails} /> ), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index 45d0ee45fab4c..4fbed04112632 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -6,8 +6,6 @@ import { CoreSetup } from 'kibana/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { getIndexPatternDatasource } from './indexpattern'; -import { renameColumns } from './rename_columns'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { @@ -34,17 +32,17 @@ export class IndexPatternDatasource { core: CoreSetup, { expressions, editorFrame, charts }: IndexPatternDatasourceSetupPlugins ) { - expressions.registerFunction(renameColumns); - - editorFrame.registerDatasource( - core.getStartServices().then(([coreStart, { data }]) => + editorFrame.registerDatasource(async () => { + const { getIndexPatternDatasource, renameColumns } = await import('../async_services'); + expressions.registerFunction(renameColumns); + return core.getStartServices().then(([coreStart, { data }]) => getIndexPatternDatasource({ core: coreStart, storage: new Storage(localStorage), data, charts, }) - ) as Promise - ); + ) as Promise; + }); } } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 3b3750cf7c560..7f7eb0bc0fdac 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -104,6 +104,8 @@ export function uniqueLabels(layers: Record) { return columnLabelMap; } +export * from './rename_columns'; + export function getIndexPatternDatasource({ core, storage, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index f3aa9c4f51c82..f5e64149c2c76 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -483,11 +483,15 @@ function createChangedNestingSuggestion(state: IndexPatternPrivateState, layerId const updatedLayer = { ...layer, columnOrder: [secondBucket, firstBucket, ...rest] }; const currentFields = state.indexPatterns[state.currentIndexPatternId].fields; const firstBucketLabel = - currentFields.find((field) => field.name === layer.columns[firstBucket].sourceField) - ?.displayName || ''; + currentFields.find((field) => { + const column = layer.columns[firstBucket]; + return hasField(column) && column.sourceField === field.name; + })?.displayName || ''; const secondBucketLabel = - currentFields.find((field) => field.name === layer.columns[secondBucket].sourceField) - ?.displayName || ''; + currentFields.find((field) => { + const column = layer.columns[secondBucket]; + return hasField(column) && column.sourceField === field.name; + })?.displayName || ''; return buildSuggestion({ state, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 19213d4afc9bc..ef6abbec9a34d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -197,7 +197,7 @@ function mockClient() { function mockIndexPatternsService() { return ({ get: jest.fn(async (id: '1' | '2') => { - return sampleIndexPatternsFromService[id]; + return { ...sampleIndexPatternsFromService[id], metaFields: [] }; }), } as unknown) as Pick; } @@ -248,6 +248,7 @@ describe('loader', () => { get: jest.fn(async () => ({ id: 'foo', title: 'Foo index', + metaFields: [], typeMeta: { aggs: { date_histogram: { @@ -295,6 +296,55 @@ describe('loader', () => { date_histogram: { agg: 'date_histogram', fixed_interval: 'm' }, }); }); + + it('should map meta flag', async () => { + const cache = await loadIndexPatterns({ + cache: {}, + patterns: ['foo'], + indexPatternsService: ({ + get: jest.fn(async () => ({ + id: 'foo', + title: 'Foo index', + metaFields: ['timestamp'], + typeMeta: { + aggs: { + date_histogram: { + timestamp: { + agg: 'date_histogram', + fixed_interval: 'm', + }, + }, + sum: { + bytes: { + agg: 'sum', + }, + }, + }, + }, + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ], + })), + } as unknown) as Pick, + }); + + expect(cache.foo.fields.find((f: IndexPatternField) => f.name === 'timestamp')!.meta).toEqual( + true + ); + }); }); describe('loadInitialState', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 0ab658b961336..c4b1eb9e0c4c4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -63,6 +63,7 @@ export async function loadIndexPatterns({ type: field.type, aggregatable: field.aggregatable, searchable: field.searchable, + meta: indexPattern.metaFields.includes(field.name), esTypes: field.esTypes, scripted: field.scripted, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index b0777c7febd7d..65119d3978ee6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; -import { FormattedIndexPatternColumn } from './column_types'; +import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); @@ -21,15 +21,18 @@ function ofName(name: string) { }); } -export interface CardinalityIndexPatternColumn extends FormattedIndexPatternColumn { +export interface CardinalityIndexPatternColumn + extends FormattedIndexPatternColumn, + FieldBasedIndexPatternColumn { operationType: 'cardinality'; } -export const cardinalityOperation: OperationDefinition = { +export const cardinalityOperation: OperationDefinition = { type: OPERATION_TYPE, displayName: i18n.translate('xpack.lens.indexPattern.cardinality', { defaultMessage: 'Unique count', }), + input: 'field', getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( supportedTypes.has(type) && diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts index 3244eeb94d1e2..2e95e3fd4250f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/column_types.ts @@ -14,7 +14,6 @@ import { Operation, DimensionPriority } from '../../../types'; export interface BaseIndexPatternColumn extends Operation { // Private operationType: string; - sourceField: string; suggestedPriority?: DimensionPriority; customLabel?: boolean; } @@ -31,23 +30,6 @@ export interface FormattedIndexPatternColumn extends BaseIndexPatternColumn { }; } -/** - * Base type for a column that doesn't have additional parameter. - * - * * `TOperationType` should be a string type containing just the type - * of the operation (e.g. `"sum"`). - * * `TBase` is the base column interface the operation type is set for - - * by default this is `FieldBasedIndexPatternColumn`, so - * `ParameterlessIndexPatternColumn<'foo'>` will give you a column type - * for an operation named foo that operates on a field. - * By passing in another `TBase` (e.g. just `BaseIndexPatternColumn`), - * you can also create other column types. - */ -export type ParameterlessIndexPatternColumn< - TOperationType extends string, - TBase extends BaseIndexPatternColumn = FieldBasedIndexPatternColumn -> = TBase & { operationType: TOperationType }; - export interface FieldBasedIndexPatternColumn extends BaseIndexPatternColumn { - suggestedPriority?: DimensionPriority; + sourceField: string; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index bb1aef856de78..cdf1a6b760493 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -6,23 +6,25 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; -import { FormattedIndexPatternColumn } from './column_types'; +import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; import { IndexPatternField } from '../../types'; const countLabel = i18n.translate('xpack.lens.indexPattern.countOf', { defaultMessage: 'Count of records', }); -export type CountIndexPatternColumn = FormattedIndexPatternColumn & { - operationType: 'count'; -}; +export type CountIndexPatternColumn = FormattedIndexPatternColumn & + FieldBasedIndexPatternColumn & { + operationType: 'count'; + }; -export const countOperation: OperationDefinition = { +export const countOperation: OperationDefinition = { type: 'count', priority: 2, displayName: i18n.translate('xpack.lens.indexPattern.count', { defaultMessage: 'Count', }), + input: 'field', onFieldChange: (oldColumn, indexPattern, field) => { return { ...oldColumn, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index 83ae8e47b39ca..185f44405bb4b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiForm, EuiFormRow, EuiSwitch, EuiSwitchEvent, @@ -37,12 +36,16 @@ export interface DateHistogramIndexPatternColumn extends FieldBasedIndexPatternC }; } -export const dateHistogramOperation: OperationDefinition = { +export const dateHistogramOperation: OperationDefinition< + DateHistogramIndexPatternColumn, + 'field' +> = { type: 'date_histogram', displayName: i18n.translate('xpack.lens.indexPattern.dateHistogram', { defaultMessage: 'Date histogram', }), - priority: 3, // Higher than any metric + input: 'field', + priority: 5, // Highest priority level used getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( type === 'date' && @@ -137,7 +140,7 @@ export const dateHistogramOperation: OperationDefinition { + paramEditor: ({ state, setState, currentColumn, layerId, dateRange, data }) => { const field = currentColumn && state.indexPatterns[state.layers[layerId].indexPatternId].fields.find( @@ -180,7 +183,7 @@ export const dateHistogramOperation: OperationDefinition + <> {!intervalIsRestricted && ( )} - + ); }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index 2d79c5faf74fe..3ac01886537dc 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -59,7 +59,6 @@ describe('filters', () => { operationType: 'filters', scale: 'ordinal', isBucketed: true, - sourceField: 'Records', params: { filters: [ { @@ -112,34 +111,14 @@ describe('filters', () => { }); }); - describe('getPossibleOperationForField', () => { + describe('getPossibleOperation', () => { it('should return operation with the right type for document', () => { - expect( - filtersOperation.getPossibleOperationForField({ - aggregatable: true, - searchable: true, - name: 'test', - displayName: 'test', - type: 'document', - }) - ).toEqual({ + expect(filtersOperation.getPossibleOperation()).toEqual({ dataType: 'string', isBucketed: true, scale: 'ordinal', }); }); - - it('should not return operation if field type is not document', () => { - expect( - filtersOperation.getPossibleOperationForField({ - aggregatable: false, - searchable: true, - name: 'test', - displayName: 'test', - type: 'string', - }) - ).toEqual(undefined); - }); }); describe('popover param editor', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index cc1e23cb82a49..ad0b9f2dbb0ab 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiLink, htmlIdGenerator } from '@elastic/eui'; import { updateColumnParam } from '../../../state_helpers'; import { OperationDefinition } from '../index'; -import { FieldBasedIndexPatternColumn } from '../column_types'; +import { BaseIndexPatternColumn } from '../column_types'; import { FilterPopover } from './filter_popover'; import { IndexPattern } from '../../../types'; import { Query, esKuery, esQuery } from '../../../../../../../../src/plugins/data/public'; @@ -61,31 +61,22 @@ export const isQueryValid = (input: Query, indexPattern: IndexPattern) => { } }; -export interface FiltersIndexPatternColumn extends FieldBasedIndexPatternColumn { +export interface FiltersIndexPatternColumn extends BaseIndexPatternColumn { operationType: 'filters'; params: { filters: Filter[]; }; } -export const filtersOperation: OperationDefinition = { +export const filtersOperation: OperationDefinition = { type: 'filters', displayName: filtersLabel, priority: 3, // Higher than any metric - getPossibleOperationForField: ({ type }) => { - if (type === 'document') { - return { - dataType: 'string', - isBucketed: true, - scale: 'ordinal', - }; - } - }, - isTransferable: () => false, - onFieldChange: (oldColumn, indexPattern, field) => oldColumn, + input: 'none', + isTransferable: () => true, - buildColumn({ suggestedPriority, field, previousColumn }) { + buildColumn({ suggestedPriority, previousColumn }) { let params = { filters: [defaultFilter] }; if (previousColumn?.operationType === 'terms') { params = { @@ -108,11 +99,18 @@ export const filtersOperation: OperationDefinition = scale: 'ordinal', suggestedPriority, isBucketed: true, - sourceField: field.name, params, }; }, + getPossibleOperation() { + return { + dataType: 'string', + isBucketed: true, + scale: 'ordinal', + }; + }, + toEsAggsConfig: (column, columnId, indexPattern) => { const validFilters = column.params.filters?.filter((f: Filter) => isQueryValid(f.input, indexPattern) @@ -226,6 +224,7 @@ export const FilterList = ({ removeTitle={i18n.translate('xpack.lens.indexPattern.filters.removeFilter', { defaultMessage: 'Remove a filter', })} + isNotRemovable={localFilters.length === 1} > ; + +// List of all operation definitions registered to this data source. +// If you want to implement a new operation, add the definition to this array and +// the column type to the `IndexPatternColumn` union type below. +const internalOperationDefinitions = [ + filtersOperation, + termsOperation, + dateHistogramOperation, + minOperation, + maxOperation, + averageOperation, + cardinalityOperation, + sumOperation, + countOperation, + rangeOperation, +]; + export { termsOperation } from './terms'; +export { rangeOperation } from './ranges'; export { filtersOperation } from './filters'; export { dateHistogramOperation } from './date_histogram'; export { minOperation, averageOperation, sumOperation, maxOperation } from './metrics'; @@ -67,7 +73,7 @@ export { countOperation } from './count'; /** * Properties passed to the operation-specific part of the popover editor */ -export interface ParamEditorProps { +export interface ParamEditorProps { currentColumn: C; state: IndexPatternPrivateState; setState: StateSetter; @@ -134,13 +140,25 @@ interface BaseBuildColumnArgs { indexPattern: IndexPattern; } -/** - * Shape of an operation definition. If the type parameter of the definition - * indicates a field based column, `getPossibleOperationForField` has to be - * specified, otherwise `getPossibleOperationForDocument` has to be defined. - */ -export interface OperationDefinition - extends BaseOperationDefinitionProps { +interface FieldlessOperationDefinition { + input: 'none'; + /** + * Builds the column object for the given parameters. Should include default p + */ + buildColumn: ( + arg: BaseBuildColumnArgs & { + previousColumn?: IndexPatternColumn; + } + ) => C; + /** + * Returns the meta data of the operation if applied. Undefined + * if the field is not applicable. + */ + getPossibleOperation: () => OperationMetadata | undefined; +} + +interface FieldBasedOperationDefinition { + input: 'field'; /** * Returns the meta data of the operation if applied to the given field. Undefined * if the field is not applicable to the operation. @@ -152,7 +170,8 @@ export interface OperationDefinition buildColumn: ( arg: BaseBuildColumnArgs & { field: IndexPatternField; - previousColumn?: IndexPatternColumn; + // previousColumn?: IndexPatternColumn; + previousColumn?: C; } ) => C; /** @@ -171,9 +190,29 @@ export interface OperationDefinition * @param indexPattern The index pattern that field is on. * @param field The field that the user changed to. */ - onFieldChange: (oldColumn: C, indexPattern: IndexPattern, field: IndexPatternField) => C; + onFieldChange: ( + // oldColumn: FieldBasedIndexPatternColumn, + oldColumn: C, + indexPattern: IndexPattern, + field: IndexPatternField + ) => C; } +interface OperationDefinitionMap { + field: FieldBasedOperationDefinition; + none: FieldlessOperationDefinition; +} + +/** + * Shape of an operation definition. If the type parameter of the definition + * indicates a field based column, `getPossibleOperationForField` has to be + * specified, otherwise `getPossibleOperation` has to be defined. + */ +export type OperationDefinition< + C extends BaseIndexPatternColumn, + Input extends keyof OperationDefinitionMap +> = BaseOperationDefinitionProps & OperationDefinitionMap[Input]; + /** * A union type of all available operation types. The operation type is a unique id of an operation. * Each column is assigned to exactly one operation type. @@ -184,7 +223,9 @@ export type OperationType = typeof internalOperationDefinitions[number]['type']; * This is an operation definition of an unspecified column out of all possible * column types. */ -export type GenericOperationDefinition = OperationDefinition; +export type GenericOperationDefinition = + | OperationDefinition + | OperationDefinition; /** * List of all available operation definitions @@ -202,7 +243,10 @@ export const operationDefinitions = internalOperationDefinitions as GenericOpera * (e.g. `import { termsOperation } from './operations/definitions'`). This map is * intended to be used in situations where the operation type is not known during compile time. */ -export const operationDefinitionMap = internalOperationDefinitions.reduce( +export const operationDefinitionMap: Record< + string, + GenericOperationDefinition +> = internalOperationDefinitions.reduce( (definitionMap, definition) => ({ ...definitionMap, [definition.type]: definition }), {} -) as Record; +); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index 4c37d95f6b050..c02f7bcb7d2cd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -6,11 +6,12 @@ import { i18n } from '@kbn/i18n'; import { OperationDefinition } from './index'; -import { FormattedIndexPatternColumn } from './column_types'; +import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; -type MetricColumn = FormattedIndexPatternColumn & { - operationType: T; -}; +type MetricColumn = FormattedIndexPatternColumn & + FieldBasedIndexPatternColumn & { + operationType: T; + }; function buildMetricOperation>({ type, @@ -27,6 +28,7 @@ function buildMetricOperation>({ type, priority, displayName, + input: 'field', getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { if ( fieldType === 'number' && @@ -78,7 +80,7 @@ function buildMetricOperation>({ missing: 0, }, }), - } as OperationDefinition; + } as OperationDefinition; } export type SumIndexPatternColumn = MetricColumn<'sum'>; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss new file mode 100644 index 0000000000000..b1658043f3204 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss @@ -0,0 +1,6 @@ +.lnsRangesOperation__popoverButton { + @include euiTextBreakWord; + @include euiFontSizeS; + min-height: $euiSizeXL; + width: 100%; +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx new file mode 100644 index 0000000000000..af07f33ec175e --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -0,0 +1,296 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 './advanced_editor.scss'; + +import React, { useState, MouseEventHandler } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useDebounce } from 'react-use'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIcon, + EuiFieldNumber, + EuiLink, + EuiText, + EuiPopover, + EuiToolTip, + htmlIdGenerator, +} from '@elastic/eui'; +import { keys } from '@elastic/eui'; +import { IFieldFormat } from '../../../../../../../../src/plugins/data/common'; +import { RangeTypeLens, isValidRange, isValidNumber } from './ranges'; +import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; +import { NewBucketButton, DragDropBuckets, DraggableBucketContainer } from '../shared_components'; + +const generateId = htmlIdGenerator(); + +type LocalRangeType = RangeTypeLens & { id: string }; + +const getBetterLabel = (range: RangeTypeLens, formatter: IFieldFormat) => + range.label || + formatter.convert({ + gte: isValidNumber(range.from) ? range.from : FROM_PLACEHOLDER, + lt: isValidNumber(range.to) ? range.to : TO_PLACEHOLDER, + }); + +export const RangePopover = ({ + range, + setRange, + Button, + isOpenByCreation, + setIsOpenByCreation, +}: { + range: LocalRangeType; + setRange: (newRange: LocalRangeType) => void; + Button: React.FunctionComponent<{ onClick: MouseEventHandler }>; + isOpenByCreation: boolean; + setIsOpenByCreation: (open: boolean) => void; + formatter: IFieldFormat; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [tempRange, setTempRange] = useState(range); + + const saveRangeAndReset = (newRange: LocalRangeType, resetRange = false) => { + if (resetRange) { + // reset the temporary range for later use + setTempRange(range); + } + // send the range back to the main state + setRange(newRange); + }; + const { from, to } = tempRange; + + const lteAppendLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanOrEqualAppend', { + defaultMessage: '\u2264', + }); + const lteTooltipContent = i18n.translate( + 'xpack.lens.indexPattern.ranges.lessThanOrEqualTooltip', + { + defaultMessage: 'Less than or equal to', + } + ); + const ltPrependLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanPrepend', { + defaultMessage: '\u003c', + }); + const ltTooltipContent = i18n.translate('xpack.lens.indexPattern.ranges.lessThanTooltip', { + defaultMessage: 'Less than', + }); + + const onSubmit = () => { + setIsPopoverOpen(false); + setIsOpenByCreation(false); + saveRangeAndReset(tempRange, true); + }; + + return ( + { + setIsPopoverOpen((isOpen) => !isOpen); + setIsOpenByCreation(false); + }} + /> + } + data-test-subj="indexPattern-ranges-popover" + > + + + + { + const newRange = { + ...tempRange, + from: target.value !== '' ? Number(target.value) : -Infinity, + }; + setTempRange(newRange); + saveRangeAndReset(newRange); + }} + append={ + + {lteAppendLabel} + + } + fullWidth + compressed + placeholder={FROM_PLACEHOLDER} + isInvalid={!isValidRange(tempRange)} + /> + + + + + + { + const newRange = { + ...tempRange, + to: target.value !== '' ? Number(target.value) : -Infinity, + }; + setTempRange(newRange); + saveRangeAndReset(newRange); + }} + prepend={ + + {ltPrependLabel} + + } + fullWidth + compressed + placeholder={TO_PLACEHOLDER} + isInvalid={!isValidRange(tempRange)} + onKeyDown={({ key }: React.KeyboardEvent) => { + if (keys.ENTER === key && onSubmit) { + onSubmit(); + } + }} + /> + + + + + ); +}; + +export const AdvancedRangeEditor = ({ + ranges, + setRanges, + onToggleEditor, + formatter, +}: { + ranges: RangeTypeLens[]; + setRanges: (newRanges: RangeTypeLens[]) => void; + onToggleEditor: () => void; + formatter: IFieldFormat; +}) => { + // use a local state to store ids with range objects + const [localRanges, setLocalRanges] = useState(() => + ranges.map((range) => ({ ...range, id: generateId() })) + ); + // we need to force the open state of the popover from the outside in some scenarios + // so we need an extra state here + const [isOpenByCreation, setIsOpenByCreation] = useState(false); + + const lastIndex = localRanges.length - 1; + + // Update locally all the time, but bounce the parents prop function + // to aviod too many requests + useDebounce( + () => { + setRanges(localRanges.map(({ id, ...rest }) => ({ ...rest }))); + }, + TYPING_DEBOUNCE_TIME, + [localRanges] + ); + + const addNewRange = () => { + setLocalRanges([ + ...localRanges, + { + id: generateId(), + from: localRanges[localRanges.length - 1].to, + to: Infinity, + label: '', + }, + ]); + }; + + return ( + + + {' '} + {i18n.translate('xpack.lens.indexPattern.ranges.customIntervalsRemoval', { + defaultMessage: 'Remove custom intervals', + })} + + + } + > + <> + setIsOpenByCreation(false)} + droppableId="RANGES_DROPPABLE_AREA" + items={localRanges} + > + {localRanges.map((range: LocalRangeType, idx: number) => ( + { + const newRanges = localRanges.filter((_, i) => i !== idx); + setLocalRanges(newRanges); + }} + removeTitle={i18n.translate('xpack.lens.indexPattern.ranges.deleteRange', { + defaultMessage: 'Delete range', + })} + isNotRemovable={localRanges.length === 1} + > + { + const newRanges = [...localRanges]; + if (newRange.id === newRanges[idx].id) { + newRanges[idx] = newRange; + } else { + newRanges.push(newRange); + } + setLocalRanges(newRanges); + }} + formatter={formatter} + Button={({ onClick }: { onClick: MouseEventHandler }) => ( + + + {getBetterLabel(range, formatter)} + + + )} + /> + + ))} + + { + addNewRange(); + setIsOpenByCreation(true); + }} + label={i18n.translate('xpack.lens.indexPattern.ranges.addInterval', { + defaultMessage: 'Add interval', + })} + /> + + + ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/constants.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/constants.ts new file mode 100644 index 0000000000000..5c3c3c19a2b0f --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/constants.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const TYPING_DEBOUNCE_TIME = 256; +// Taken from the Visualize editor +export const FROM_PLACEHOLDER = '\u2212\u221E'; +export const TO_PLACEHOLDER = '+\u221E'; + +export const DEFAULT_INTERVAL = 1000; +export const AUTO_BARS = 'auto'; +export const MIN_HISTOGRAM_BARS = 1; +export const SLICES = 6; + +export const MODES = { + Range: 'range', + Histogram: 'histogram', +} as const; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/index.ts new file mode 100644 index 0000000000000..ccae0c949af0d --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './ranges'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx new file mode 100644 index 0000000000000..5d5acf7778973 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useDebounce } from 'react-use'; +import { + EuiButtonEmpty, + EuiFormRow, + EuiRange, + EuiFlexItem, + EuiFlexGroup, + EuiButtonIcon, + EuiToolTip, +} from '@elastic/eui'; +import { IFieldFormat } from 'src/plugins/data/public'; +import { RangeColumnParams, UpdateParamsFnType, MODES_TYPES } from './ranges'; +import { AdvancedRangeEditor } from './advanced_editor'; +import { TYPING_DEBOUNCE_TIME, MODES, MIN_HISTOGRAM_BARS } from './constants'; + +const BaseRangeEditor = ({ + maxBars, + step, + maxHistogramBars, + onToggleEditor, + onMaxBarsChange, +}: { + maxBars: number; + step: number; + maxHistogramBars: number; + onToggleEditor: () => void; + onMaxBarsChange: (newMaxBars: number) => void; +}) => { + const [maxBarsValue, setMaxBarsValue] = useState(String(maxBars)); + + useDebounce( + () => { + onMaxBarsChange(Number(maxBarsValue)); + }, + TYPING_DEBOUNCE_TIME, + [maxBarsValue] + ); + + const granularityLabel = i18n.translate('xpack.lens.indexPattern.ranges.granularity', { + defaultMessage: 'Granularity', + }); + const decreaseButtonLabel = i18n.translate('xpack.lens.indexPattern.ranges.decreaseButtonLabel', { + defaultMessage: 'Decrease granularity', + }); + const increaseButtonLabel = i18n.translate('xpack.lens.indexPattern.ranges.increaseButtonLabel', { + defaultMessage: 'Increase granularity', + }); + + return ( + <> + + + + + + setMaxBarsValue('' + Math.max(Number(maxBarsValue) - step, MIN_HISTOGRAM_BARS)) + } + aria-label={decreaseButtonLabel} + /> + + + + setMaxBarsValue(currentTarget.value)} + /> + + + + + setMaxBarsValue('' + Math.min(Number(maxBarsValue) + step, maxHistogramBars)) + } + aria-label={increaseButtonLabel} + /> + + + + + + onToggleEditor()}> + {i18n.translate('xpack.lens.indexPattern.ranges.customIntervalsToggle', { + defaultMessage: 'Create custom intervals', + })} + + + ); +}; + +export const RangeEditor = ({ + setParam, + params, + maxHistogramBars, + maxBars, + granularityStep, + onChangeMode, + rangeFormatter, +}: { + params: RangeColumnParams; + maxHistogramBars: number; + maxBars: number; + granularityStep: number; + setParam: UpdateParamsFnType; + onChangeMode: (mode: MODES_TYPES) => void; + rangeFormatter: IFieldFormat; +}) => { + const [isAdvancedEditor, toggleAdvancedEditor] = useState(params.type === MODES.Range); + + // if the maxBars in the params is set to auto refresh it with the default value + // only on bootstrap + useEffect(() => { + if (params.maxBars !== maxBars) { + setParam('maxBars', maxBars); + } + }, [maxBars, params.maxBars, setParam]); + + if (isAdvancedEditor) { + return ( + { + setParam('ranges', ranges); + }} + onToggleEditor={() => { + onChangeMode(MODES.Histogram); + toggleAdvancedEditor(false); + }} + formatter={rangeFormatter} + /> + ); + } + + return ( + { + setParam('maxBars', newMaxBars); + }} + onToggleEditor={() => { + onChangeMode(MODES.Range); + toggleAdvancedEditor(true); + }} + /> + ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx new file mode 100644 index 0000000000000..2409406afcdbc --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -0,0 +1,555 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiFieldNumber, EuiRange, EuiButtonEmpty, EuiLink } from '@elastic/eui'; +import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { IndexPatternPrivateState, IndexPattern } from '../../../types'; +import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; +import { rangeOperation } from '../index'; +import { RangeIndexPatternColumn } from './ranges'; +import { + MODES, + DEFAULT_INTERVAL, + TYPING_DEBOUNCE_TIME, + MIN_HISTOGRAM_BARS, + SLICES, +} from './constants'; +import { RangePopover } from './advanced_editor'; +import { DragDropBuckets } from '../shared_components'; + +const dataPluginMockValue = dataPluginMock.createStartContract(); +// need to overwrite the formatter field first +dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(() => { + return { convert: ({ gte, lt }: { gte: string; lt: string }) => `${gte} - ${lt}` }; +}); + +type ReactMouseEvent = React.MouseEvent & + React.MouseEvent; + +const defaultOptions = { + storage: {} as IStorageWrapper, + // need this for MAX_HISTOGRAM value + uiSettings: ({ + get: () => 100, + } as unknown) as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { + fromDate: 'now-1y', + toDate: 'now', + }, + data: dataPluginMockValue, + http: {} as HttpSetup, +}; + +describe('ranges', () => { + let state: IndexPatternPrivateState; + const InlineOptions = rangeOperation.paramEditor!; + const sourceField = 'MyField'; + const MAX_HISTOGRAM_VALUE = 100; + const GRANULARITY_DEFAULT_VALUE = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / 2; + const GRANULARITY_STEP = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / SLICES; + + function setToHistogramMode() { + const column = state.layers.first.columns.col1 as RangeIndexPatternColumn; + column.dataType = 'number'; + column.scale = 'interval'; + column.params.type = MODES.Histogram; + } + + function setToRangeMode() { + const column = state.layers.first.columns.col1 as RangeIndexPatternColumn; + column.dataType = 'string'; + column.scale = 'ordinal'; + column.params.type = MODES.Range; + } + + function getDefaultState(): IndexPatternPrivateState { + return { + indexPatternRefs: [], + indexPatterns: {}, + existingFields: {}, + currentIndexPatternId: '1', + isFirstExistenceFetch: false, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + // Start with the histogram type + col1: { + label: sourceField, + dataType: 'number', + operationType: 'range', + scale: 'interval', + isBucketed: true, + sourceField, + params: { + type: MODES.Histogram, + ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }], + maxBars: 'auto', + }, + }, + col2: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + }, + }, + }; + } + + beforeAll(() => { + jest.useFakeTimers(); + }); + + beforeEach(() => { + state = getDefaultState(); + }); + + describe('toEsAggConfig', () => { + afterAll(() => setToHistogramMode()); + + it('should reflect params correctly', () => { + const esAggsConfig = rangeOperation.toEsAggsConfig( + state.layers.first.columns.col1 as RangeIndexPatternColumn, + 'col1', + {} as IndexPattern + ); + expect(esAggsConfig).toEqual( + expect.objectContaining({ + type: MODES.Histogram, + params: expect.objectContaining({ + field: sourceField, + maxBars: null, + }), + }) + ); + }); + + it('should reflect the type correctly', () => { + setToRangeMode(); + + const esAggsConfig = rangeOperation.toEsAggsConfig( + state.layers.first.columns.col1 as RangeIndexPatternColumn, + 'col1', + {} as IndexPattern + ); + + expect(esAggsConfig).toEqual( + expect.objectContaining({ + type: MODES.Range, + }) + ); + }); + }); + + describe('getPossibleOperationForField', () => { + it('should return operation with the right type for number', () => { + expect( + rangeOperation.getPossibleOperationForField({ + aggregatable: true, + searchable: true, + name: 'test', + displayName: 'test', + type: 'number', + }) + ).toEqual({ + dataType: 'number', + isBucketed: true, + scale: 'interval', + }); + }); + + it('should not return operation if field type is not number', () => { + expect( + rangeOperation.getPossibleOperationForField({ + aggregatable: false, + searchable: true, + name: 'test', + displayName: 'test', + type: 'string', + }) + ).toEqual(undefined); + }); + }); + + describe('paramEditor', () => { + describe('Modify intervals in basic mode', () => { + beforeEach(() => { + state = getDefaultState(); + }); + + it('should start update the state with the default maxBars value', () => { + const setStateSpy = jest.fn(); + mount( + + ); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE, + }, + }, + }, + }, + }, + }); + }); + + it('should update state when changing Max bars number', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + act(() => { + instance.find(EuiRange).prop('onChange')!( + { + currentTarget: { + value: '' + MAX_HISTOGRAM_VALUE, + }, + } as React.ChangeEvent, + true + ); + jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + maxBars: MAX_HISTOGRAM_VALUE, + }, + }, + }, + }, + }, + }); + }); + }); + + it('should update the state using the plus or minus buttons by the step amount', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + act(() => { + // minus button + instance + .find('[data-test-subj="lns-indexPattern-range-maxBars-minus"]') + .find('button') + .prop('onClick')!({} as ReactMouseEvent); + jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE - GRANULARITY_STEP, + }, + }, + }, + }, + }, + }); + + // plus button + instance + .find('[data-test-subj="lns-indexPattern-range-maxBars-plus"]') + .find('button') + .prop('onClick')!({} as ReactMouseEvent); + jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE, + }, + }, + }, + }, + }, + }); + }); + }); + }); + + describe('Specify range intervals manually', () => { + // @ts-expect-error + window['__react-beautiful-dnd-disable-dev-warnings'] = true; // issue with enzyme & react-beautiful-dnd throwing errors: https://github.com/atlassian/react-beautiful-dnd/issues/1593 + + beforeEach(() => setToRangeMode()); + + it('should show one range interval to start with', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + expect(instance.find(DragDropBuckets).children).toHaveLength(1); + }); + + it('should add a new range', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + // This series of act clojures are made to make it work properly the update flush + act(() => { + instance.find(EuiButtonEmpty).prop('onClick')!({} as ReactMouseEvent); + }); + + act(() => { + // need another wrapping for this in order to work + instance.update(); + + expect(instance.find(RangePopover)).toHaveLength(2); + + // edit the range and check + instance.find(RangePopover).find(EuiFieldNumber).first().prop('onChange')!({ + target: { + value: '50', + }, + } as React.ChangeEvent); + jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + ranges: [ + { from: 0, to: DEFAULT_INTERVAL, label: '' }, + { from: 50, to: Infinity, label: '' }, + ], + }, + }, + }, + }, + }, + }); + }); + }); + + it('should open a popover to edit an existing range', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + // This series of act clojures are made to make it work properly the update flush + act(() => { + instance.find(RangePopover).find(EuiLink).prop('onClick')!({} as ReactMouseEvent); + }); + + act(() => { + // need another wrapping for this in order to work + instance.update(); + + // edit the range "to" field + instance.find(RangePopover).find(EuiFieldNumber).last().prop('onChange')!({ + target: { + value: '50', + }, + } as React.ChangeEvent); + jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); + + expect(setStateSpy).toHaveBeenCalledWith({ + ...state, + layers: { + first: { + ...state.layers.first, + columns: { + ...state.layers.first.columns, + col1: { + ...state.layers.first.columns.col1, + params: { + ...state.layers.first.columns.col1.params, + ranges: [{ from: 0, to: 50, label: '' }], + }, + }, + }, + }, + }, + }); + }); + }); + + it('should not accept invalid ranges', () => { + const setStateSpy = jest.fn(); + + const instance = mount( + + ); + + // This series of act clojures are made to make it work properly the update flush + act(() => { + instance.find(RangePopover).find(EuiLink).prop('onClick')!({} as ReactMouseEvent); + }); + + act(() => { + // need another wrapping for this in order to work + instance.update(); + + // edit the range "to" field + instance.find(RangePopover).find(EuiFieldNumber).last().prop('onChange')!({ + target: { + value: '-1', + }, + } as React.ChangeEvent); + }); + + act(() => { + instance.update(); + + // and check + expect(instance.find(RangePopover).find(EuiFieldNumber).last().prop('isInvalid')).toBe( + true + ); + }); + }); + + it('should be possible to remove a range if multiple', () => { + const setStateSpy = jest.fn(); + + // Add an extra range + (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ + from: DEFAULT_INTERVAL, + to: 2 * DEFAULT_INTERVAL, + label: '', + }); + + const instance = mount( + + ); + + expect(instance.find(RangePopover)).toHaveLength(2); + + // This series of act closures are made to make it work properly the update flush + act(() => { + instance + .find('[data-test-subj="lns-customBucketContainer-remove"]') + .last() + .prop('onClick')!({} as ReactMouseEvent); + }); + + act(() => { + // need another wrapping for this in order to work + instance.update(); + + expect(instance.find(RangePopover)).toHaveLength(1); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx new file mode 100644 index 0000000000000..1971fb2875bed --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -0,0 +1,200 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common'; +import { Range } from '../../../../../../../../src/plugins/expressions/common/expression_types/index'; +import { RangeEditor } from './range_editor'; +import { OperationDefinition } from '../index'; +import { FieldBasedIndexPatternColumn } from '../column_types'; +import { updateColumnParam, changeColumn } from '../../../state_helpers'; +import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; + +type RangeType = Omit; +export type RangeTypeLens = RangeType & { label: string }; + +export type MODES_TYPES = typeof MODES[keyof typeof MODES]; + +export interface RangeIndexPatternColumn extends FieldBasedIndexPatternColumn { + operationType: 'range'; + params: { + type: MODES_TYPES; + maxBars: typeof AUTO_BARS | number; + ranges: RangeTypeLens[]; + }; +} + +export type RangeColumnParams = RangeIndexPatternColumn['params']; +export type UpdateParamsFnType = ( + paramName: K, + value: RangeColumnParams[K] +) => void; + +export const isValidNumber = (value: number | '') => + value !== '' && !isNaN(value) && isFinite(value); +export const isRangeWithin = (range: RangeTypeLens): boolean => range.from <= range.to; +const isFullRange = ({ from, to }: RangeType) => isValidNumber(from) && isValidNumber(to); +export const isValidRange = (range: RangeTypeLens): boolean => { + if (isFullRange(range)) { + return isRangeWithin(range); + } + return true; +}; + +function getEsAggsParams({ sourceField, params }: RangeIndexPatternColumn) { + if (params.type === MODES.Range) { + return { + field: sourceField, + ranges: params.ranges.filter(isValidRange).map>((range) => { + if (isFullRange(range)) { + return { from: range.from, to: range.to }; + } + const partialRange: Partial = {}; + // be careful with the fields to set on partial ranges + if (isValidNumber(range.from)) { + partialRange.from = range.from; + } + if (isValidNumber(range.to)) { + partialRange.to = range.to; + } + return partialRange; + }), + }; + } + return { + field: sourceField, + // fallback to 0 in case of empty string + maxBars: params.maxBars === AUTO_BARS ? null : params.maxBars, + has_extended_bounds: false, + min_doc_count: 0, + extended_bounds: { min: '', max: '' }, + }; +} + +export const rangeOperation: OperationDefinition = { + type: 'range', + displayName: i18n.translate('xpack.lens.indexPattern.ranges', { + defaultMessage: 'Ranges', + }), + priority: 4, // Higher than terms, so numbers get histogram + input: 'field', + getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { + if ( + type === 'number' && + aggregatable && + (!aggregationRestrictions || aggregationRestrictions.range) + ) { + return { + dataType: 'number', + isBucketed: true, + scale: 'interval', + }; + } + }, + buildColumn({ suggestedPriority, field }) { + return { + label: field.name, + dataType: 'number', // string for Range + operationType: 'range', + suggestedPriority, + sourceField: field.name, + isBucketed: true, + scale: 'interval', // ordinal for Range + params: { + type: MODES.Histogram, + ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }], + maxBars: AUTO_BARS, + }, + }; + }, + isTransferable: (column, newIndexPattern) => { + const newField = newIndexPattern.fields.find((field) => field.name === column.sourceField); + + return Boolean( + newField && + newField.type === 'number' && + newField.aggregatable && + (!newField.aggregationRestrictions || newField.aggregationRestrictions.range) + ); + }, + onFieldChange: (oldColumn, indexPattern, field) => { + return { + ...oldColumn, + label: field.name, + sourceField: field.name, + }; + }, + toEsAggsConfig: (column, columnId) => { + const params = getEsAggsParams(column); + return { + id: columnId, + enabled: true, + type: column.params.type, + schema: 'segment', + params, + }; + }, + paramEditor: ({ state, setState, currentColumn, layerId, columnId, uiSettings, data }) => { + const rangeFormatter = data.fieldFormats.deserialize({ id: 'range' }); + const MAX_HISTOGRAM_BARS = uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS); + const granularityStep = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / SLICES; + const maxBarsDefaultValue = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / 2; + + // Used to change one param at the time + const setParam: UpdateParamsFnType = (paramName, value) => { + setState( + updateColumnParam({ + state, + layerId, + currentColumn, + paramName, + value, + }) + ); + }; + + // Useful to change more params at once + const onChangeMode = (newMode: MODES_TYPES) => { + const scale = newMode === MODES.Range ? 'ordinal' : 'interval'; + const dataType = newMode === MODES.Range ? 'string' : 'number'; + setState( + changeColumn({ + state, + layerId, + columnId, + newColumn: { + ...currentColumn, + scale, + dataType, + params: { + type: newMode, + ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }], + maxBars: maxBarsDefaultValue, + }, + }, + keepParams: false, + }) + ); + }; + return ( + + ); + }, +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx index 73378cea919a6..47380f7865578 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx @@ -35,6 +35,7 @@ interface BucketContainerProps { invalidMessage: string; onRemoveClick: () => void; removeTitle: string; + isNotRemovable?: boolean; children: React.ReactNode; dataTestSubj?: string; } @@ -46,6 +47,7 @@ const BucketContainer = ({ removeTitle, children, dataTestSubj, + isNotRemovable, }: BucketContainerProps) => { return ( @@ -75,6 +77,7 @@ const BucketContainer = ({ onClick={onRemoveClick} aria-label={removeTitle} title={removeTitle} + disabled={isNotRemovable} /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx index 20c421008a746..c147029bbd3c7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiForm, EuiFormRow, EuiRange, EuiSelect } from '@elastic/eui'; +import { EuiFormRow, EuiRange, EuiSelect } from '@elastic/eui'; import { IndexPatternColumn } from '../../indexpattern'; import { updateColumnParam } from '../../state_helpers'; import { DataType } from '../../../types'; @@ -48,12 +48,13 @@ export interface TermsIndexPatternColumn extends FieldBasedIndexPatternColumn { }; } -export const termsOperation: OperationDefinition = { +export const termsOperation: OperationDefinition = { type: 'terms', displayName: i18n.translate('xpack.lens.indexPattern.terms', { defaultMessage: 'Top values', }), priority: 3, // Higher than any metric + input: 'field', getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( supportedTypes.has(type) && @@ -95,23 +96,25 @@ export const termsOperation: OperationDefinition = { }, }; }, - toEsAggsConfig: (column, columnId, _indexPattern) => ({ - id: columnId, - enabled: true, - type: 'terms', - schema: 'segment', - params: { - field: column.sourceField, - orderBy: - column.params.orderBy.type === 'alphabetical' ? '_key' : column.params.orderBy.columnId, - order: column.params.orderDirection, - size: column.params.size, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, - }), + toEsAggsConfig: (column, columnId, _indexPattern) => { + return { + id: columnId, + enabled: true, + type: 'terms', + schema: 'segment', + params: { + field: column.sourceField, + orderBy: + column.params.orderBy.type === 'alphabetical' ? '_key' : column.params.orderBy.columnId, + order: column.params.orderDirection, + size: column.params.size, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + }, + }; + }, onFieldChange: (oldColumn, indexPattern, field) => { return { ...oldColumn, @@ -171,7 +174,7 @@ export const termsOperation: OperationDefinition = { }), }); return ( - + <> = { })} /> - + ); }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts index 1e2bc5dcb6b62..31a36c59274da 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts @@ -5,4 +5,4 @@ */ export * from './operations'; -export { OperationType, IndexPatternColumn } from './definitions'; +export { OperationType, IndexPatternColumn, FieldBasedIndexPatternColumn } from './definitions'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 4ac3fc89500f9..c1bd4b84099b7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -182,7 +182,7 @@ describe('getOperationTypesForField', () => { }, }; - it('should build a column for the given operation type if it is passed in', () => { + it('should build a column for the given field-based operation type if it is passed in', () => { const column = buildColumn({ layerId: 'first', indexPattern: expectedIndexPatterns[1], @@ -194,6 +194,17 @@ describe('getOperationTypesForField', () => { expect(column.operationType).toEqual('count'); }); + it('should build a column for the given no-input operation type if it is passed in', () => { + const column = buildColumn({ + layerId: 'first', + indexPattern: expectedIndexPatterns[1], + columns: state.layers.first.columns, + suggestedPriority: 0, + op: 'filters', + }); + expect(column.operationType).toEqual('filters'); + }); + it('should build a column for the given operation type and field if it is passed in', () => { const field = expectedIndexPatterns[1].fields[1]; const column = buildColumn({ @@ -222,19 +233,33 @@ describe('getOperationTypesForField', () => { ); }); - it('should list out all field-operation tuples for different operation meta data', () => { + it('should list out all operation tuples', () => { expect(getAvailableOperationsByMetadata(expectedIndexPatterns[1])).toMatchInlineSnapshot(` Array [ + Object { + "operationMetaData": Object { + "dataType": "date", + "isBucketed": true, + "scale": "interval", + }, + "operations": Array [ + Object { + "field": "timestamp", + "operationType": "date_histogram", + "type": "field", + }, + ], + }, Object { "operationMetaData": Object { "dataType": "number", "isBucketed": true, - "scale": "ordinal", + "scale": "interval", }, "operations": Array [ Object { "field": "bytes", - "operationType": "terms", + "operationType": "range", "type": "field", }, ], @@ -246,6 +271,10 @@ describe('getOperationTypesForField', () => { "scale": "ordinal", }, "operations": Array [ + Object { + "operationType": "filters", + "type": "none", + }, Object { "field": "source", "operationType": "terms", @@ -255,14 +284,14 @@ describe('getOperationTypesForField', () => { }, Object { "operationMetaData": Object { - "dataType": "date", + "dataType": "number", "isBucketed": true, - "scale": "interval", + "scale": "ordinal", }, "operations": Array [ Object { - "field": "timestamp", - "operationType": "date_histogram", + "field": "bytes", + "operationType": "terms", "type": "field", }, ], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts index 9e5a0f496357d..46dd73ba849a2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts @@ -63,7 +63,7 @@ export function getOperationTypesForField(field: IndexPatternField): OperationTy return operationDefinitions .filter( (operationDefinition) => - 'getPossibleOperationForField' in operationDefinition && + operationDefinition.input === 'field' && operationDefinition.getPossibleOperationForField(field) ) .sort(getSortScoreByPriority) @@ -80,11 +80,16 @@ export function isDocumentOperation(type: string) { return documentOperations.has(type); } -interface OperationFieldTuple { - type: 'field'; - operationType: OperationType; - field: string; -} +type OperationFieldTuple = + | { + type: 'field'; + operationType: OperationType; + field: string; + } + | { + type: 'none'; + operationType: OperationType; + }; /** * Returns all possible operations (matches between operations and fields of the index @@ -100,11 +105,18 @@ interface OperationFieldTuple { * [ * { * operationMetaData: { dataType: 'string', isBucketed: true }, - * operations: ['terms'] + * operations: [{ + * type: 'field', + * operationType: ['terms'], + * field: 'keyword' + * }] * }, * { - * operationMetaData: { dataType: 'number', isBucketed: false }, - * operations: ['avg', 'min', 'max'] + * operationMetaData: { dataType: 'string', isBucketed: true }, + * operations: [{ + * type: 'none', + * operationType: ['filters'], + * }] * }, * ] * ``` @@ -133,30 +145,31 @@ export function getAvailableOperationsByMetadata(indexPattern: IndexPattern) { }; operationDefinitions.sort(getSortScoreByPriority).forEach((operationDefinition) => { - indexPattern.fields.forEach((field) => { + if (operationDefinition.input === 'field') { + indexPattern.fields.forEach((field) => { + addToMap( + { + type: 'field', + operationType: operationDefinition.type, + field: field.name, + }, + operationDefinition.getPossibleOperationForField(field) + ); + }); + } else if (operationDefinition.input === 'none') { addToMap( { - type: 'field', + type: 'none', operationType: operationDefinition.type, - field: field.name, }, - getPossibleOperationForField(operationDefinition, field) + operationDefinition.getPossibleOperation() ); - }); + } }); return Object.values(operationByMetadata); } -function getPossibleOperationForField( - operationDefinition: GenericOperationDefinition, - field: IndexPatternField -): OperationMetadata | undefined { - return 'getPossibleOperationForField' in operationDefinition - ? operationDefinition.getPossibleOperationForField(field) - : undefined; -} - /** * Changes the field of the passed in colum. To do so, this method uses the `onFieldChange` function of * the operation definition of the column. Returns a new column object with the field changed. @@ -171,13 +184,13 @@ export function changeField( ) { const operationDefinition = operationDefinitionMap[column.operationType]; - if (!('onFieldChange' in operationDefinition)) { + if (operationDefinition.input === 'field' && 'sourceField' in column) { + return operationDefinition.onFieldChange(column, indexPattern, newField); + } else { throw new Error( "Invariant error: Cannot change field if operation isn't a field based operaiton" ); } - - return operationDefinition.onFieldChange(column, indexPattern, newField); } /** @@ -203,7 +216,7 @@ export function buildColumn({ suggestedPriority: DimensionPriority | undefined; layerId: string; indexPattern: IndexPattern; - field: IndexPatternField; + field?: IndexPatternField; previousColumn?: IndexPatternColumn; }): IndexPatternColumn { const operationDefinition = operationDefinitionMap[op]; @@ -220,16 +233,18 @@ export function buildColumn({ previousColumn, }; + if (operationDefinition.input === 'none') { + return operationDefinition.buildColumn(baseOptions); + } + if (!field) { throw new Error(`Invariant error: ${operationDefinition.type} operation requires field`); } - const newColumn = operationDefinition.buildColumn({ + return operationDefinition.buildColumn({ ...baseOptions, field, }); - - return newColumn; } export { operationDefinitionMap } from './definitions'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.ts index 51691ae18a99a..c977a7e0fa370 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/state_helpers.ts @@ -5,8 +5,7 @@ */ import _ from 'lodash'; -import { isColumnTransferable } from './operations'; -import { operationDefinitionMap, IndexPatternColumn } from './operations'; +import { isColumnTransferable, operationDefinitionMap, IndexPatternColumn } from './operations'; import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types'; export function updateColumnParam({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index b691c5b5c4c40..a3c0e8aed7421 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -26,6 +26,7 @@ export interface IndexPattern { export type IndexPatternField = IFieldType & { displayName: string; aggregationRestrictions?: Partial; + meta?: boolean; }; export interface IndexPatternLayer { diff --git a/x-pack/plugins/lens/public/lens_attribute_service.ts b/x-pack/plugins/lens/public/lens_attribute_service.ts new file mode 100644 index 0000000000000..3c43fd98cceb4 --- /dev/null +++ b/x-pack/plugins/lens/public/lens_attribute_service.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from '../../../../src/core/public'; +import { LensPluginStartDependencies } from './plugin'; +import { AttributeService } from '../../../../src/plugins/dashboard/public'; +import { + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput, +} from './editor_frame_service/embeddable/embeddable'; +import { SavedObjectIndexStore, DOC_TYPE } from './persistence'; + +export type LensAttributeService = AttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput +>; + +export function getLensAttributeService( + core: CoreStart, + startDependencies: LensPluginStartDependencies +): LensAttributeService { + const savedObjectStore = new SavedObjectIndexStore(core.savedObjects.client); + return startDependencies.dashboard.getAttributeService< + LensSavedObjectAttributes, + LensByValueInput, + LensByReferenceInput + >(DOC_TYPE, { + customSaveMethod: async ( + type: string, + attributes: LensSavedObjectAttributes, + savedObjectId?: string + ) => { + const savedDoc = await savedObjectStore.save({ + ...attributes, + savedObjectId, + type: DOC_TYPE, + }); + return { id: savedDoc.savedObjectId }; + }, + customUnwrapMethod: (savedObject) => { + return { + ...savedObject.attributes, + references: savedObject.references, + }; + }, + }); +} diff --git a/x-pack/plugins/lens/public/lens_ui_telemetry/factory.test.ts b/x-pack/plugins/lens/public/lens_ui_telemetry/factory.test.ts index 8bb1e086a37c2..fa7747dd18e42 100644 --- a/x-pack/plugins/lens/public/lens_ui_telemetry/factory.test.ts +++ b/x-pack/plugins/lens/public/lens_ui_telemetry/factory.test.ts @@ -83,7 +83,7 @@ describe('Lens UI telemetry', () => { jest.runOnlyPendingTimers(); - expect(http.post).toHaveBeenCalledWith(`/api/lens/telemetry`, { + expect(http.post).toHaveBeenCalledWith(`/api/lens/stats`, { body: JSON.stringify({ events: { '2019-10-23': { diff --git a/x-pack/plugins/lens/public/lens_ui_telemetry/factory.ts b/x-pack/plugins/lens/public/lens_ui_telemetry/factory.ts index cb517acff4f7a..8f9ce7f2ceab8 100644 --- a/x-pack/plugins/lens/public/lens_ui_telemetry/factory.ts +++ b/x-pack/plugins/lens/public/lens_ui_telemetry/factory.ts @@ -86,7 +86,7 @@ export class LensReportManager { this.readFromStorage(); if (Object.keys(this.events).length || Object.keys(this.suggestionEvents).length) { try { - await this.http.post(`${BASE_API_URL}/telemetry`, { + await this.http.post(`${BASE_API_URL}/stats`, { body: JSON.stringify({ events: this.events, suggestionEvents: this.suggestionEvents, diff --git a/x-pack/plugins/lens/public/metric_visualization/index.scss b/x-pack/plugins/lens/public/metric_visualization/expression.scss similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/index.scss rename to x-pack/plugins/lens/public/metric_visualization/expression.scss diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx new file mode 100644 index 0000000000000..0c92cdb2c31fc --- /dev/null +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { metricChart, MetricChart } from './expression'; +import { LensMultiTable } from '../types'; +import React from 'react'; +import { shallow } from 'enzyme'; +import { MetricConfig } from './types'; +import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; +import { IFieldFormat } from '../../../../../src/plugins/data/public'; + +function sampleArgs() { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + l1: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + ], + rows: [{ a: 10110, b: 2, c: 3 }], + }, + }, + }; + + const args: MetricConfig = { + accessor: 'a', + layerId: 'l1', + title: 'My fanci metric chart', + mode: 'full', + }; + + return { data, args }; +} + +describe('metric_expression', () => { + describe('metricChart', () => { + test('it renders with the specified data and args', () => { + const { data, args } = sampleArgs(); + const result = metricChart.fn(data, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'render', + as: 'lens_metric_chart_renderer', + value: { data, args }, + }); + }); + }); + + describe('MetricChart component', () => { + test('it renders the title and value', () => { + const { data, args } = sampleArgs(); + + expect( + shallow( x as IFieldFormat} />) + ).toMatchInlineSnapshot(` + + +
+ 10110 +
+
+ My fanci metric chart +
+
+
+ `); + }); + + test('it does not render title in reduced mode', () => { + const { data, args } = sampleArgs(); + + expect( + shallow( + x as IFieldFormat} + /> + ) + ).toMatchInlineSnapshot(` + + +
+ 10110 +
+
+
+ `); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx new file mode 100644 index 0000000000000..7eeef13240f72 --- /dev/null +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 './expression.scss'; + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { + ExpressionFunctionDefinition, + ExpressionRenderDefinition, + IInterpreterRenderHandlers, +} from '../../../../../src/plugins/expressions/public'; +import { MetricConfig } from './types'; +import { FormatFactory, LensMultiTable } from '../types'; +import { AutoScale } from './auto_scale'; +import { VisualizationContainer } from '../visualization_container'; + +export interface MetricChartProps { + data: LensMultiTable; + args: MetricConfig; +} + +export interface MetricRender { + type: 'render'; + as: 'lens_metric_chart_renderer'; + value: MetricChartProps; +} + +export const metricChart: ExpressionFunctionDefinition< + 'lens_metric_chart', + LensMultiTable, + Omit, + MetricRender +> = { + name: 'lens_metric_chart', + type: 'render', + help: 'A metric chart', + args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + accessor: { + types: ['string'], + help: 'The column whose value is being displayed', + }, + mode: { + types: ['string'], + options: ['reduced', 'full'], + default: 'full', + help: + 'The display mode of the chart - reduced will only show the metric itself without min size', + }, + }, + inputTypes: ['lens_multitable'], + fn(data, args) { + return { + type: 'render', + as: 'lens_metric_chart_renderer', + value: { + data, + args, + }, + } as MetricRender; + }, +}; + +export const getMetricChartRenderer = ( + formatFactory: Promise +): ExpressionRenderDefinition => ({ + name: 'lens_metric_chart_renderer', + displayName: 'Metric chart', + help: 'Metric chart renderer', + validate: () => undefined, + reuseDomNode: true, + render: async ( + domNode: Element, + config: MetricChartProps, + handlers: IInterpreterRenderHandlers + ) => { + const resolvedFormatFactory = await formatFactory; + ReactDOM.render( + , + domNode, + () => { + handlers.done(); + } + ); + handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + }, +}); + +export function MetricChart({ + data, + args, + formatFactory, +}: MetricChartProps & { formatFactory: FormatFactory }) { + const { title, accessor, mode } = args; + let value = '-'; + const firstTable = Object.values(data.tables)[0]; + if (!accessor) { + return ( + + ); + } + + if (firstTable) { + const column = firstTable.columns[0]; + const row = firstTable.rows[0]; + if (row[accessor]) { + value = + column && column.formatHint + ? formatFactory(column.formatHint).convert(row[accessor]) + : Number(Number(row[accessor]).toFixed(3)).toString(); + } + } + + return ( + + +
+ {value} +
+ {mode === 'full' && ( +
+ {title} +
+ )} +
+
+ ); +} diff --git a/x-pack/plugins/lens/public/metric_visualization/index.ts b/x-pack/plugins/lens/public/metric_visualization/index.ts index 2960da52191e4..f6245669b9964 100644 --- a/x-pack/plugins/lens/public/metric_visualization/index.ts +++ b/x-pack/plugins/lens/public/metric_visualization/index.ts @@ -5,9 +5,7 @@ */ import { CoreSetup } from 'kibana/public'; -import { metricVisualization } from './metric_visualization'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; -import { metricChart, getMetricChartRenderer } from './metric_expression'; import { EditorFrameSetup, FormatFactory } from '../types'; export interface MetricVisualizationPluginSetupPlugins { @@ -23,10 +21,15 @@ export class MetricVisualization { _core: CoreSetup | null, { expressions, formatFactory, editorFrame }: MetricVisualizationPluginSetupPlugins ) { - expressions.registerFunction(() => metricChart); + editorFrame.registerVisualization(async () => { + const { metricVisualization, metricChart, getMetricChartRenderer } = await import( + '../async_services' + ); - expressions.registerRenderer(() => getMetricChartRenderer(formatFactory)); + expressions.registerFunction(() => metricChart); - editorFrame.registerVisualization(metricVisualization); + expressions.registerRenderer(() => getMetricChartRenderer(formatFactory)); + return metricVisualization; + }); } } diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_expression.test.tsx deleted file mode 100644 index 27f971c2ba11a..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/metric_expression.test.tsx +++ /dev/null @@ -1,126 +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 { metricChart, MetricChart } from './metric_expression'; -import { LensMultiTable } from '../types'; -import React from 'react'; -import { shallow } from 'enzyme'; -import { MetricConfig } from './types'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { IFieldFormat } from '../../../../../src/plugins/data/public'; - -function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - l1: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - ], - rows: [{ a: 10110, b: 2, c: 3 }], - }, - }, - }; - - const args: MetricConfig = { - accessor: 'a', - layerId: 'l1', - title: 'My fanci metric chart', - mode: 'full', - }; - - return { data, args }; -} - -describe('metric_expression', () => { - describe('metricChart', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = metricChart.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'lens_metric_chart_renderer', - value: { data, args }, - }); - }); - }); - - describe('MetricChart component', () => { - test('it renders the title and value', () => { - const { data, args } = sampleArgs(); - - expect( - shallow( x as IFieldFormat} />) - ).toMatchInlineSnapshot(` - - -
- 10110 -
-
- My fanci metric chart -
-
-
- `); - }); - - test('it does not render title in reduced mode', () => { - const { data, args } = sampleArgs(); - - expect( - shallow( - x as IFieldFormat} - /> - ) - ).toMatchInlineSnapshot(` - - -
- 10110 -
-
-
- `); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_expression.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_expression.tsx deleted file mode 100644 index 3484837f65b43..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/metric_expression.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import { - ExpressionFunctionDefinition, - ExpressionRenderDefinition, - IInterpreterRenderHandlers, -} from '../../../../../src/plugins/expressions/public'; -import { MetricConfig } from './types'; -import { FormatFactory, LensMultiTable } from '../types'; -import { AutoScale } from './auto_scale'; -import { VisualizationContainer } from '../visualization_container'; - -export interface MetricChartProps { - data: LensMultiTable; - args: MetricConfig; -} - -export interface MetricRender { - type: 'render'; - as: 'lens_metric_chart_renderer'; - value: MetricChartProps; -} - -export const metricChart: ExpressionFunctionDefinition< - 'lens_metric_chart', - LensMultiTable, - Omit, - MetricRender -> = { - name: 'lens_metric_chart', - type: 'render', - help: 'A metric chart', - args: { - title: { - types: ['string'], - help: 'The chart title.', - }, - accessor: { - types: ['string'], - help: 'The column whose value is being displayed', - }, - mode: { - types: ['string'], - options: ['reduced', 'full'], - default: 'full', - help: - 'The display mode of the chart - reduced will only show the metric itself without min size', - }, - }, - inputTypes: ['lens_multitable'], - fn(data, args) { - return { - type: 'render', - as: 'lens_metric_chart_renderer', - value: { - data, - args, - }, - } as MetricRender; - }, -}; - -export const getMetricChartRenderer = ( - formatFactory: Promise -): ExpressionRenderDefinition => ({ - name: 'lens_metric_chart_renderer', - displayName: 'Metric chart', - help: 'Metric chart renderer', - validate: () => undefined, - reuseDomNode: true, - render: async ( - domNode: Element, - config: MetricChartProps, - handlers: IInterpreterRenderHandlers - ) => { - const resolvedFormatFactory = await formatFactory; - ReactDOM.render( - , - domNode, - () => { - handlers.done(); - } - ); - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); - -export function MetricChart({ - data, - args, - formatFactory, -}: MetricChartProps & { formatFactory: FormatFactory }) { - const { title, accessor, mode } = args; - let value = '-'; - const firstTable = Object.values(data.tables)[0]; - if (!accessor) { - return ( - - ); - } - - if (firstTable) { - const column = firstTable.columns[0]; - const row = firstTable.rows[0]; - if (row[accessor]) { - value = - column && column.formatHint - ? formatFactory(column.formatHint).convert(row[accessor]) - : Number(Number(row[accessor]).toFixed(3)).toString(); - } - } - - return ( - - -
- {value} -
- {mode === 'full' && ( -
- {title} -
- )} -
-
- ); -} diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.test.ts deleted file mode 100644 index f3c9a725ee2e2..0000000000000 --- a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.test.ts +++ /dev/null @@ -1,190 +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 { metricVisualization } from './metric_visualization'; -import { State } from './types'; -import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; -import { generateId } from '../id_generator'; -import { DatasourcePublicAPI, FramePublicAPI } from '../types'; - -jest.mock('../id_generator'); - -function exampleState(): State { - return { - accessor: 'a', - layerId: 'l1', - }; -} - -function mockFrame(): FramePublicAPI { - return { - ...createMockFramePublicAPI(), - addNewLayer: () => 'l42', - datasourceLayers: { - l1: createMockDatasource('l1').publicAPIMock, - l42: createMockDatasource('l42').publicAPIMock, - }, - }; -} - -describe('metric_visualization', () => { - describe('#initialize', () => { - it('loads default state', () => { - (generateId as jest.Mock).mockReturnValueOnce('test-id1'); - const initialState = metricVisualization.initialize(mockFrame()); - - expect(initialState.accessor).not.toBeDefined(); - expect(initialState).toMatchInlineSnapshot(` - Object { - "accessor": undefined, - "layerId": "l42", - } - `); - }); - - it('loads from persisted state', () => { - expect(metricVisualization.initialize(mockFrame(), exampleState())).toEqual(exampleState()); - }); - }); - - describe('#getLayerIds', () => { - it('returns the layer id', () => { - expect(metricVisualization.getLayerIds(exampleState())).toEqual(['l1']); - }); - }); - - describe('#clearLayer', () => { - it('returns a clean layer', () => { - (generateId as jest.Mock).mockReturnValueOnce('test-id1'); - expect(metricVisualization.clearLayer(exampleState(), 'l1')).toEqual({ - accessor: undefined, - layerId: 'l1', - }); - }); - }); - - describe('#getConfiguration', () => { - it('can add a metric when there is no accessor', () => { - expect( - metricVisualization.getConfiguration({ - state: { - accessor: undefined, - layerId: 'l1', - }, - layerId: 'l1', - frame: mockFrame(), - }) - ).toEqual({ - groups: [ - expect.objectContaining({ - supportsMoreColumns: true, - }), - ], - }); - }); - - it('is not allowed to add a metric once one accessor is set', () => { - expect( - metricVisualization.getConfiguration({ - state: { - accessor: 'a', - layerId: 'l1', - }, - layerId: 'l1', - frame: mockFrame(), - }) - ).toEqual({ - groups: [ - expect.objectContaining({ - supportsMoreColumns: false, - }), - ], - }); - }); - }); - - describe('#setDimension', () => { - it('sets the accessor', () => { - expect( - metricVisualization.setDimension({ - prevState: { - accessor: undefined, - layerId: 'l1', - }, - layerId: 'l1', - groupId: '', - columnId: 'newDimension', - }) - ).toEqual({ - accessor: 'newDimension', - layerId: 'l1', - }); - }); - }); - - describe('#removeDimension', () => { - it('removes the accessor', () => { - expect( - metricVisualization.removeDimension({ - prevState: { - accessor: 'a', - layerId: 'l1', - }, - layerId: 'l1', - columnId: 'a', - }) - ).toEqual({ - accessor: undefined, - layerId: 'l1', - }); - }); - }); - - describe('#toExpression', () => { - it('should map to a valid AST', () => { - const datasource: DatasourcePublicAPI = { - ...createMockDatasource('l1').publicAPIMock, - getOperationForColumnId(_: string) { - return { - id: 'a', - dataType: 'number', - isBucketed: false, - label: 'shazm', - }; - }, - }; - - const frame = { - ...mockFrame(), - datasourceLayers: { l1: datasource }, - }; - - expect(metricVisualization.toExpression(exampleState(), frame.datasourceLayers)) - .toMatchInlineSnapshot(` - Object { - "chain": Array [ - Object { - "arguments": Object { - "accessor": Array [ - "a", - ], - "mode": Array [ - "full", - ], - "title": Array [ - "shazm", - ], - }, - "function": "lens_metric_chart", - "type": "function", - }, - ], - "type": "expression", - } - `); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.ts new file mode 100644 index 0000000000000..c6fe54a82e2d1 --- /dev/null +++ b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.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 * from './expression'; +export * from './visualization'; diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts new file mode 100644 index 0000000000000..aa3de93013e66 --- /dev/null +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -0,0 +1,190 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { metricVisualization } from './visualization'; +import { State } from './types'; +import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; +import { generateId } from '../id_generator'; +import { DatasourcePublicAPI, FramePublicAPI } from '../types'; + +jest.mock('../id_generator'); + +function exampleState(): State { + return { + accessor: 'a', + layerId: 'l1', + }; +} + +function mockFrame(): FramePublicAPI { + return { + ...createMockFramePublicAPI(), + addNewLayer: () => 'l42', + datasourceLayers: { + l1: createMockDatasource('l1').publicAPIMock, + l42: createMockDatasource('l42').publicAPIMock, + }, + }; +} + +describe('metric_visualization', () => { + describe('#initialize', () => { + it('loads default state', () => { + (generateId as jest.Mock).mockReturnValueOnce('test-id1'); + const initialState = metricVisualization.initialize(mockFrame()); + + expect(initialState.accessor).not.toBeDefined(); + expect(initialState).toMatchInlineSnapshot(` + Object { + "accessor": undefined, + "layerId": "l42", + } + `); + }); + + it('loads from persisted state', () => { + expect(metricVisualization.initialize(mockFrame(), exampleState())).toEqual(exampleState()); + }); + }); + + describe('#getLayerIds', () => { + it('returns the layer id', () => { + expect(metricVisualization.getLayerIds(exampleState())).toEqual(['l1']); + }); + }); + + describe('#clearLayer', () => { + it('returns a clean layer', () => { + (generateId as jest.Mock).mockReturnValueOnce('test-id1'); + expect(metricVisualization.clearLayer(exampleState(), 'l1')).toEqual({ + accessor: undefined, + layerId: 'l1', + }); + }); + }); + + describe('#getConfiguration', () => { + it('can add a metric when there is no accessor', () => { + expect( + metricVisualization.getConfiguration({ + state: { + accessor: undefined, + layerId: 'l1', + }, + layerId: 'l1', + frame: mockFrame(), + }) + ).toEqual({ + groups: [ + expect.objectContaining({ + supportsMoreColumns: true, + }), + ], + }); + }); + + it('is not allowed to add a metric once one accessor is set', () => { + expect( + metricVisualization.getConfiguration({ + state: { + accessor: 'a', + layerId: 'l1', + }, + layerId: 'l1', + frame: mockFrame(), + }) + ).toEqual({ + groups: [ + expect.objectContaining({ + supportsMoreColumns: false, + }), + ], + }); + }); + }); + + describe('#setDimension', () => { + it('sets the accessor', () => { + expect( + metricVisualization.setDimension({ + prevState: { + accessor: undefined, + layerId: 'l1', + }, + layerId: 'l1', + groupId: '', + columnId: 'newDimension', + }) + ).toEqual({ + accessor: 'newDimension', + layerId: 'l1', + }); + }); + }); + + describe('#removeDimension', () => { + it('removes the accessor', () => { + expect( + metricVisualization.removeDimension({ + prevState: { + accessor: 'a', + layerId: 'l1', + }, + layerId: 'l1', + columnId: 'a', + }) + ).toEqual({ + accessor: undefined, + layerId: 'l1', + }); + }); + }); + + describe('#toExpression', () => { + it('should map to a valid AST', () => { + const datasource: DatasourcePublicAPI = { + ...createMockDatasource('l1').publicAPIMock, + getOperationForColumnId(_: string) { + return { + id: 'a', + dataType: 'number', + isBucketed: false, + label: 'shazm', + }; + }, + }; + + const frame = { + ...mockFrame(), + datasourceLayers: { l1: datasource }, + }; + + expect(metricVisualization.toExpression(exampleState(), frame.datasourceLayers)) + .toMatchInlineSnapshot(` + Object { + "chain": Array [ + Object { + "arguments": Object { + "accessor": Array [ + "a", + ], + "mode": Array [ + "full", + ], + "title": Array [ + "shazm", + ], + }, + "function": "lens_metric_chart", + "type": "function", + }, + ], + "type": "expression", + } + `); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx similarity index 100% rename from x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx rename to x-pack/plugins/lens/public/metric_visualization/visualization.tsx diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts index ba7c0ee6ae786..6b6f81aeefed0 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.test.ts @@ -42,7 +42,7 @@ describe('LensStore', () => { }); expect(doc).toEqual({ - id: 'FOO', + savedObjectId: 'FOO', title: 'Hello', description: 'My doc', visualizationType: 'bar', @@ -82,7 +82,7 @@ describe('LensStore', () => { test('updates and returns a visualization document', async () => { const { client, store } = testStore(); const doc = await store.save({ - id: 'Gandalf', + savedObjectId: 'Gandalf', title: 'Even the very wise cannot see all ends.', visualizationType: 'line', references: [], @@ -95,7 +95,7 @@ describe('LensStore', () => { }); expect(doc).toEqual({ - id: 'Gandalf', + savedObjectId: 'Gandalf', title: 'Even the very wise cannot see all ends.', visualizationType: 'line', references: [], diff --git a/x-pack/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/plugins/lens/public/persistence/saved_object_store.ts index e4609213ec792..c6b3fd2cc0f65 100644 --- a/x-pack/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/plugins/lens/public/persistence/saved_object_store.ts @@ -13,7 +13,7 @@ import { Query } from '../../../../../src/plugins/data/public'; import { PersistableFilter } from '../../common'; export interface Document { - id?: string; + savedObjectId?: string; type?: string; visualizationType: string | null; title: string; @@ -30,11 +30,11 @@ export interface Document { export const DOC_TYPE = 'lens'; export interface DocumentSaver { - save: (vis: Document) => Promise<{ id: string }>; + save: (vis: Document) => Promise<{ savedObjectId: string }>; } export interface DocumentLoader { - load: (id: string) => Promise; + load: (savedObjectId: string) => Promise; } export type SavedObjectStore = DocumentLoader & DocumentSaver; @@ -46,20 +46,20 @@ export class SavedObjectIndexStore implements SavedObjectStore { this.client = client; } - async save(vis: Document) { - const { id, type, references, ...rest } = vis; + save = async (vis: Document) => { + const { savedObjectId, type, references, ...rest } = vis; // TODO: SavedObjectAttributes should support this kind of object, // remove this workaround when SavedObjectAttributes is updated. const attributes = (rest as unknown) as SavedObjectAttributes; - const result = await (id - ? this.safeUpdate(id, attributes, references) + const result = await (savedObjectId + ? this.safeUpdate(savedObjectId, attributes, references) : this.client.create(DOC_TYPE, attributes, { references, })); - return { ...vis, id: result.id }; - } + return { ...vis, savedObjectId: result.id }; + }; // As Lens is using an object to store its attributes, using the update API // will merge the new attribute object with the old one, not overwriting deleted @@ -68,7 +68,7 @@ export class SavedObjectIndexStore implements SavedObjectStore { // This function fixes this by doing two updates - one to empty out the document setting // every key to null, and a second one to load the new content. private async safeUpdate( - id: string, + savedObjectId: string, attributes: SavedObjectAttributes, references: SavedObjectReference[] ) { @@ -78,14 +78,14 @@ export class SavedObjectIndexStore implements SavedObjectStore { }); return ( await this.client.bulkUpdate([ - { type: DOC_TYPE, id, attributes: resetAttributes, references }, - { type: DOC_TYPE, id, attributes, references }, + { type: DOC_TYPE, id: savedObjectId, attributes: resetAttributes, references }, + { type: DOC_TYPE, id: savedObjectId, attributes, references }, ]) ).savedObjects[1]; } - async load(id: string): Promise { - const { type, attributes, references, error } = await this.client.get(DOC_TYPE, id); + async load(savedObjectId: string): Promise { + const { type, attributes, references, error } = await this.client.get(DOC_TYPE, savedObjectId); if (error) { throw error; @@ -94,7 +94,7 @@ export class SavedObjectIndexStore implements SavedObjectStore { return { ...(attributes as SavedObjectAttributes), references, - id, + savedObjectId, type, } as Document; } diff --git a/x-pack/plugins/lens/public/pie_visualization/register_expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx similarity index 100% rename from x-pack/plugins/lens/public/pie_visualization/register_expression.tsx rename to x-pack/plugins/lens/public/pie_visualization/expression.tsx diff --git a/x-pack/plugins/lens/public/pie_visualization/index.ts b/x-pack/plugins/lens/public/pie_visualization/index.ts index 401b6d634c696..36dd9b93c3e39 100644 --- a/x-pack/plugins/lens/public/pie_visualization/index.ts +++ b/x-pack/plugins/lens/public/pie_visualization/index.ts @@ -6,8 +6,6 @@ import { CoreSetup } from 'src/core/public'; import { ExpressionsSetup } from 'src/plugins/expressions/public'; -import { pieVisualization } from './pie_visualization'; -import { pie, getPieRenderer } from './register_expression'; import { EditorFrameSetup, FormatFactory } from '../types'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; @@ -30,15 +28,18 @@ export class PieVisualization { core: CoreSetup, { expressions, formatFactory, editorFrame, charts }: PieVisualizationPluginSetupPlugins ) { - expressions.registerFunction(() => pie); + editorFrame.registerVisualization(async () => { + const { pieVisualization, pie, getPieRenderer } = await import('../async_services'); - expressions.registerRenderer( - getPieRenderer({ - formatFactory, - chartsThemeService: charts.theme, - }) - ); + expressions.registerFunction(() => pie); - editorFrame.registerVisualization(pieVisualization); + expressions.registerRenderer( + getPieRenderer({ + formatFactory, + chartsThemeService: charts.theme, + }) + ); + return pieVisualization; + }); } } diff --git a/x-pack/plugins/lens/public/pie_visualization/pie_visualization.ts b/x-pack/plugins/lens/public/pie_visualization/pie_visualization.ts new file mode 100644 index 0000000000000..c6fe54a82e2d1 --- /dev/null +++ b/x-pack/plugins/lens/public/pie_visualization/pie_visualization.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 * from './expression'; +export * from './visualization'; diff --git a/x-pack/plugins/lens/public/pie_visualization/pie_visualization.tsx b/x-pack/plugins/lens/public/pie_visualization/visualization.tsx similarity index 100% rename from x-pack/plugins/lens/public/pie_visualization/pie_visualization.tsx rename to x-pack/plugins/lens/public/pie_visualization/visualization.tsx diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index f9c63f54d6713..38d256d2b3afd 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -7,10 +7,12 @@ import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; +import { DashboardStart } from 'src/plugins/dashboard/public'; import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; import { VisualizationsSetup } from 'src/plugins/visualizations/public'; import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; import { UrlForwardingSetup } from 'src/plugins/url_forwarding/public'; +import { GlobalSearchPluginSetup } from '../../global_search/public'; import { ChartsPluginSetup } from '../../../../src/plugins/charts/public'; import { EditorFrameService } from './editor_frame_service'; import { @@ -31,8 +33,9 @@ import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; +import { getSearchProvider } from './search_provider'; -import './index.scss'; +import { getLensAttributeService, LensAttributeService } from './lens_attribute_service'; export interface LensPluginSetupDependencies { urlForwarding: UrlForwardingSetup; @@ -41,6 +44,7 @@ export interface LensPluginSetupDependencies { embeddable?: EmbeddableSetup; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; + globalSearch?: GlobalSearchPluginSetup; } export interface LensPluginStartDependencies { @@ -48,13 +52,14 @@ export interface LensPluginStartDependencies { expressions: ExpressionsStart; navigation: NavigationPublicPluginStart; uiActions: UiActionsStart; - embeddable: EmbeddableStart; + dashboard: DashboardStart; + embeddable?: EmbeddableStart; } - export class LensPlugin { private datatableVisualization: DatatableVisualization; private editorFrameService: EditorFrameService; private createEditorFrame: EditorFrameStart['createInstance'] | null = null; + private attributeService: LensAttributeService | null = null; private indexpatternDatasource: IndexPatternDatasource; private xyVisualization: XyVisualization; private metricVisualization: MetricVisualization; @@ -78,13 +83,18 @@ export class LensPlugin { embeddable, visualizations, charts, + globalSearch, }: LensPluginSetupDependencies ) { - const editorFrameSetupInterface = this.editorFrameService.setup(core, { - data, - embeddable, - expressions, - }); + const editorFrameSetupInterface = this.editorFrameService.setup( + core, + { + data, + embeddable, + expressions, + }, + () => this.attributeService! + ); const dependencies: IndexPatternDatasourceSetupPlugins & XyVisualizationPluginSetupPlugins & DatatableVisualizationPluginSetupPlugins & @@ -106,20 +116,44 @@ export class LensPlugin { visualizations.registerAlias(getLensAliasConfig()); + const getByValueFeatureFlag = async () => { + const [, deps] = await core.getStartServices(); + return deps.dashboard.dashboardFeatureFlagConfig; + }; + core.application.register({ id: 'lens', title: NOT_INTERNATIONALIZED_PRODUCT_NAME, navLinkStatus: AppNavLinkStatus.hidden, mount: async (params: AppMountParameters) => { - const { mountApp } = await import('./app_plugin/mounter'); - return mountApp(core, params, this.createEditorFrame!); + const { mountApp } = await import('./async_services'); + return mountApp(core, params, { + createEditorFrame: this.createEditorFrame!, + attributeService: this.attributeService!, + getByValueFeatureFlag, + }); }, }); + if (globalSearch) { + globalSearch.registerResultProvider( + getSearchProvider( + core.getStartServices().then( + ([ + { + application: { capabilities }, + }, + ]) => capabilities + ) + ) + ); + } + urlForwarding.forwardApp('lens', 'lens'); } start(core: CoreStart, startDependencies: LensPluginStartDependencies) { + this.attributeService = getLensAttributeService(core, startDependencies); this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; } diff --git a/x-pack/plugins/lens/public/search_provider.ts b/x-pack/plugins/lens/public/search_provider.ts new file mode 100644 index 0000000000000..c19e7970b45ae --- /dev/null +++ b/x-pack/plugins/lens/public/search_provider.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 levenshtein from 'js-levenshtein'; +import { ApplicationStart } from 'kibana/public'; +import { from } from 'rxjs'; +import { i18n } from '@kbn/i18n'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; +import { GlobalSearchResultProvider } from '../../global_search/public'; +import { getFullPath } from '../common'; + +/** + * Global search provider adding a Lens entry. + * This is necessary because Lens does not show up in the nav bar and is filtered out by the + * default app provider. + * + * It is inlining the same search term matching logic as the application search provider. + * + * TODO: This is a workaround and can be removed once there is a generic way to register sub features + * of apps. In this case, Lens should be considered a feature of Visualize. + */ +export const getSearchProvider: ( + uiCapabilities: Promise +) => GlobalSearchResultProvider = (uiCapabilities) => ({ + id: 'lens', + find: (term) => { + return from( + uiCapabilities.then(({ navLinks: { visualize: visualizeNavLink } }) => { + if (!visualizeNavLink) { + return []; + } + const title = i18n.translate('xpack.lens.searchTitle', { + defaultMessage: 'Lens: create visualizations', + description: 'Lens is a product name and should not be translated', + }); + const searchableTitle = title.toLowerCase(); + + term = term.toLowerCase(); + let score = 0; + + // shortcuts to avoid calculating the distance when there is an exact match somewhere. + if (searchableTitle === term) { + score = 100; + } else if (searchableTitle.startsWith(term)) { + score = 90; + } else if (searchableTitle.includes(term)) { + score = 75; + } else { + const length = Math.max(term.length, searchableTitle.length); + const distance = levenshtein(term, searchableTitle); + + // maximum lev distance is length, we compute the match ratio (lower distance is better) + const ratio = Math.floor((1 - distance / length) * 100); + if (ratio >= 60) { + score = ratio; + } + } + if (score === 0) return []; + return [ + { + id: 'lens', + title, + type: 'application', + icon: 'logoKibana', + meta: { + categoryId: DEFAULT_APP_CATEGORIES.kibana.id, + categoryLabel: DEFAULT_APP_CATEGORIES.kibana.label, + }, + score, + url: getFullPath(), + }, + ]; + }) + ); + }, +}); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index edb787d9ec1a1..e97e0d612a2ee 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -57,8 +57,12 @@ export interface EditorFrameInstance { export interface EditorFrameSetup { // generic type on the API functions to pull the "unknown vs. specific type" error into the implementation - registerDatasource: (datasource: Datasource | Promise>) => void; - registerVisualization: (visualization: Visualization | Promise>) => void; + registerDatasource: ( + datasource: Datasource | (() => Promise>) + ) => void; + registerVisualization: ( + visualization: Visualization | (() => Promise>) + ) => void; } export interface EditorFrameStart { diff --git a/x-pack/plugins/lens/public/visualization_container.scss b/x-pack/plugins/lens/public/visualization_container.scss index e5c359112fe4b..a67aa50127c81 100644 --- a/x-pack/plugins/lens/public/visualization_container.scss +++ b/x-pack/plugins/lens/public/visualization_container.scss @@ -1,3 +1,17 @@ .lnsVisualizationContainer { + @include euiScrollBar; overflow: auto; -} \ No newline at end of file +} + +.lnsExpressionRenderer { + @include euiScrollBar; + position: relative; + width: 100%; + height: 100%; + display: flex; + overflow: auto; + + .lnsExpressionRenderer__component { + position: static; // Let the progress indicator position itself against the outer parent + } +} diff --git a/x-pack/plugins/lens/public/visualization_container.tsx b/x-pack/plugins/lens/public/visualization_container.tsx index 3ca8d5de932d7..521d41b6f8d94 100644 --- a/x-pack/plugins/lens/public/visualization_container.tsx +++ b/x-pack/plugins/lens/public/visualization_container.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import './visualization_container.scss'; + import React from 'react'; import classNames from 'classnames'; -import './visualization_container.scss'; - interface Props extends React.HTMLAttributes { isReady?: boolean; reportTitle?: string; diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/xy_expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/__snapshots__/xy_expression.test.tsx.snap rename to x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap diff --git a/x-pack/plugins/lens/public/xy_visualization/_index.scss b/x-pack/plugins/lens/public/xy_visualization/_index.scss deleted file mode 100644 index 110a9589a6fb4..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'xy_expression'; diff --git a/x-pack/plugins/lens/public/xy_visualization/_xy_expression.scss b/x-pack/plugins/lens/public/xy_visualization/expression.scss similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/_xy_expression.scss rename to x-pack/plugins/lens/public/xy_visualization/expression.scss diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx new file mode 100644 index 0000000000000..3bd6cc73d6320 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -0,0 +1,1992 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { + AreaSeries, + Axis, + BarSeries, + Position, + LineSeries, + Settings, + ScaleType, + GeometryValue, + XYChartSeriesIdentifier, + SeriesNameFn, + Fit, +} from '@elastic/charts'; +import { xyChart, XYChart } from './expression'; +import { LensMultiTable } from '../types'; +import { KibanaDatatable, KibanaDatatableRow } from '../../../../../src/plugins/expressions/public'; +import React from 'react'; +import { shallow } from 'enzyme'; +import { + XYArgs, + LegendConfig, + legendConfig, + layerConfig, + LayerArgs, + AxesSettingsConfig, + tickLabelsConfig, + gridlinesConfig, +} from './types'; +import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; + +const onClickValue = jest.fn(); +const onSelectRange = jest.fn(); + +const chartsThemeService = chartPluginMock.createSetupContract().theme; + +const dateHistogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + timeLayer: { + type: 'kibana_datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date_histogram', + indexPatternId: 'indexPatternId', + aggConfigParams: { + field: 'order_date', + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, + }, + }, + formatHint: { id: 'date', params: { pattern: 'HH:mm' } }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'terms', + indexPatternId: 'indexPatternId', + aggConfigParams: { + field: 'category.keyword', + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + }, + }, + formatHint: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, + }, + }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'count', + indexPatternId: 'indexPatternId', + aggConfigParams: {}, + }, + formatHint: { id: 'number' }, + }, + ], + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, +}; + +const dateHistogramLayer: LayerArgs = { + layerId: 'timeLayer', + hide: false, + xAccessor: 'xAccessorId', + yScaleType: 'linear', + xScaleType: 'time', + isHistogram: true, + splitAccessor: 'splitAccessorId', + seriesType: 'bar_stacked', + accessors: ['yAccessorId'], +}; + +const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({ + type: 'kibana_datatable', + columns: [ + { + id: 'a', + name: 'a', + formatHint: { id: 'number', params: { pattern: '0,0.000' } }, + }, + { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, + { + id: 'c', + name: 'c', + formatHint: { id: 'string' }, + meta: { type: 'date-histogram', aggConfigParams: { interval: 'auto' } }, + }, + { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, + ], + rows, +}); + +const sampleLayer: LayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, +}; + +const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { + type: 'lens_xy_legendConfig', + isVisible: false, + position: Position.Top, + }, + axisTitlesVisibilitySettings: { + type: 'lens_xy_axisTitlesVisibilityConfig', + x: true, + yLeft: true, + yRight: true, + }, + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + layers, +}); + +function sampleArgs() { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]), + }, + }; + + const args: XYArgs = createArgsWithLayers(); + + return { data, args }; +} + +describe('xy_expression', () => { + describe('configs', () => { + test('legendConfig produces the correct arguments', () => { + const args: LegendConfig = { + isVisible: true, + position: Position.Left, + }; + + const result = legendConfig.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_legendConfig', + ...args, + }); + }); + + test('layerConfig produces the correct arguments', () => { + const args: LayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + xScaleType: 'linear', + yScaleType: 'linear', + isHistogram: false, + }; + + const result = layerConfig.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_layer', + ...args, + }); + }); + }); + + test('tickLabelsConfig produces the correct arguments', () => { + const args: AxesSettingsConfig = { + x: true, + yLeft: false, + yRight: false, + }; + + const result = tickLabelsConfig.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_tickLabelsConfig', + ...args, + }); + }); + + test('gridlinesConfig produces the correct arguments', () => { + const args: AxesSettingsConfig = { + x: true, + yLeft: false, + yRight: false, + }; + + const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'lens_xy_gridlinesConfig', + ...args, + }); + }); + + describe('xyChart', () => { + test('it renders with the specified data and args', () => { + const { data, args } = sampleArgs(); + const result = xyChart.fn(data, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'render', + as: 'lens_xy_chart_renderer', + value: { data, args }, + }); + }); + }); + + describe('XYChart component', () => { + let getFormatSpy: jest.Mock; + let convertSpy: jest.Mock; + + const dataWithoutFormats: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + { id: 'd', name: 'd' }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + }, + }; + const dataWithFormats: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + { id: 'd', name: 'd', formatHint: { id: 'custom' } }, + ], + rows: [ + { a: 1, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + }, + }; + + const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { + return shallow( + + ); + }; + + beforeEach(() => { + convertSpy = jest.fn((x) => x); + getFormatSpy = jest.fn(); + getFormatSpy.mockReturnValue({ convert: convertSpy }); + }); + + test('it renders line', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(LineSeries)).toHaveLength(2); + expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + describe('date range', () => { + const timeSampleLayer: LayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'time', + yScaleType: 'linear', + isHistogram: false, + }; + const multiLayerArgs = createArgsWithLayers([ + timeSampleLayer, + { + ...timeSampleLayer, + layerId: 'second', + seriesType: 'bar', + xScaleType: 'time', + }, + ]); + test('it uses the full date range', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + + test('it generates correct xDomain for a layer with single value and a layer with no data (1-0) ', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([]), + }, + }; + + const component = shallow( + + ); + + // real auto interval is 30mins = 1800000 + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": 1728000, + } + `); + }); + + test('it generates correct xDomain for two layers with single value(1-1)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([{ a: 10, b: 5, c: 'J', d: 'Bar' }]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + test('it generates correct xDomain for a layer with single value and layer with multiple value data (1-n)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([ + { a: 10, b: 5, c: 'J', d: 'Bar' }, + { a: 8, b: 5, c: 'K', d: 'Buzz' }, + ]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + + test('it generates correct xDomain for 2 layers with multiple value data (n-n)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 8, b: 5, c: 'K', d: 'Buzz' }, + { a: 9, b: 7, c: 'L', d: 'Bar' }, + { a: 10, b: 2, c: 'G', d: 'Bear' }, + ]), + second: createSampleDatatableWithRows([ + { a: 10, b: 5, c: 'J', d: 'Bar' }, + { a: 8, b: 4, c: 'K', d: 'Fi' }, + { a: 1, b: 8, c: 'O', d: 'Pi' }, + ]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + }); + + test('it does not use date range if the x is not a time scale', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('xDomain')).toBeUndefined(); + }); + + test('it renders bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + test('it renders area', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(AreaSeries)).toHaveLength(2); + expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); + }); + + test('it renders horizontal bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); + expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); + expect(component.find(Settings).prop('rotation')).toEqual(90); + }); + + test('onBrushEnd returns correct context data for date histogram data', () => { + const { args } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); + + expect(onSelectRange).toHaveBeenCalledWith({ + column: 0, + table: dateHistogramData.tables.timeLayer, + range: [1585757732783, 1585758880838], + timeFieldName: 'order_date', + }); + }); + + test('onElementClick returns correct context data', () => { + const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null }; + const series = { + key: 'spec{d}yAccessor{d}splitAccessors{b-2}', + specId: 'd', + yAccessor: 'd', + splitAccessors: {}, + seriesKeys: [2, 'd'], + }; + + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + wrapper.find(Settings).first().prop('onElementClick')!([ + [geometry, series as XYChartSeriesIdentifier], + ]); + + expect(onClickValue).toHaveBeenCalledWith({ + data: [ + { + column: 1, + row: 1, + table: data.tables.first, + value: 5, + }, + { + column: 1, + row: 0, + table: data.tables.first, + value: 2, + }, + ], + }); + }); + + test('it renders stacked bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + }); + + test('it renders stacked area', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(AreaSeries)).toHaveLength(2); + expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); + }); + + test('it renders stacked horizontal bar', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(component.find(BarSeries)).toHaveLength(2); + expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); + expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); + expect(component.find(Settings).prop('rotation')).toEqual(90); + }); + + test('it passes time zone to the series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); + expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); + }); + + test('it applies histogram mode to the series for single series', () => { + const { data, args } = sampleArgs(); + const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; + delete firstLayer.splitAccessor; + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + }); + + test('it applies histogram mode to the series for stacked series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + }); + + test('it does not apply histogram mode for splitted series', () => { + const { data, args } = sampleArgs(); + const component = shallow( + + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + }); + + describe('y axes', () => { + test('single axis if possible', () => { + const args = createArgsWithLayers(); + + const component = getRenderedComponent(dataWithoutFormats, args); + const axes = component.find(Axis); + expect(axes).toHaveLength(2); + }); + + test('multiple axes because of config', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + yConfig: [ + { + forAccessor: 'a', + axisMode: 'left', + }, + { + forAccessor: 'b', + axisMode: 'right', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(3); + expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( + axes.at(1).prop('groupId') + ); + expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( + axes.at(2).prop('groupId') + ); + }); + + test('multiple axes because of incompatible formatters', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['c', 'd'], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(3); + expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( + axes.at(1).prop('groupId') + ); + expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( + axes.at(2).prop('groupId') + ); + }); + + test('single axis despite different formatters if enforced', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['c', 'd'], + yConfig: [ + { + forAccessor: 'c', + axisMode: 'left', + }, + { + forAccessor: 'd', + axisMode: 'left', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const axes = component.find(Axis); + expect(axes).toHaveLength(2); + }); + }); + + describe('y series coloring', () => { + test('color is applied to chart for multiple series', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['a', 'b'], + yConfig: [ + { + forAccessor: 'a', + color: '#550000', + }, + { + forAccessor: 'b', + color: '#FFFF00', + }, + ], + }, + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['c'], + yConfig: [ + { + forAccessor: 'c', + color: '#FEECDF', + }, + ], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + expect((component.find(LineSeries).at(0).prop('color') as Function)!()).toEqual('#550000'); + expect((component.find(LineSeries).at(1).prop('color') as Function)!()).toEqual('#FFFF00'); + expect((component.find(LineSeries).at(2).prop('color') as Function)!()).toEqual('#FEECDF'); + }); + test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + yConfig: [ + { + forAccessor: 'a', + color: '#550000', + }, + ], + }, + { + ...args.layers[0], + splitAccessor: undefined, + accessors: ['c'], + }, + ], + } as XYArgs; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + expect((component.find(LineSeries).at(0).prop('color') as Function)!()).toEqual(null); + expect((component.find(LineSeries).at(1).prop('color') as Function)!()).toEqual(null); + }); + }); + + describe('provides correct series naming', () => { + const nameFnArgs = { + seriesKeys: [], + key: '', + specId: 'a', + yAccessor: '', + splitAccessors: new Map(), + }; + + test('simplest xy chart without human-readable name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + // In this case, the ID is used as the name. This shouldn't happen in practice + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('simplest xy chart with empty name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '{"a":""}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + // In this case, the ID is used as the name. This shouldn't happen in practice + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('simplest xy chart with human-readable name', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: undefined, + columnToLabel: '{"a":"Column A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); + }); + + test('multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: undefined, + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + // This accessor has a human-readable name + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); + // This accessor does not + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); + }); + + test('split series without formatting and single y accessor', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); + }); + + test('split series with formatting and single y accessor', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; + + convertSpy.mockReturnValueOnce('formatted'); + expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); + expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); + }); + + test('split series without formatting with multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A","b": "Label B"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithoutFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( + 'split1 - Label A' + ); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( + 'split1 - Label B' + ); + }); + + test('split series with formatting with multiple y accessors', () => { + const args = createArgsWithLayers(); + const newArgs = { + ...args, + layers: [ + { + ...args.layers[0], + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A","b": "Label B"}', + }, + ], + }; + + const component = getRenderedComponent(dataWithFormats, newArgs); + const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; + const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; + + convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); + expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( + 'formatted1 - Label A' + ); + expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( + 'formatted2 - Label B' + ); + }); + }); + + test('it set the scale of the x axis according to the args prop', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); + expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); + }); + + test('it set the scale of the y axis according to the args prop', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); + expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); + }); + + test('it gets the formatter for the x axis', () => { + const { data, args } = sampleArgs(); + + shallow( + + ); + + expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); + }); + + test('it gets the formatter for the y axis if there is only one accessor', () => { + const { data, args } = sampleArgs(); + + shallow( + + ); + expect(getFormatSpy).toHaveBeenCalledWith({ + id: 'number', + params: { pattern: '0,0.000' }, + }); + }); + + test('it should pass the formatter function to the axis', () => { + const { data, args } = sampleArgs(); + + const instance = shallow( + + ); + + const tickFormatter = instance.find(Axis).first().prop('tickFormat'); + + if (!tickFormatter) { + throw new Error('tickFormatter prop not found'); + } + + tickFormatter('I'); + + expect(convertSpy).toHaveBeenCalledWith('I'); + }); + + test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: false, + }, + }); + }); + + test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).at(1).prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: false, + }, + }); + }); + + test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: true, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: true, + }, + }); + }); + + test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { + const { data, args } = sampleArgs(); + + args.tickLabelsVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_tickLabelsConfig', + }; + + const instance = shallow( + + ); + + const axisStyle = instance.find(Axis).at(1).prop('style'); + + expect(axisStyle).toMatchObject({ + tickLabel: { + visible: true, + }, + }); + }); + + test('it should remove invalid rows', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + ], + rows: [ + { a: undefined, b: 2, c: 'I', d: 'Row 1' }, + { a: 1, b: 5, c: 'J', d: 'Row 2' }, + ], + }, + second: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + ], + rows: [ + { a: undefined, b: undefined, c: undefined }, + { a: undefined, b: undefined, c: undefined }, + ], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: true, + yRight: true, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + layers: [ + { + layerId: 'first', + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + }, + { + layerId: 'second', + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + }, + ], + }; + + const component = shallow( + + ); + + const series = component.find(LineSeries); + + // Only one series should be rendered, even though 2 are configured + // This one series should only have one row, even though 2 are sent + expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); + }); + + test('it should not remove rows with falsy but non-undefined values', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + ], + rows: [ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + layers: [ + { + layerId: 'first', + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + }, + ], + }; + + const component = shallow( + + ); + + const series = component.find(LineSeries); + + expect(series.prop('data')).toEqual([ + { a: 0, b: 2, c: 5 }, + { a: 1, b: 0, c: 7 }, + ]); + }); + + test('it should show legend for split series, even with one row', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a' }, + { id: 'b', name: 'b' }, + { id: 'c', name: 'c' }, + ], + rows: [{ a: 1, b: 5, c: 'J' }], + }, + }, + }; + + const args: XYArgs = { + xTitle: '', + yTitle: '', + yRightTitle: '', + legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, + tickLabelsVisibilitySettings: { + type: 'lens_xy_tickLabelsConfig', + x: true, + yLeft: false, + yRight: false, + }, + gridlinesVisibilitySettings: { + type: 'lens_xy_gridlinesConfig', + x: true, + yLeft: false, + yRight: false, + }, + layers: [ + { + layerId: 'first', + seriesType: 'line', + xAccessor: 'a', + accessors: ['c'], + splitAccessor: 'b', + columnToLabel: '', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, + }, + ], + }; + + const component = shallow( + + ); + + expect(component.find(Settings).prop('showLegend')).toEqual(true); + }); + + test('it should always show legend if showSingleSeries is set', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('showLegend')).toEqual(true); + }); + + test('it not show legend if isVisible is set to false', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('showLegend')).toEqual(false); + }); + + test('it should show legend on right side', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(Settings).prop('legendPosition')).toEqual('top'); + }); + + test('it should apply the fitting function to all non-bar series', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]), + }, + }; + + const args: XYArgs = createArgsWithLayers([ + { ...sampleLayer, accessors: ['a'] }, + { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, + { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, + { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, + ]); + + const component = shallow( + + ); + + expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(BarSeries).prop('fit')).toEqual(undefined); + expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); + expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); + expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); + }); + + test('it should apply None fitting function if not specified', () => { + const { data, args } = sampleArgs(); + + args.layers[0].accessors = ['a']; + + const component = shallow( + + ); + + expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); + }); + + test('it should apply the xTitle if is specified', () => { + const { data, args } = sampleArgs(); + + args.xTitle = 'My custom x-axis title'; + + const component = shallow( + + ); + + expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); + }); + + test('it should hide the X axis title if the corresponding switch is off', () => { + const { data, args } = sampleArgs(); + + args.axisTitlesVisibilitySettings = { + x: false, + yLeft: true, + yRight: true, + type: 'lens_xy_axisTitlesVisibilityConfig', + }; + + const component = shallow( + + ); + + const axisStyle = component.find(Axis).first().prop('style'); + + expect(axisStyle).toMatchObject({ + axisTitle: { + visible: false, + }, + }); + }); + + test('it should show the X axis gridlines if the setting is on', () => { + const { data, args } = sampleArgs(); + + args.gridlinesVisibilitySettings = { + x: true, + yLeft: false, + yRight: false, + type: 'lens_xy_gridlinesConfig', + }; + + const component = shallow( + + ); + + expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ + visible: true, + }); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx new file mode 100644 index 0000000000000..64e0a3670a9aa --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -0,0 +1,676 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 './expression.scss'; + +import React, { useState, useEffect } from 'react'; +import ReactDOM from 'react-dom'; +import moment from 'moment'; +import { + Chart, + Settings, + Axis, + LineSeries, + AreaSeries, + BarSeries, + Position, + GeometryValue, + XYChartSeriesIdentifier, + StackMode, +} from '@elastic/charts'; +import { I18nProvider } from '@kbn/i18n/react'; +import { + ExpressionFunctionDefinition, + ExpressionRenderDefinition, + ExpressionValueSearchContext, + KibanaDatatable, +} from 'src/plugins/expressions/public'; +import { IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + LensMultiTable, + FormatFactory, + ILensInterpreterRenderHandlers, + LensFilterEvent, + LensBrushEvent, +} from '../types'; +import { XYArgs, SeriesType, visualizationTypes } from './types'; +import { VisualizationContainer } from '../visualization_container'; +import { isHorizontalChart, getSeriesColor } from './state_helpers'; +import { parseInterval } from '../../../../../src/plugins/data/common'; +import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; +import { EmptyPlaceholder } from '../shared_components'; +import { desanitizeFilterContext } from '../utils'; +import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions'; +import { getAxesConfiguration } from './axes_configuration'; + +type InferPropType = T extends React.FunctionComponent ? P : T; +type SeriesSpec = InferPropType & + InferPropType & + InferPropType; + +export interface XYChartProps { + data: LensMultiTable; + args: XYArgs; +} + +export interface XYRender { + type: 'render'; + as: 'lens_xy_chart_renderer'; + value: XYChartProps; +} + +type XYChartRenderProps = XYChartProps & { + chartsThemeService: ChartsPluginSetup['theme']; + formatFactory: FormatFactory; + timeZone: string; + histogramBarTarget: number; + onClickValue: (data: LensFilterEvent['data']) => void; + onSelectRange: (data: LensBrushEvent['data']) => void; +}; + +export const xyChart: ExpressionFunctionDefinition< + 'lens_xy_chart', + LensMultiTable | ExpressionValueSearchContext | null, + XYArgs, + XYRender +> = { + name: 'lens_xy_chart', + type: 'render', + inputTypes: ['lens_multitable', 'kibana_context', 'null'], + help: i18n.translate('xpack.lens.xyChart.help', { + defaultMessage: 'An X/Y chart', + }), + args: { + xTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.xTitle.help', { + defaultMessage: 'X axis title', + }), + }, + yTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { + defaultMessage: 'Y left axis title', + }), + }, + yRightTitle: { + types: ['string'], + help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { + defaultMessage: 'Y right axis title', + }), + }, + legend: { + types: ['lens_xy_legendConfig'], + help: i18n.translate('xpack.lens.xyChart.legend.help', { + defaultMessage: 'Configure the chart legend.', + }), + }, + fittingFunction: { + types: ['string'], + options: [...fittingFunctionDefinitions.map(({ id }) => id)], + help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { + defaultMessage: 'Define how missing values are treated', + }), + }, + tickLabelsVisibilitySettings: { + types: ['lens_xy_tickLabelsConfig'], + help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { + defaultMessage: 'Show x and y axes tick labels', + }), + }, + gridlinesVisibilitySettings: { + types: ['lens_xy_gridlinesConfig'], + help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { + defaultMessage: 'Show x and y axes gridlines', + }), + }, + axisTitlesVisibilitySettings: { + types: ['lens_xy_axisTitlesVisibilityConfig'], + help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { + defaultMessage: 'Show x and y axes titles', + }), + }, + layers: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + types: ['lens_xy_layer'] as any, + help: 'Layers of visual series', + multi: true, + }, + }, + fn(data: LensMultiTable, args: XYArgs) { + return { + type: 'render', + as: 'lens_xy_chart_renderer', + value: { + data, + args, + }, + }; + }, +}; + +export const getXyChartRenderer = (dependencies: { + formatFactory: Promise; + chartsThemeService: ChartsPluginSetup['theme']; + histogramBarTarget: number; + timeZone: string; +}): ExpressionRenderDefinition => ({ + name: 'lens_xy_chart_renderer', + displayName: 'XY chart', + help: i18n.translate('xpack.lens.xyChart.renderer.help', { + defaultMessage: 'X/Y chart renderer', + }), + validate: () => undefined, + reuseDomNode: true, + render: async ( + domNode: Element, + config: XYChartProps, + handlers: ILensInterpreterRenderHandlers + ) => { + handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + const onClickValue = (data: LensFilterEvent['data']) => { + handlers.event({ name: 'filter', data }); + }; + const onSelectRange = (data: LensBrushEvent['data']) => { + handlers.event({ name: 'brush', data }); + }; + const formatFactory = await dependencies.formatFactory; + ReactDOM.render( + + + , + domNode, + () => handlers.done() + ); + }, +}); + +function getIconForSeriesType(seriesType: SeriesType): IconType { + return visualizationTypes.find((c) => c.id === seriesType)!.icon || 'empty'; +} + +const MemoizedChart = React.memo(XYChart); + +export function XYChartReportable(props: XYChartRenderProps) { + const [state, setState] = useState({ + isReady: false, + }); + + // It takes a cycle for the XY chart to render. This prevents + // reporting from printing a blank chart placeholder. + useEffect(() => { + setState({ isReady: true }); + }, [setState]); + + return ( + + + + ); +} + +export function XYChart({ + data, + args, + formatFactory, + timeZone, + chartsThemeService, + histogramBarTarget, + onClickValue, + onSelectRange, +}: XYChartRenderProps) { + const { legend, layers, fittingFunction, gridlinesVisibilitySettings } = args; + const chartTheme = chartsThemeService.useChartsTheme(); + const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); + + const filteredLayers = layers.filter(({ layerId, xAccessor, accessors }) => { + return !( + !accessors.length || + !data.tables[layerId] || + data.tables[layerId].rows.length === 0 || + (xAccessor && data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) + ); + }); + + if (filteredLayers.length === 0) { + const icon: IconType = layers.length > 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; + return ; + } + + // use formatting hint of first x axis column to format ticks + const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( + ({ id }) => id === filteredLayers[0].xAccessor + ); + const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.formatHint); + const layersAlreadyFormatted: Record = {}; + // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers + const safeXAccessorLabelRenderer = (value: unknown): string => + xAxisColumn && layersAlreadyFormatted[xAxisColumn.id] + ? (value as string) + : xAxisFormatter.convert(value); + + const chartHasMoreThanOneSeries = + filteredLayers.length > 1 || + filteredLayers.some((layer) => layer.accessors.length > 1) || + filteredLayers.some((layer) => layer.splitAccessor); + const shouldRotate = isHorizontalChart(filteredLayers); + + const yAxesConfiguration = getAxesConfiguration( + filteredLayers, + shouldRotate, + data.tables, + formatFactory + ); + + const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); + const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { + x: true, + yLeft: true, + yRight: true, + }; + const tickLabelsVisibilitySettings = args.tickLabelsVisibilitySettings || { + x: true, + yLeft: true, + yRight: true, + }; + + function calculateMinInterval() { + // check all the tables to see if all of the rows have the same timestamp + // that would mean that chart will draw a single bar + const isSingleTimestampInXDomain = () => { + const firstRowValue = + data.tables[filteredLayers[0].layerId].rows[0][filteredLayers[0].xAccessor!]; + for (const layer of filteredLayers) { + if ( + layer.xAccessor && + data.tables[layer.layerId].rows.some((row) => row[layer.xAccessor!] !== firstRowValue) + ) { + return false; + } + } + return true; + }; + + // add minInterval only for single point in domain + if (data.dateRange && isSingleTimestampInXDomain()) { + if (xAxisColumn?.meta?.aggConfigParams?.interval !== 'auto') + return parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds(); + + const { fromDate, toDate } = data.dateRange; + const duration = moment(toDate).diff(moment(fromDate)); + const targetMs = duration / histogramBarTarget; + return isNaN(targetMs) ? 0 : Math.max(Math.floor(targetMs), 1); + } + return undefined; + } + + const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); + + const xDomain = isTimeViz + ? { + min: data.dateRange?.fromDate.getTime(), + max: data.dateRange?.toDate.getTime(), + minInterval: calculateMinInterval(), + } + : undefined; + + const getYAxesTitles = ( + axisSeries: Array<{ layer: string; accessor: string }>, + groupId: string + ) => { + const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; + return ( + yTitle || + axisSeries + .map( + (series) => + data.tables[series.layer].columns.find((column) => column.id === series.accessor)?.name + ) + .filter((name) => Boolean(name))[0] + ); + }; + + const getYAxesStyle = (groupId: string) => { + const style = { + tickLabel: { + visible: + groupId === 'right' + ? tickLabelsVisibilitySettings?.yRight + : tickLabelsVisibilitySettings?.yLeft, + }, + axisTitle: { + visible: + groupId === 'right' + ? axisTitlesVisibilitySettings?.yRight + : axisTitlesVisibilitySettings?.yLeft, + }, + }; + return style; + }; + + return ( + + safeXAccessorLabelRenderer(d.value), + }} + rotation={shouldRotate ? 90 : 0} + xDomain={xDomain} + onBrushEnd={({ x }) => { + if (!x) { + return; + } + const [min, max] = x; + // in the future we want to make it also for histogram + if (!xAxisColumn || !isTimeViz) { + return; + } + + const table = data.tables[filteredLayers[0].layerId]; + + const xAxisColumnIndex = table.columns.findIndex( + (el) => el.id === filteredLayers[0].xAccessor + ); + const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; + + const context: LensBrushEvent['data'] = { + range: [min, max], + table, + column: xAxisColumnIndex, + timeFieldName, + }; + onSelectRange(context); + }} + onElementClick={([[geometry, series]]) => { + // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue + const xySeries = series as XYChartSeriesIdentifier; + const xyGeometry = geometry as GeometryValue; + + const layer = filteredLayers.find((l) => + xySeries.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) + ); + if (!layer) { + return; + } + + const table = data.tables[layer.layerId]; + + const points = [ + { + row: table.rows.findIndex((row) => { + if (layer.xAccessor) { + if (layersAlreadyFormatted[layer.xAccessor]) { + // stringify the value to compare with the chart value + return xAxisFormatter.convert(row[layer.xAccessor]) === xyGeometry.x; + } + return row[layer.xAccessor] === xyGeometry.x; + } + }), + column: table.columns.findIndex((col) => col.id === layer.xAccessor), + value: xyGeometry.x, + }, + ]; + + if (xySeries.seriesKeys.length > 1) { + const pointValue = xySeries.seriesKeys[0]; + + points.push({ + row: table.rows.findIndex( + (row) => layer.splitAccessor && row[layer.splitAccessor] === pointValue + ), + column: table.columns.findIndex((col) => col.id === layer.splitAccessor), + value: pointValue, + }); + } + + const xAxisFieldName = table.columns.find((el) => el.id === layer.xAccessor)?.meta + ?.aggConfigParams?.field; + const timeFieldName = xDomain && xAxisFieldName; + + const context: LensFilterEvent['data'] = { + data: points.map((point) => ({ + row: point.row, + column: point.column, + value: point.value, + table, + })), + timeFieldName, + }; + onClickValue(desanitizeFilterContext(context)); + }} + /> + + safeXAccessorLabelRenderer(d)} + style={{ + tickLabel: { + visible: tickLabelsVisibilitySettings?.x, + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + }, + }} + /> + + {yAxesConfiguration.map((axis) => ( + axis.formatter?.convert(d) || ''} + style={getYAxesStyle(axis.groupId)} + /> + ))} + + {filteredLayers.flatMap((layer, layerIndex) => + layer.accessors.map((accessor, accessorIndex) => { + const { + splitAccessor, + seriesType, + accessors, + xAccessor, + layerId, + columnToLabel, + yScaleType, + xScaleType, + isHistogram, + } = layer; + const columnToLabelMap: Record = columnToLabel + ? JSON.parse(columnToLabel) + : {}; + + const table = data.tables[layerId]; + + const isPrimitive = (value: unknown): boolean => + value != null && typeof value !== 'object'; + + // what if row values are not primitive? That is the case of, for instance, Ranges + // remaps them to their serialized version with the formatHint metadata + // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on + const tableConverted: KibanaDatatable = { + ...table, + rows: table.rows.map((row) => { + const newRow = { ...row }; + for (const column of table.columns) { + const record = newRow[column.id]; + if (record && !isPrimitive(record)) { + newRow[column.id] = formatFactory(column.formatHint).convert(record); + } + } + return newRow; + }), + }; + + // save the id of the layer with the custom table + table.columns.reduce>( + (alreadyFormatted: Record, { id }) => { + if (alreadyFormatted[id]) { + return alreadyFormatted; + } + alreadyFormatted[id] = table.rows.some( + (row, i) => row[id] !== tableConverted.rows[i][id] + ); + return alreadyFormatted; + }, + layersAlreadyFormatted + ); + + // For date histogram chart type, we're getting the rows that represent intervals without data. + // To not display them in the legend, they need to be filtered out. + const rows = tableConverted.rows.filter( + (row) => + !(xAccessor && typeof row[xAccessor] === 'undefined') && + !( + splitAccessor && + typeof row[splitAccessor] === 'undefined' && + typeof row[accessor] === 'undefined' + ) + ); + + if (!xAccessor) { + rows.forEach((row) => { + row.unifiedX = i18n.translate('xpack.lens.xyChart.emptyXLabel', { + defaultMessage: '(empty)', + }); + }); + } + + const seriesProps: SeriesSpec = { + splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], + stackAccessors: seriesType.includes('stacked') ? [xAccessor as string] : [], + id: `${splitAccessor}-${accessor}`, + xAccessor: xAccessor || 'unifiedX', + yAccessors: [accessor], + data: rows, + xScaleType: xAccessor ? xScaleType : 'ordinal', + yScaleType, + color: () => getSeriesColor(layer, accessor), + groupId: yAxesConfiguration.find((axisConfiguration) => + axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) + )?.groupId, + enableHistogramMode: isHistogram && (seriesType.includes('stacked') || !splitAccessor), + stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined, + timeZone, + areaSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, + }, + }, + lineSeriesStyle: { + point: { + visible: !xAccessor, + radius: 5, + }, + }, + name(d) { + const splitHint = table.columns.find((col) => col.id === splitAccessor)?.formatHint; + + // For multiple y series, the name of the operation is used on each, either: + // * Key - Y name + // * Formatted value - Y name + if (accessors.length > 1) { + const result = d.seriesKeys + .map((key: string | number, i) => { + if ( + i === 0 && + splitHint && + splitAccessor && + !layersAlreadyFormatted[splitAccessor] + ) { + return formatFactory(splitHint).convert(key); + } + return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; + }) + .join(' - '); + return result; + } + + // For formatted split series, format the key + // This handles splitting by dates, for example + if (splitHint) { + if (splitAccessor && layersAlreadyFormatted[splitAccessor]) { + return d.seriesKeys[0]; + } + return formatFactory(splitHint).convert(d.seriesKeys[0]); + } + // This handles both split and single-y cases: + // * If split series without formatting, show the value literally + // * If single Y, the seriesKey will be the accessor, so we show the human-readable name + return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; + }, + }; + + const index = `${layerIndex}-${accessorIndex}`; + + switch (seriesType) { + case 'line': + return ( + + ); + case 'bar': + case 'bar_stacked': + case 'bar_percentage_stacked': + case 'bar_horizontal': + case 'bar_horizontal_stacked': + case 'bar_horizontal_percentage_stacked': + return ; + case 'area_stacked': + case 'area_percentage_stacked': + return ( + + ); + case 'area': + return ( + + ); + default: + return assertNever(seriesType); + } + }) + )} + + ); +} + +function assertNever(x: never): never { + throw new Error('Unexpected series type: ' + x); +} diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 470d197e847eb..259267236ec49 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -8,16 +8,6 @@ import { CoreSetup, IUiSettingsClient } from 'kibana/public'; import moment from 'moment-timezone'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { UI_SETTINGS } from '../../../../../src/plugins/data/public'; -import { xyVisualization } from './xy_visualization'; -import { xyChart, getXyChartRenderer } from './xy_expression'; -import { - legendConfig, - layerConfig, - yAxisConfig, - tickLabelsConfig, - gridlinesConfig, - axisTitlesVisibilityConfig, -} from './types'; import { EditorFrameSetup, FormatFactory } from '../types'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; @@ -44,23 +34,35 @@ export class XyVisualization { core: CoreSetup, { expressions, formatFactory, editorFrame, charts }: XyVisualizationPluginSetupPlugins ) { - expressions.registerFunction(() => legendConfig); - expressions.registerFunction(() => yAxisConfig); - expressions.registerFunction(() => tickLabelsConfig); - expressions.registerFunction(() => gridlinesConfig); - expressions.registerFunction(() => axisTitlesVisibilityConfig); - expressions.registerFunction(() => layerConfig); - expressions.registerFunction(() => xyChart); + editorFrame.registerVisualization(async () => { + const { + legendConfig, + yAxisConfig, + tickLabelsConfig, + gridlinesConfig, + axisTitlesVisibilityConfig, + layerConfig, + xyChart, + getXyChartRenderer, + xyVisualization, + } = await import('../async_services'); + expressions.registerFunction(() => legendConfig); + expressions.registerFunction(() => yAxisConfig); + expressions.registerFunction(() => tickLabelsConfig); + expressions.registerFunction(() => gridlinesConfig); + expressions.registerFunction(() => axisTitlesVisibilityConfig); + expressions.registerFunction(() => layerConfig); + expressions.registerFunction(() => xyChart); - expressions.registerRenderer( - getXyChartRenderer({ - formatFactory, - chartsThemeService: charts.theme, - timeZone: getTimeZone(core.uiSettings), - histogramBarTarget: core.uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET), - }) - ); - - editorFrame.registerVisualization(xyVisualization); + expressions.registerRenderer( + getXyChartRenderer({ + formatFactory, + chartsThemeService: charts.theme, + timeZone: getTimeZone(core.uiSettings), + histogramBarTarget: core.uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET), + }) + ); + return xyVisualization; + }); } } diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts new file mode 100644 index 0000000000000..621fd082faf2d --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts @@ -0,0 +1,374 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { xyVisualization } from './visualization'; +import { Position } from '@elastic/charts'; +import { Operation } from '../types'; +import { State, SeriesType } from './types'; +import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; +import { LensIconChartBar } from '../assets/chart_bar'; + +function exampleState(): State { + return { + legend: { position: Position.Bottom, isVisible: true }, + preferredSeriesType: 'bar', + layers: [ + { + layerId: 'first', + seriesType: 'area', + splitAccessor: 'd', + xAccessor: 'a', + accessors: ['b', 'c'], + }, + ], + }; +} + +describe('xy_visualization', () => { + describe('#getDescription', () => { + function mixedState(...types: SeriesType[]) { + const state = exampleState(); + return { + ...state, + layers: types.map((t, i) => ({ + ...state.layers[0], + layerId: `layer_${i}`, + seriesType: t, + })), + }; + } + + it('should show mixed xy chart when multilple series types', () => { + const desc = xyVisualization.getDescription(mixedState('bar', 'line')); + + expect(desc.label).toEqual('Mixed XY chart'); + }); + + it('should show the preferredSeriesType if there are no layers', () => { + const desc = xyVisualization.getDescription(mixedState()); + + expect(desc.icon).toEqual(LensIconChartBar); + expect(desc.label).toEqual('Bar chart'); + }); + + it('should show mixed horizontal bar chart when multiple horizontal bar types', () => { + const desc = xyVisualization.getDescription( + mixedState('bar_horizontal', 'bar_horizontal_stacked') + ); + + expect(desc.label).toEqual('Mixed horizontal bar chart'); + }); + + it('should show bar chart when bar only', () => { + const desc = xyVisualization.getDescription(mixedState('bar_horizontal', 'bar_horizontal')); + + expect(desc.label).toEqual('Horizontal bar chart'); + }); + + it('should show the chart description if not mixed', () => { + expect(xyVisualization.getDescription(mixedState('area')).label).toEqual('Area chart'); + expect(xyVisualization.getDescription(mixedState('line')).label).toEqual('Line chart'); + expect(xyVisualization.getDescription(mixedState('area_stacked')).label).toEqual( + 'Stacked area chart' + ); + expect(xyVisualization.getDescription(mixedState('bar_horizontal_stacked')).label).toEqual( + 'Stacked horizontal bar chart' + ); + }); + }); + + describe('#getVisualizationTypeId', () => { + function mixedState(...types: SeriesType[]) { + const state = exampleState(); + return { + ...state, + layers: types.map((t, i) => ({ + ...state.layers[0], + layerId: `layer_${i}`, + seriesType: t, + })), + }; + } + + it('should show mixed when each layer is different', () => { + expect(xyVisualization.getVisualizationTypeId(mixedState('bar', 'line'))).toEqual('mixed'); + }); + + it('should show the preferredSeriesType if there are no layers', () => { + expect(xyVisualization.getVisualizationTypeId(mixedState())).toEqual('bar'); + }); + + it('should combine multiple layers into one type', () => { + expect( + xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal', 'bar_horizontal')) + ).toEqual('bar_horizontal'); + }); + + it('should return the subtype for single layers', () => { + expect(xyVisualization.getVisualizationTypeId(mixedState('area'))).toEqual('area'); + expect(xyVisualization.getVisualizationTypeId(mixedState('line'))).toEqual('line'); + expect(xyVisualization.getVisualizationTypeId(mixedState('area_stacked'))).toEqual( + 'area_stacked' + ); + expect(xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal_stacked'))).toEqual( + 'bar_horizontal_stacked' + ); + }); + }); + + describe('#initialize', () => { + it('loads default state', () => { + const mockFrame = createMockFramePublicAPI(); + const initialState = xyVisualization.initialize(mockFrame); + + expect(initialState.layers).toHaveLength(1); + expect(initialState.layers[0].xAccessor).not.toBeDefined(); + expect(initialState.layers[0].accessors).toHaveLength(0); + + expect(initialState).toMatchInlineSnapshot(` + Object { + "layers": Array [ + Object { + "accessors": Array [], + "layerId": "", + "position": "top", + "seriesType": "bar_stacked", + "showGridlines": false, + }, + ], + "legend": Object { + "isVisible": true, + "position": "right", + }, + "preferredSeriesType": "bar_stacked", + "title": "Empty XY chart", + } + `); + }); + + it('loads from persisted state', () => { + expect(xyVisualization.initialize(createMockFramePublicAPI(), exampleState())).toEqual( + exampleState() + ); + }); + }); + + describe('#removeLayer', () => { + it('removes the specified layer', () => { + const prevState: State = { + ...exampleState(), + layers: [ + ...exampleState().layers, + { + layerId: 'second', + seriesType: 'area', + splitAccessor: 'e', + xAccessor: 'f', + accessors: ['g', 'h'], + }, + ], + }; + + expect(xyVisualization.removeLayer!(prevState, 'second')).toEqual(exampleState()); + }); + }); + + describe('#appendLayer', () => { + it('adds a layer', () => { + const layers = xyVisualization.appendLayer!(exampleState(), 'foo').layers; + expect(layers.length).toEqual(exampleState().layers.length + 1); + expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); + }); + }); + + describe('#clearLayer', () => { + it('clears the specified layer', () => { + const layer = xyVisualization.clearLayer(exampleState(), 'first').layers[0]; + expect(layer).toMatchObject({ + accessors: [], + layerId: 'first', + seriesType: 'bar', + }); + }); + }); + + describe('#getLayerIds', () => { + it('returns layerids', () => { + expect(xyVisualization.getLayerIds(exampleState())).toEqual(['first']); + }); + }); + + describe('#setDimension', () => { + it('sets the x axis', () => { + expect( + xyVisualization.setDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: undefined, + accessors: [], + }, + ], + }, + layerId: 'first', + groupId: 'x', + columnId: 'newCol', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: 'newCol', + accessors: [], + }); + }); + + it('replaces the x axis', () => { + expect( + xyVisualization.setDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: 'a', + accessors: [], + }, + ], + }, + layerId: 'first', + groupId: 'x', + columnId: 'newCol', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: 'newCol', + accessors: [], + }); + }); + }); + + describe('#removeDimension', () => { + it('removes the x axis', () => { + expect( + xyVisualization.removeDimension({ + prevState: { + ...exampleState(), + layers: [ + { + layerId: 'first', + seriesType: 'area', + xAccessor: 'a', + accessors: [], + }, + ], + }, + layerId: 'first', + columnId: 'a', + }).layers[0] + ).toEqual({ + layerId: 'first', + seriesType: 'area', + xAccessor: undefined, + accessors: [], + }); + }); + }); + + describe('#getConfiguration', () => { + let mockDatasource: ReturnType; + let frame: ReturnType; + + beforeEach(() => { + frame = createMockFramePublicAPI(); + mockDatasource = createMockDatasource('testDatasource'); + + mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ + { columnId: 'd' }, + { columnId: 'a' }, + { columnId: 'b' }, + { columnId: 'c' }, + ]); + + frame.datasourceLayers = { + first: mockDatasource.publicAPIMock, + }; + }); + + it('should return options for 3 dimensions', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + expect(options).toHaveLength(3); + expect(options.map((o) => o.groupId)).toEqual(['x', 'y', 'breakdown']); + }); + + it('should only accept bucketed operations for x', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + const filterOperations = options.find((o) => o.groupId === 'x')!.filterOperations; + + const exampleOperation: Operation = { + dataType: 'number', + isBucketed: false, + label: 'bar', + }; + const bucketedOps: Operation[] = [ + { ...exampleOperation, isBucketed: true, dataType: 'number' }, + { ...exampleOperation, isBucketed: true, dataType: 'string' }, + { ...exampleOperation, isBucketed: true, dataType: 'boolean' }, + { ...exampleOperation, isBucketed: true, dataType: 'date' }, + ]; + const ops: Operation[] = [ + ...bucketedOps, + { ...exampleOperation, dataType: 'number' }, + { ...exampleOperation, dataType: 'string' }, + { ...exampleOperation, dataType: 'boolean' }, + { ...exampleOperation, dataType: 'date' }, + ]; + expect(ops.filter(filterOperations)).toEqual(bucketedOps); + }); + + it('should not allow anything to be added to x', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + expect(options.find((o) => o.groupId === 'x')?.supportsMoreColumns).toBe(false); + }); + + it('should allow number operations on y', () => { + const options = xyVisualization.getConfiguration({ + state: exampleState(), + frame, + layerId: 'first', + }).groups; + const filterOperations = options.find((o) => o.groupId === 'y')!.filterOperations; + const exampleOperation: Operation = { + dataType: 'number', + isBucketed: false, + label: 'bar', + }; + const ops: Operation[] = [ + { ...exampleOperation, dataType: 'number' }, + { ...exampleOperation, dataType: 'string' }, + { ...exampleOperation, dataType: 'boolean' }, + { ...exampleOperation, dataType: 'date' }, + ]; + expect(ops.filter(filterOperations).map((x) => x.dataType)).toEqual(['number']); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx similarity index 100% rename from x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx rename to x-pack/plugins/lens/public/xy_visualization/visualization.tsx diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx index c7781c2e1d50c..ee22ee51301df 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -17,7 +17,6 @@ import { EuiFormRow, EuiText, htmlIdGenerator, - EuiForm, EuiColorPicker, EuiColorPickerProps, EuiToolTip, @@ -366,7 +365,7 @@ export function DimensionEditor(props: VisualizationDimensionEditorProps) 'auto'; return ( - + <> ) }} /> - + ); } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx deleted file mode 100644 index 1d809f222eb00..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ /dev/null @@ -1,1992 +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 { - AreaSeries, - Axis, - BarSeries, - Position, - LineSeries, - Settings, - ScaleType, - GeometryValue, - XYChartSeriesIdentifier, - SeriesNameFn, - Fit, -} from '@elastic/charts'; -import { xyChart, XYChart } from './xy_expression'; -import { LensMultiTable } from '../types'; -import { KibanaDatatable, KibanaDatatableRow } from '../../../../../src/plugins/expressions/public'; -import React from 'react'; -import { shallow } from 'enzyme'; -import { - XYArgs, - LegendConfig, - legendConfig, - layerConfig, - LayerArgs, - AxesSettingsConfig, - tickLabelsConfig, - gridlinesConfig, -} from './types'; -import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks'; - -const onClickValue = jest.fn(); -const onSelectRange = jest.fn(); - -const chartsThemeService = chartPluginMock.createSetupContract().theme; - -const dateHistogramData: LensMultiTable = { - type: 'lens_multitable', - tables: { - timeLayer: { - type: 'kibana_datatable', - rows: [ - { - xAccessorId: 1585758120000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Accessories", - yAccessorId: 1, - }, - { - xAccessorId: 1585758360000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585759380000, - splitAccessorId: "Women's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760700000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Clothing", - yAccessorId: 1, - }, - { - xAccessorId: 1585760760000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - { - xAccessorId: 1585761120000, - splitAccessorId: "Men's Shoes", - yAccessorId: 1, - }, - ], - columns: [ - { - id: 'xAccessorId', - name: 'order_date per minute', - meta: { - type: 'date_histogram', - indexPatternId: 'indexPatternId', - aggConfigParams: { - field: 'order_date', - timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, - useNormalizedEsInterval: true, - scaleMetricValues: false, - interval: '1m', - drop_partials: false, - min_doc_count: 0, - extended_bounds: {}, - }, - }, - formatHint: { id: 'date', params: { pattern: 'HH:mm' } }, - }, - { - id: 'splitAccessorId', - name: 'Top values of category.keyword', - meta: { - type: 'terms', - indexPatternId: 'indexPatternId', - aggConfigParams: { - field: 'category.keyword', - orderBy: 'yAccessorId', - order: 'desc', - size: 3, - otherBucket: false, - otherBucketLabel: 'Other', - missingBucket: false, - missingBucketLabel: 'Missing', - }, - }, - formatHint: { - id: 'terms', - params: { - id: 'string', - otherBucketLabel: 'Other', - missingBucketLabel: 'Missing', - parsedUrl: { - origin: 'http://localhost:5601', - pathname: '/jiy/app/kibana', - basePath: '/jiy', - }, - }, - }, - }, - { - id: 'yAccessorId', - name: 'Count of records', - meta: { - type: 'count', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, - }, - formatHint: { id: 'number' }, - }, - ], - }, - }, - dateRange: { - fromDate: new Date('2020-04-01T16:14:16.246Z'), - toDate: new Date('2020-04-01T17:15:41.263Z'), - }, -}; - -const dateHistogramLayer: LayerArgs = { - layerId: 'timeLayer', - hide: false, - xAccessor: 'xAccessorId', - yScaleType: 'linear', - xScaleType: 'time', - isHistogram: true, - splitAccessor: 'splitAccessorId', - seriesType: 'bar_stacked', - accessors: ['yAccessorId'], -}; - -const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({ - type: 'kibana_datatable', - columns: [ - { - id: 'a', - name: 'a', - formatHint: { id: 'number', params: { pattern: '0,0.000' } }, - }, - { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, - { - id: 'c', - name: 'c', - formatHint: { id: 'string' }, - meta: { type: 'date-histogram', aggConfigParams: { interval: 'auto' } }, - }, - { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, - ], - rows, -}); - -const sampleLayer: LayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, -}; - -const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { - type: 'lens_xy_legendConfig', - isVisible: false, - position: Position.Top, - }, - axisTitlesVisibilitySettings: { - type: 'lens_xy_axisTitlesVisibilityConfig', - x: true, - yLeft: true, - yRight: true, - }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - layers, -}); - -function sampleArgs() { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - }; - - const args: XYArgs = createArgsWithLayers(); - - return { data, args }; -} - -describe('xy_expression', () => { - describe('configs', () => { - test('legendConfig produces the correct arguments', () => { - const args: LegendConfig = { - isVisible: true, - position: Position.Left, - }; - - const result = legendConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_legendConfig', - ...args, - }); - }); - - test('layerConfig produces the correct arguments', () => { - const args: LayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - xScaleType: 'linear', - yScaleType: 'linear', - isHistogram: false, - }; - - const result = layerConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_layer', - ...args, - }); - }); - }); - - test('tickLabelsConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = tickLabelsConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_tickLabelsConfig', - ...args, - }); - }); - - test('gridlinesConfig produces the correct arguments', () => { - const args: AxesSettingsConfig = { - x: true, - yLeft: false, - yRight: false, - }; - - const result = gridlinesConfig.fn(null, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'lens_xy_gridlinesConfig', - ...args, - }); - }); - - describe('xyChart', () => { - test('it renders with the specified data and args', () => { - const { data, args } = sampleArgs(); - const result = xyChart.fn(data, args, createMockExecutionContext()); - - expect(result).toEqual({ - type: 'render', - as: 'lens_xy_chart_renderer', - value: { data, args }, - }); - }); - }); - - describe('XYChart component', () => { - let getFormatSpy: jest.Mock; - let convertSpy: jest.Mock; - - const dataWithoutFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - { id: 'd', name: 'd' }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - const dataWithFormats: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - { id: 'd', name: 'd', formatHint: { id: 'custom' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - }, - }; - - const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow( - - ); - }; - - beforeEach(() => { - convertSpy = jest.fn((x) => x); - getFormatSpy = jest.fn(); - getFormatSpy.mockReturnValue({ convert: convertSpy }); - }); - - test('it renders line', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(LineSeries)).toHaveLength(2); - expect(component.find(LineSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(LineSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - describe('date range', () => { - const timeSampleLayer: LayerArgs = { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'time', - yScaleType: 'linear', - isHistogram: false, - }; - const multiLayerArgs = createArgsWithLayers([ - timeSampleLayer, - { - ...timeSampleLayer, - layerId: 'second', - seriesType: 'bar', - xScaleType: 'time', - }, - ]); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - - test('it generates correct xDomain for a layer with single value and a layer with no data (1-0) ', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([]), - }, - }; - - const component = shallow( - - ); - - // real auto interval is 30mins = 1800000 - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": 1728000, - } - `); - }); - - test('it generates correct xDomain for two layers with single value(1-1)', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([{ a: 10, b: 5, c: 'J', d: 'Bar' }]), - }, - }; - const component = shallow( - - ); - - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - test('it generates correct xDomain for a layer with single value and layer with multiple value data (1-n)', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), - second: createSampleDatatableWithRows([ - { a: 10, b: 5, c: 'J', d: 'Bar' }, - { a: 8, b: 5, c: 'K', d: 'Buzz' }, - ]), - }, - }; - const component = shallow( - - ); - - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - - test('it generates correct xDomain for 2 layers with multiple value data (n-n)', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 8, b: 5, c: 'K', d: 'Buzz' }, - { a: 9, b: 7, c: 'L', d: 'Bar' }, - { a: 10, b: 2, c: 'G', d: 'Bear' }, - ]), - second: createSampleDatatableWithRows([ - { a: 10, b: 5, c: 'J', d: 'Bar' }, - { a: 8, b: 4, c: 'K', d: 'Fi' }, - { a: 1, b: 8, c: 'O', d: 'Pi' }, - ]), - }, - }; - const component = shallow( - - ); - - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); - }); - }); - - test('it does not use date range if the x is not a time scale', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(Settings).prop('xDomain')).toBeUndefined(); - }); - - test('it renders bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(AreaSeries).at(1).prop('yAccessors')).toEqual(['b']); - }); - - test('it renders horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('yAccessors')).toEqual(['a']); - expect(component.find(BarSeries).at(1).prop('yAccessors')).toEqual(['b']); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('onBrushEnd returns correct context data for date histogram data', () => { - const { args } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] }); - - expect(onSelectRange).toHaveBeenCalledWith({ - column: 0, - table: dateHistogramData.tables.timeLayer, - range: [1585757732783, 1585758880838], - timeFieldName: 'order_date', - }); - }); - - test('onElementClick returns correct context data', () => { - const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null }; - const series = { - key: 'spec{d}yAccessor{d}splitAccessors{b-2}', - specId: 'd', - yAccessor: 'd', - splitAccessors: {}, - seriesKeys: [2, 'd'], - }; - - const { args, data } = sampleArgs(); - - const wrapper = mountWithIntl( - - ); - - wrapper.find(Settings).first().prop('onElementClick')!([ - [geometry, series as XYChartSeriesIdentifier], - ]); - - expect(onClickValue).toHaveBeenCalledWith({ - data: [ - { - column: 1, - row: 1, - table: data.tables.first, - value: 5, - }, - { - column: 1, - row: 0, - table: data.tables.first, - value: 2, - }, - ], - }); - }); - - test('it renders stacked bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked area', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(AreaSeries)).toHaveLength(2); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toHaveLength(1); - }); - - test('it renders stacked horizontal bar', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component).toMatchSnapshot(); - expect(component.find(BarSeries)).toHaveLength(2); - expect(component.find(BarSeries).at(0).prop('stackAccessors')).toHaveLength(1); - expect(component.find(BarSeries).at(1).prop('stackAccessors')).toHaveLength(1); - expect(component.find(Settings).prop('rotation')).toEqual(90); - }); - - test('it passes time zone to the series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); - expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); - }); - - test('it applies histogram mode to the series for single series', () => { - const { data, args } = sampleArgs(); - const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; - delete firstLayer.splitAccessor; - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it applies histogram mode to the series for stacked series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); - }); - - test('it does not apply histogram mode for splitted series', () => { - const { data, args } = sampleArgs(); - const component = shallow( - - ); - expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); - }); - - describe('y axes', () => { - test('single axis if possible', () => { - const args = createArgsWithLayers(); - - const component = getRenderedComponent(dataWithoutFormats, args); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - - test('multiple axes because of config', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - axisMode: 'left', - }, - { - forAccessor: 'b', - axisMode: 'right', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('multiple axes because of incompatible formatters', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(3); - expect(component.find(LineSeries).at(0).prop('groupId')).toEqual( - axes.at(1).prop('groupId') - ); - expect(component.find(LineSeries).at(1).prop('groupId')).toEqual( - axes.at(2).prop('groupId') - ); - }); - - test('single axis despite different formatters if enforced', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['c', 'd'], - yConfig: [ - { - forAccessor: 'c', - axisMode: 'left', - }, - { - forAccessor: 'd', - axisMode: 'left', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const axes = component.find(Axis); - expect(axes).toHaveLength(2); - }); - }); - - describe('y series coloring', () => { - test('color is applied to chart for multiple series', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['a', 'b'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - { - forAccessor: 'b', - color: '#FFFF00', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - yConfig: [ - { - forAccessor: 'c', - color: '#FEECDF', - }, - ], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect((component.find(LineSeries).at(0).prop('color') as Function)!()).toEqual('#550000'); - expect((component.find(LineSeries).at(1).prop('color') as Function)!()).toEqual('#FFFF00'); - expect((component.find(LineSeries).at(2).prop('color') as Function)!()).toEqual('#FEECDF'); - }); - test('color is not applied to chart when splitAccessor is defined or when yConfig is not configured', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - yConfig: [ - { - forAccessor: 'a', - color: '#550000', - }, - ], - }, - { - ...args.layers[0], - splitAccessor: undefined, - accessors: ['c'], - }, - ], - } as XYArgs; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - expect((component.find(LineSeries).at(0).prop('color') as Function)!()).toEqual(null); - expect((component.find(LineSeries).at(1).prop('color') as Function)!()).toEqual(null); - }); - }); - - describe('provides correct series naming', () => { - const nameFnArgs = { - seriesKeys: [], - key: '', - specId: 'a', - yAccessor: '', - splitAccessors: new Map(), - }; - - test('simplest xy chart without human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with empty name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":""}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - // In this case, the ID is used as the name. This shouldn't happen in practice - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual(''); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('simplest xy chart with human-readable name', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: undefined, - columnToLabel: '{"a":"Column A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Column A'); - }); - - test('multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: undefined, - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - // This accessor has a human-readable name - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['a'] }, false)).toEqual('Label A'); - // This accessor does not - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['b'] }, false)).toEqual(''); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['nonsense'] }, false)).toEqual(''); - }); - - test('split series without formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('split1'); - }); - - test('split series with formatting and single y accessor', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn = component.find(LineSeries).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted'); - expect(nameFn({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual('formatted'); - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'custom' }); - }); - - test('split series without formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithoutFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'split1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'split1 - Label B' - ); - }); - - test('split series with formatting with multiple y accessors', () => { - const args = createArgsWithLayers(); - const newArgs = { - ...args, - layers: [ - { - ...args.layers[0], - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A","b": "Label B"}', - }, - ], - }; - - const component = getRenderedComponent(dataWithFormats, newArgs); - const nameFn1 = component.find(LineSeries).at(0).prop('name') as SeriesNameFn; - const nameFn2 = component.find(LineSeries).at(1).prop('name') as SeriesNameFn; - - convertSpy.mockReturnValueOnce('formatted1').mockReturnValueOnce('formatted2'); - expect(nameFn1({ ...nameFnArgs, seriesKeys: ['split1', 'a'] }, false)).toEqual( - 'formatted1 - Label A' - ); - expect(nameFn2({ ...nameFnArgs, seriesKeys: ['split1', 'b'] }, false)).toEqual( - 'formatted2 - Label B' - ); - }); - }); - - test('it set the scale of the x axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); - expect(component.find(LineSeries).at(1).prop('xScaleType')).toEqual(ScaleType.Ordinal); - }); - - test('it set the scale of the y axis according to the args prop', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); - expect(component.find(LineSeries).at(1).prop('yScaleType')).toEqual(ScaleType.Sqrt); - }); - - test('it gets the formatter for the x axis', () => { - const { data, args } = sampleArgs(); - - shallow( - - ); - - expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); - }); - - test('it gets the formatter for the y axis if there is only one accessor', () => { - const { data, args } = sampleArgs(); - - shallow( - - ); - expect(getFormatSpy).toHaveBeenCalledWith({ - id: 'number', - params: { pattern: '0,0.000' }, - }); - }); - - test('it should pass the formatter function to the axis', () => { - const { data, args } = sampleArgs(); - - const instance = shallow( - - ); - - const tickFormatter = instance.find(Axis).first().prop('tickFormat'); - - if (!tickFormatter) { - throw new Error('tickFormatter prop not found'); - } - - tickFormatter('I'); - - expect(convertSpy).toHaveBeenCalledWith('I'); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is hidden', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: false, - }, - }); - }); - - test('it should set the tickLabel visibility on the x axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: true, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should set the tickLabel visibility on the y axis if the tick labels is shown', () => { - const { data, args } = sampleArgs(); - - args.tickLabelsVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_tickLabelsConfig', - }; - - const instance = shallow( - - ); - - const axisStyle = instance.find(Axis).at(1).prop('style'); - - expect(axisStyle).toMatchObject({ - tickLabel: { - visible: true, - }, - }); - }); - - test('it should remove invalid rows', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - ], - rows: [ - { a: undefined, b: 2, c: 'I', d: 'Row 1' }, - { a: 1, b: 5, c: 'J', d: 'Row 2' }, - ], - }, - second: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - ], - rows: [ - { a: undefined, b: undefined, c: undefined }, - { a: undefined, b: undefined, c: undefined }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: true, - yRight: true, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - layers: [ - { - layerId: 'first', - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - }, - { - layerId: 'second', - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - }, - ], - }; - - const component = shallow( - - ); - - const series = component.find(LineSeries); - - // Only one series should be rendered, even though 2 are configured - // This one series should only have one row, even though 2 are sent - expect(series.prop('data')).toEqual([{ a: 1, b: 5, c: 'J', d: 'Row 2' }]); - }); - - test('it should not remove rows with falsy but non-undefined values', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - ], - rows: [ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - layers: [ - { - layerId: 'first', - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - }, - ], - }; - - const component = shallow( - - ); - - const series = component.find(LineSeries); - - expect(series.prop('data')).toEqual([ - { a: 0, b: 2, c: 5 }, - { a: 1, b: 0, c: 7 }, - ]); - }); - - test('it should show legend for split series, even with one row', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: { - type: 'kibana_datatable', - columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - ], - rows: [{ a: 1, b: 5, c: 'J' }], - }, - }, - }; - - const args: XYArgs = { - xTitle: '', - yTitle: '', - yRightTitle: '', - legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top }, - tickLabelsVisibilitySettings: { - type: 'lens_xy_tickLabelsConfig', - x: true, - yLeft: false, - yRight: false, - }, - gridlinesVisibilitySettings: { - type: 'lens_xy_gridlinesConfig', - x: true, - yLeft: false, - yRight: false, - }, - layers: [ - { - layerId: 'first', - seriesType: 'line', - xAccessor: 'a', - accessors: ['c'], - splitAccessor: 'b', - columnToLabel: '', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - }, - ], - }; - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it should always show legend if showSingleSeries is set', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(true); - }); - - test('it not show legend if isVisible is set to false', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('showLegend')).toEqual(false); - }); - - test('it should show legend on right side', () => { - const { data, args } = sampleArgs(); - - const component = shallow( - - ); - - expect(component.find(Settings).prop('legendPosition')).toEqual('top'); - }); - - test('it should apply the fitting function to all non-bar series', () => { - const data: LensMultiTable = { - type: 'lens_multitable', - tables: { - first: createSampleDatatableWithRows([ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ]), - }, - }; - - const args: XYArgs = createArgsWithLayers([ - { ...sampleLayer, accessors: ['a'] }, - { ...sampleLayer, seriesType: 'bar', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area', accessors: ['a'] }, - { ...sampleLayer, seriesType: 'area_stacked', accessors: ['a'] }, - ]); - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(BarSeries).prop('fit')).toEqual(undefined); - expect(component.find(AreaSeries).at(0).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(0).prop('stackAccessors')).toEqual([]); - expect(component.find(AreaSeries).at(1).prop('fit')).toEqual({ type: Fit.Carry }); - expect(component.find(AreaSeries).at(1).prop('stackAccessors')).toEqual(['c']); - }); - - test('it should apply None fitting function if not specified', () => { - const { data, args } = sampleArgs(); - - args.layers[0].accessors = ['a']; - - const component = shallow( - - ); - - expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); - }); - - test('it should apply the xTitle if is specified', () => { - const { data, args } = sampleArgs(); - - args.xTitle = 'My custom x-axis title'; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); - }); - - test('it should hide the X axis title if the corresponding switch is off', () => { - const { data, args } = sampleArgs(); - - args.axisTitlesVisibilitySettings = { - x: false, - yLeft: true, - yRight: true, - type: 'lens_xy_axisTitlesVisibilityConfig', - }; - - const component = shallow( - - ); - - const axisStyle = component.find(Axis).first().prop('style'); - - expect(axisStyle).toMatchObject({ - axisTitle: { - visible: false, - }, - }); - }); - - test('it should show the X axis gridlines if the setting is on', () => { - const { data, args } = sampleArgs(); - - args.gridlinesVisibilitySettings = { - x: true, - yLeft: false, - yRight: false, - type: 'lens_xy_gridlinesConfig', - }; - - const component = shallow( - - ); - - expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ - visible: true, - }); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx deleted file mode 100644 index 9379c8a612eb2..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ /dev/null @@ -1,618 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; -import moment from 'moment'; -import { - Chart, - Settings, - Axis, - LineSeries, - AreaSeries, - BarSeries, - Position, - GeometryValue, - XYChartSeriesIdentifier, - StackMode, -} from '@elastic/charts'; -import { I18nProvider } from '@kbn/i18n/react'; -import { - ExpressionFunctionDefinition, - ExpressionRenderDefinition, - ExpressionValueSearchContext, -} from 'src/plugins/expressions/public'; -import { IconType } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { - LensMultiTable, - FormatFactory, - ILensInterpreterRenderHandlers, - LensFilterEvent, - LensBrushEvent, -} from '../types'; -import { XYArgs, SeriesType, visualizationTypes } from './types'; -import { VisualizationContainer } from '../visualization_container'; -import { isHorizontalChart, getSeriesColor } from './state_helpers'; -import { parseInterval } from '../../../../../src/plugins/data/common'; -import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; -import { EmptyPlaceholder } from '../shared_components'; -import { desanitizeFilterContext } from '../utils'; -import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions'; -import { getAxesConfiguration } from './axes_configuration'; - -type InferPropType = T extends React.FunctionComponent ? P : T; -type SeriesSpec = InferPropType & - InferPropType & - InferPropType; - -export interface XYChartProps { - data: LensMultiTable; - args: XYArgs; -} - -export interface XYRender { - type: 'render'; - as: 'lens_xy_chart_renderer'; - value: XYChartProps; -} - -type XYChartRenderProps = XYChartProps & { - chartsThemeService: ChartsPluginSetup['theme']; - formatFactory: FormatFactory; - timeZone: string; - histogramBarTarget: number; - onClickValue: (data: LensFilterEvent['data']) => void; - onSelectRange: (data: LensBrushEvent['data']) => void; -}; - -export const xyChart: ExpressionFunctionDefinition< - 'lens_xy_chart', - LensMultiTable | ExpressionValueSearchContext | null, - XYArgs, - XYRender -> = { - name: 'lens_xy_chart', - type: 'render', - inputTypes: ['lens_multitable', 'kibana_context', 'null'], - help: i18n.translate('xpack.lens.xyChart.help', { - defaultMessage: 'An X/Y chart', - }), - args: { - xTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.xTitle.help', { - defaultMessage: 'X axis title', - }), - }, - yTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', { - defaultMessage: 'Y left axis title', - }), - }, - yRightTitle: { - types: ['string'], - help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', { - defaultMessage: 'Y right axis title', - }), - }, - legend: { - types: ['lens_xy_legendConfig'], - help: i18n.translate('xpack.lens.xyChart.legend.help', { - defaultMessage: 'Configure the chart legend.', - }), - }, - fittingFunction: { - types: ['string'], - options: [...fittingFunctionDefinitions.map(({ id }) => id)], - help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', { - defaultMessage: 'Define how missing values are treated', - }), - }, - tickLabelsVisibilitySettings: { - types: ['lens_xy_tickLabelsConfig'], - help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', { - defaultMessage: 'Show x and y axes tick labels', - }), - }, - gridlinesVisibilitySettings: { - types: ['lens_xy_gridlinesConfig'], - help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', { - defaultMessage: 'Show x and y axes gridlines', - }), - }, - axisTitlesVisibilitySettings: { - types: ['lens_xy_axisTitlesVisibilityConfig'], - help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', { - defaultMessage: 'Show x and y axes titles', - }), - }, - layers: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - types: ['lens_xy_layer'] as any, - help: 'Layers of visual series', - multi: true, - }, - }, - fn(data: LensMultiTable, args: XYArgs) { - return { - type: 'render', - as: 'lens_xy_chart_renderer', - value: { - data, - args, - }, - }; - }, -}; - -export const getXyChartRenderer = (dependencies: { - formatFactory: Promise; - chartsThemeService: ChartsPluginSetup['theme']; - histogramBarTarget: number; - timeZone: string; -}): ExpressionRenderDefinition => ({ - name: 'lens_xy_chart_renderer', - displayName: 'XY chart', - help: i18n.translate('xpack.lens.xyChart.renderer.help', { - defaultMessage: 'X/Y chart renderer', - }), - validate: () => undefined, - reuseDomNode: true, - render: async ( - domNode: Element, - config: XYChartProps, - handlers: ILensInterpreterRenderHandlers - ) => { - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - const onClickValue = (data: LensFilterEvent['data']) => { - handlers.event({ name: 'filter', data }); - }; - const onSelectRange = (data: LensBrushEvent['data']) => { - handlers.event({ name: 'brush', data }); - }; - const formatFactory = await dependencies.formatFactory; - ReactDOM.render( - - - , - domNode, - () => handlers.done() - ); - }, -}); - -function getIconForSeriesType(seriesType: SeriesType): IconType { - return visualizationTypes.find((c) => c.id === seriesType)!.icon || 'empty'; -} - -const MemoizedChart = React.memo(XYChart); - -export function XYChartReportable(props: XYChartRenderProps) { - const [state, setState] = useState({ - isReady: false, - }); - - // It takes a cycle for the XY chart to render. This prevents - // reporting from printing a blank chart placeholder. - useEffect(() => { - setState({ isReady: true }); - }, [setState]); - - return ( - - - - ); -} - -export function XYChart({ - data, - args, - formatFactory, - timeZone, - chartsThemeService, - histogramBarTarget, - onClickValue, - onSelectRange, -}: XYChartRenderProps) { - const { legend, layers, fittingFunction, gridlinesVisibilitySettings } = args; - const chartTheme = chartsThemeService.useChartsTheme(); - const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); - - const filteredLayers = layers.filter(({ layerId, xAccessor, accessors }) => { - return !( - !accessors.length || - !data.tables[layerId] || - data.tables[layerId].rows.length === 0 || - (xAccessor && data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) - ); - }); - - if (filteredLayers.length === 0) { - const icon: IconType = layers.length > 0 ? getIconForSeriesType(layers[0].seriesType) : 'bar'; - return ; - } - - // use formatting hint of first x axis column to format ticks - const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( - ({ id }) => id === filteredLayers[0].xAccessor - ); - const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.formatHint); - - const chartHasMoreThanOneSeries = - filteredLayers.length > 1 || - filteredLayers.some((layer) => layer.accessors.length > 1) || - filteredLayers.some((layer) => layer.splitAccessor); - const shouldRotate = isHorizontalChart(filteredLayers); - - const yAxesConfiguration = getAxesConfiguration( - filteredLayers, - shouldRotate, - data.tables, - formatFactory - ); - - const xTitle = args.xTitle || (xAxisColumn && xAxisColumn.name); - const axisTitlesVisibilitySettings = args.axisTitlesVisibilitySettings || { - x: true, - yLeft: true, - yRight: true, - }; - const tickLabelsVisibilitySettings = args.tickLabelsVisibilitySettings || { - x: true, - yLeft: true, - yRight: true, - }; - - function calculateMinInterval() { - // check all the tables to see if all of the rows have the same timestamp - // that would mean that chart will draw a single bar - const isSingleTimestampInXDomain = () => { - const firstRowValue = - data.tables[filteredLayers[0].layerId].rows[0][filteredLayers[0].xAccessor!]; - for (const layer of filteredLayers) { - if ( - layer.xAccessor && - data.tables[layer.layerId].rows.some((row) => row[layer.xAccessor!] !== firstRowValue) - ) { - return false; - } - } - return true; - }; - - // add minInterval only for single point in domain - if (data.dateRange && isSingleTimestampInXDomain()) { - if (xAxisColumn?.meta?.aggConfigParams?.interval !== 'auto') - return parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds(); - - const { fromDate, toDate } = data.dateRange; - const duration = moment(toDate).diff(moment(fromDate)); - const targetMs = duration / histogramBarTarget; - return isNaN(targetMs) ? 0 : Math.max(Math.floor(targetMs), 1); - } - return undefined; - } - - const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); - - const xDomain = isTimeViz - ? { - min: data.dateRange?.fromDate.getTime(), - max: data.dateRange?.toDate.getTime(), - minInterval: calculateMinInterval(), - } - : undefined; - - const getYAxesTitles = ( - axisSeries: Array<{ layer: string; accessor: string }>, - groupId: string - ) => { - const yTitle = groupId === 'right' ? args.yRightTitle : args.yTitle; - return ( - yTitle || - axisSeries - .map( - (series) => - data.tables[series.layer].columns.find((column) => column.id === series.accessor)?.name - ) - .filter((name) => Boolean(name))[0] - ); - }; - - const getYAxesStyle = (groupId: string) => { - const style = { - tickLabel: { - visible: - groupId === 'right' - ? tickLabelsVisibilitySettings?.yRight - : tickLabelsVisibilitySettings?.yLeft, - }, - axisTitle: { - visible: - groupId === 'right' - ? axisTitlesVisibilitySettings?.yRight - : axisTitlesVisibilitySettings?.yLeft, - }, - }; - return style; - }; - - return ( - - xAxisFormatter.convert(d.value), - }} - rotation={shouldRotate ? 90 : 0} - xDomain={xDomain} - onBrushEnd={({ x }) => { - if (!x) { - return; - } - const [min, max] = x; - // in the future we want to make it also for histogram - if (!xAxisColumn || !isTimeViz) { - return; - } - - const table = data.tables[filteredLayers[0].layerId]; - - const xAxisColumnIndex = table.columns.findIndex( - (el) => el.id === filteredLayers[0].xAccessor - ); - const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; - - const context: LensBrushEvent['data'] = { - range: [min, max], - table, - column: xAxisColumnIndex, - timeFieldName, - }; - onSelectRange(context); - }} - onElementClick={([[geometry, series]]) => { - // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue - const xySeries = series as XYChartSeriesIdentifier; - const xyGeometry = geometry as GeometryValue; - - const layer = filteredLayers.find((l) => - xySeries.seriesKeys.some((key: string | number) => l.accessors.includes(key.toString())) - ); - if (!layer) { - return; - } - - const table = data.tables[layer.layerId]; - - const points = [ - { - row: table.rows.findIndex( - (row) => layer.xAccessor && row[layer.xAccessor] === xyGeometry.x - ), - column: table.columns.findIndex((col) => col.id === layer.xAccessor), - value: xyGeometry.x, - }, - ]; - - if (xySeries.seriesKeys.length > 1) { - const pointValue = xySeries.seriesKeys[0]; - - points.push({ - row: table.rows.findIndex( - (row) => layer.splitAccessor && row[layer.splitAccessor] === pointValue - ), - column: table.columns.findIndex((col) => col.id === layer.splitAccessor), - value: pointValue, - }); - } - - const xAxisFieldName = table.columns.find((el) => el.id === layer.xAccessor)?.meta - ?.aggConfigParams?.field; - const timeFieldName = xDomain && xAxisFieldName; - - const context: LensFilterEvent['data'] = { - data: points.map((point) => ({ - row: point.row, - column: point.column, - value: point.value, - table, - })), - timeFieldName, - }; - onClickValue(desanitizeFilterContext(context)); - }} - /> - - xAxisFormatter.convert(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - }, - axisTitle: { - visible: axisTitlesVisibilitySettings.x, - }, - }} - /> - - {yAxesConfiguration.map((axis) => ( - axis.formatter?.convert(d) || ''} - style={getYAxesStyle(axis.groupId)} - /> - ))} - - {filteredLayers.flatMap((layer, layerIndex) => - layer.accessors.map((accessor, accessorIndex) => { - const { - splitAccessor, - seriesType, - accessors, - xAccessor, - layerId, - columnToLabel, - yScaleType, - xScaleType, - isHistogram, - } = layer; - const columnToLabelMap: Record = columnToLabel - ? JSON.parse(columnToLabel) - : {}; - - const table = data.tables[layerId]; - - // For date histogram chart type, we're getting the rows that represent intervals without data. - // To not display them in the legend, they need to be filtered out. - const rows = table.rows.filter( - (row) => - !(xAccessor && typeof row[xAccessor] === 'undefined') && - !( - splitAccessor && - typeof row[splitAccessor] === 'undefined' && - typeof row[accessor] === 'undefined' - ) - ); - - if (!xAccessor) { - rows.forEach((row) => { - row.unifiedX = i18n.translate('xpack.lens.xyChart.emptyXLabel', { - defaultMessage: '(empty)', - }); - }); - } - - const seriesProps: SeriesSpec = { - splitSeriesAccessors: splitAccessor ? [splitAccessor] : [], - stackAccessors: seriesType.includes('stacked') ? [xAccessor as string] : [], - id: `${splitAccessor}-${accessor}`, - xAccessor: xAccessor || 'unifiedX', - yAccessors: [accessor], - data: rows, - xScaleType: xAccessor ? xScaleType : 'ordinal', - yScaleType, - color: () => getSeriesColor(layer, accessor), - groupId: yAxesConfiguration.find((axisConfiguration) => - axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) - )?.groupId, - enableHistogramMode: isHistogram && (seriesType.includes('stacked') || !splitAccessor), - stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined, - timeZone, - areaSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, - }, - }, - lineSeriesStyle: { - point: { - visible: !xAccessor, - radius: 5, - }, - }, - name(d) { - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.formatHint; - - // For multiple y series, the name of the operation is used on each, either: - // * Key - Y name - // * Formatted value - Y name - if (accessors.length > 1) { - return d.seriesKeys - .map((key: string | number, i) => { - if (i === 0 && splitHint) { - return formatFactory(splitHint).convert(key); - } - return splitAccessor && i === 0 ? key : columnToLabelMap[key] ?? ''; - }) - .join(' - '); - } - - // For formatted split series, format the key - // This handles splitting by dates, for example - if (splitHint) { - return formatFactory(splitHint).convert(d.seriesKeys[0]); - } - // This handles both split and single-y cases: - // * If split series without formatting, show the value literally - // * If single Y, the seriesKey will be the accessor, so we show the human-readable name - return splitAccessor ? d.seriesKeys[0] : columnToLabelMap[d.seriesKeys[0]] ?? ''; - }, - }; - - const index = `${layerIndex}-${accessorIndex}`; - - switch (seriesType) { - case 'line': - return ( - - ); - case 'bar': - case 'bar_stacked': - case 'bar_percentage_stacked': - case 'bar_horizontal': - case 'bar_horizontal_stacked': - case 'bar_horizontal_percentage_stacked': - return ; - case 'area_stacked': - case 'area_percentage_stacked': - return ( - - ); - case 'area': - return ( - - ); - default: - return assertNever(seriesType); - } - }) - )} - - ); -} - -function assertNever(x: never): never { - throw new Error('Unexpected series type: ' + x); -} diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.test.ts deleted file mode 100644 index 7cf1830cdc2fa..0000000000000 --- a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.test.ts +++ /dev/null @@ -1,374 +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 { xyVisualization } from './xy_visualization'; -import { Position } from '@elastic/charts'; -import { Operation } from '../types'; -import { State, SeriesType } from './types'; -import { createMockDatasource, createMockFramePublicAPI } from '../editor_frame_service/mocks'; -import { LensIconChartBar } from '../assets/chart_bar'; - -function exampleState(): State { - return { - legend: { position: Position.Bottom, isVisible: true }, - preferredSeriesType: 'bar', - layers: [ - { - layerId: 'first', - seriesType: 'area', - splitAccessor: 'd', - xAccessor: 'a', - accessors: ['b', 'c'], - }, - ], - }; -} - -describe('xy_visualization', () => { - describe('#getDescription', () => { - function mixedState(...types: SeriesType[]) { - const state = exampleState(); - return { - ...state, - layers: types.map((t, i) => ({ - ...state.layers[0], - layerId: `layer_${i}`, - seriesType: t, - })), - }; - } - - it('should show mixed xy chart when multilple series types', () => { - const desc = xyVisualization.getDescription(mixedState('bar', 'line')); - - expect(desc.label).toEqual('Mixed XY chart'); - }); - - it('should show the preferredSeriesType if there are no layers', () => { - const desc = xyVisualization.getDescription(mixedState()); - - expect(desc.icon).toEqual(LensIconChartBar); - expect(desc.label).toEqual('Bar chart'); - }); - - it('should show mixed horizontal bar chart when multiple horizontal bar types', () => { - const desc = xyVisualization.getDescription( - mixedState('bar_horizontal', 'bar_horizontal_stacked') - ); - - expect(desc.label).toEqual('Mixed horizontal bar chart'); - }); - - it('should show bar chart when bar only', () => { - const desc = xyVisualization.getDescription(mixedState('bar_horizontal', 'bar_horizontal')); - - expect(desc.label).toEqual('Horizontal bar chart'); - }); - - it('should show the chart description if not mixed', () => { - expect(xyVisualization.getDescription(mixedState('area')).label).toEqual('Area chart'); - expect(xyVisualization.getDescription(mixedState('line')).label).toEqual('Line chart'); - expect(xyVisualization.getDescription(mixedState('area_stacked')).label).toEqual( - 'Stacked area chart' - ); - expect(xyVisualization.getDescription(mixedState('bar_horizontal_stacked')).label).toEqual( - 'Stacked horizontal bar chart' - ); - }); - }); - - describe('#getVisualizationTypeId', () => { - function mixedState(...types: SeriesType[]) { - const state = exampleState(); - return { - ...state, - layers: types.map((t, i) => ({ - ...state.layers[0], - layerId: `layer_${i}`, - seriesType: t, - })), - }; - } - - it('should show mixed when each layer is different', () => { - expect(xyVisualization.getVisualizationTypeId(mixedState('bar', 'line'))).toEqual('mixed'); - }); - - it('should show the preferredSeriesType if there are no layers', () => { - expect(xyVisualization.getVisualizationTypeId(mixedState())).toEqual('bar'); - }); - - it('should combine multiple layers into one type', () => { - expect( - xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal', 'bar_horizontal')) - ).toEqual('bar_horizontal'); - }); - - it('should return the subtype for single layers', () => { - expect(xyVisualization.getVisualizationTypeId(mixedState('area'))).toEqual('area'); - expect(xyVisualization.getVisualizationTypeId(mixedState('line'))).toEqual('line'); - expect(xyVisualization.getVisualizationTypeId(mixedState('area_stacked'))).toEqual( - 'area_stacked' - ); - expect(xyVisualization.getVisualizationTypeId(mixedState('bar_horizontal_stacked'))).toEqual( - 'bar_horizontal_stacked' - ); - }); - }); - - describe('#initialize', () => { - it('loads default state', () => { - const mockFrame = createMockFramePublicAPI(); - const initialState = xyVisualization.initialize(mockFrame); - - expect(initialState.layers).toHaveLength(1); - expect(initialState.layers[0].xAccessor).not.toBeDefined(); - expect(initialState.layers[0].accessors).toHaveLength(0); - - expect(initialState).toMatchInlineSnapshot(` - Object { - "layers": Array [ - Object { - "accessors": Array [], - "layerId": "", - "position": "top", - "seriesType": "bar_stacked", - "showGridlines": false, - }, - ], - "legend": Object { - "isVisible": true, - "position": "right", - }, - "preferredSeriesType": "bar_stacked", - "title": "Empty XY chart", - } - `); - }); - - it('loads from persisted state', () => { - expect(xyVisualization.initialize(createMockFramePublicAPI(), exampleState())).toEqual( - exampleState() - ); - }); - }); - - describe('#removeLayer', () => { - it('removes the specified layer', () => { - const prevState: State = { - ...exampleState(), - layers: [ - ...exampleState().layers, - { - layerId: 'second', - seriesType: 'area', - splitAccessor: 'e', - xAccessor: 'f', - accessors: ['g', 'h'], - }, - ], - }; - - expect(xyVisualization.removeLayer!(prevState, 'second')).toEqual(exampleState()); - }); - }); - - describe('#appendLayer', () => { - it('adds a layer', () => { - const layers = xyVisualization.appendLayer!(exampleState(), 'foo').layers; - expect(layers.length).toEqual(exampleState().layers.length + 1); - expect(layers[layers.length - 1]).toMatchObject({ layerId: 'foo' }); - }); - }); - - describe('#clearLayer', () => { - it('clears the specified layer', () => { - const layer = xyVisualization.clearLayer(exampleState(), 'first').layers[0]; - expect(layer).toMatchObject({ - accessors: [], - layerId: 'first', - seriesType: 'bar', - }); - }); - }); - - describe('#getLayerIds', () => { - it('returns layerids', () => { - expect(xyVisualization.getLayerIds(exampleState())).toEqual(['first']); - }); - }); - - describe('#setDimension', () => { - it('sets the x axis', () => { - expect( - xyVisualization.setDimension({ - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - seriesType: 'area', - xAccessor: undefined, - accessors: [], - }, - ], - }, - layerId: 'first', - groupId: 'x', - columnId: 'newCol', - }).layers[0] - ).toEqual({ - layerId: 'first', - seriesType: 'area', - xAccessor: 'newCol', - accessors: [], - }); - }); - - it('replaces the x axis', () => { - expect( - xyVisualization.setDimension({ - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - seriesType: 'area', - xAccessor: 'a', - accessors: [], - }, - ], - }, - layerId: 'first', - groupId: 'x', - columnId: 'newCol', - }).layers[0] - ).toEqual({ - layerId: 'first', - seriesType: 'area', - xAccessor: 'newCol', - accessors: [], - }); - }); - }); - - describe('#removeDimension', () => { - it('removes the x axis', () => { - expect( - xyVisualization.removeDimension({ - prevState: { - ...exampleState(), - layers: [ - { - layerId: 'first', - seriesType: 'area', - xAccessor: 'a', - accessors: [], - }, - ], - }, - layerId: 'first', - columnId: 'a', - }).layers[0] - ).toEqual({ - layerId: 'first', - seriesType: 'area', - xAccessor: undefined, - accessors: [], - }); - }); - }); - - describe('#getConfiguration', () => { - let mockDatasource: ReturnType; - let frame: ReturnType; - - beforeEach(() => { - frame = createMockFramePublicAPI(); - mockDatasource = createMockDatasource('testDatasource'); - - mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([ - { columnId: 'd' }, - { columnId: 'a' }, - { columnId: 'b' }, - { columnId: 'c' }, - ]); - - frame.datasourceLayers = { - first: mockDatasource.publicAPIMock, - }; - }); - - it('should return options for 3 dimensions', () => { - const options = xyVisualization.getConfiguration({ - state: exampleState(), - frame, - layerId: 'first', - }).groups; - expect(options).toHaveLength(3); - expect(options.map((o) => o.groupId)).toEqual(['x', 'y', 'breakdown']); - }); - - it('should only accept bucketed operations for x', () => { - const options = xyVisualization.getConfiguration({ - state: exampleState(), - frame, - layerId: 'first', - }).groups; - const filterOperations = options.find((o) => o.groupId === 'x')!.filterOperations; - - const exampleOperation: Operation = { - dataType: 'number', - isBucketed: false, - label: 'bar', - }; - const bucketedOps: Operation[] = [ - { ...exampleOperation, isBucketed: true, dataType: 'number' }, - { ...exampleOperation, isBucketed: true, dataType: 'string' }, - { ...exampleOperation, isBucketed: true, dataType: 'boolean' }, - { ...exampleOperation, isBucketed: true, dataType: 'date' }, - ]; - const ops: Operation[] = [ - ...bucketedOps, - { ...exampleOperation, dataType: 'number' }, - { ...exampleOperation, dataType: 'string' }, - { ...exampleOperation, dataType: 'boolean' }, - { ...exampleOperation, dataType: 'date' }, - ]; - expect(ops.filter(filterOperations)).toEqual(bucketedOps); - }); - - it('should not allow anything to be added to x', () => { - const options = xyVisualization.getConfiguration({ - state: exampleState(), - frame, - layerId: 'first', - }).groups; - expect(options.find((o) => o.groupId === 'x')?.supportsMoreColumns).toBe(false); - }); - - it('should allow number operations on y', () => { - const options = xyVisualization.getConfiguration({ - state: exampleState(), - frame, - layerId: 'first', - }).groups; - const filterOperations = options.find((o) => o.groupId === 'y')!.filterOperations; - const exampleOperation: Operation = { - dataType: 'number', - isBucketed: false, - label: 'bar', - }; - const ops: Operation[] = [ - { ...exampleOperation, dataType: 'number' }, - { ...exampleOperation, dataType: 'string' }, - { ...exampleOperation, dataType: 'boolean' }, - { ...exampleOperation, dataType: 'date' }, - ]; - expect(ops.filter(filterOperations).map((x) => x.dataType)).toEqual(['number']); - }); - }); -}); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.ts new file mode 100644 index 0000000000000..35053bba738b9 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.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 * from './expression'; +export * from './types'; +export * from './visualization'; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.test.ts b/x-pack/plugins/lens/server/routes/existing_fields.test.ts index 728b78c8e97bc..9799dcf92ae41 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.test.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.test.ts @@ -15,6 +15,7 @@ describe('existingFields', () => { name, isScript: false, isAlias: false, + isMeta: false, path: name.split('.'), ...obj, }; @@ -101,6 +102,15 @@ describe('existingFields', () => { expect(result).toEqual(['baz']); }); + + it('supports meta fields', () => { + const result = existingFields( + [{ _mymeta: 'abc', ...indexPattern({}, { bar: 'scriptvalue' }) }], + [field({ name: '_mymeta', isMeta: true, path: ['_mymeta'] })] + ); + + expect(result).toEqual(['_mymeta']); + }); }); describe('buildFieldList', () => { @@ -116,6 +126,7 @@ describe('buildFieldList', () => { { name: 'bar' }, { name: '@bar' }, { name: 'baz' }, + { name: '_mymeta' }, ]), }, references: [], @@ -142,7 +153,7 @@ describe('buildFieldList', () => { ]; it('uses field descriptors to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.name === 'baz')).toMatchObject({ isAlias: false, isScript: false, @@ -152,7 +163,7 @@ describe('buildFieldList', () => { }); it('uses aliases to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.isAlias)).toMatchObject({ isAlias: true, isScript: false, @@ -162,7 +173,7 @@ describe('buildFieldList', () => { }); it('supports scripted fields', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); expect(fields.find((f) => f.isScript)).toMatchObject({ isAlias: false, isScript: true, @@ -173,13 +184,24 @@ describe('buildFieldList', () => { }); }); + it('supports meta fields', () => { + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, ['_mymeta']); + expect(fields.find((f) => f.isMeta)).toMatchObject({ + isAlias: false, + isScript: false, + isMeta: true, + name: '_mymeta', + path: ['_mymeta'], + }); + }); + it('handles missing mappings', () => { - const fields = buildFieldList(indexPattern, {}, fieldDescriptors); + const fields = buildFieldList(indexPattern, {}, fieldDescriptors, []); expect(fields.every((f) => f.isAlias === false)).toEqual(true); }); it('handles empty fieldDescriptors by skipping multi-mappings', () => { - const fields = buildFieldList(indexPattern, mappings, []); + const fields = buildFieldList(indexPattern, mappings, [], []); expect(fields.find((f) => f.name === 'baz')).toMatchObject({ isAlias: false, isScript: false, diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 7ab3cdceb2145..33fcafacfad73 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -12,6 +12,7 @@ import { BASE_API_URL } from '../../common'; import { IndexPatternsFetcher, IndexPatternAttributes, + UI_SETTINGS, } from '../../../../../src/plugins/data/server'; /** @@ -36,13 +37,12 @@ export interface Field { name: string; isScript: boolean; isAlias: boolean; + isMeta: boolean; path: string[]; lang?: string; script?: string; } -const metaFields = ['_source', '_type']; - export async function existingFieldsRoute(setup: CoreSetup) { const router = setup.http.createRouter(); @@ -104,14 +104,15 @@ async function fetchFieldExistence({ toDate?: string; timeFieldName?: string; }) { + const metaFields: string[] = await context.core.uiSettings.client.get(UI_SETTINGS.META_FIELDS); const { indexPattern, indexPatternTitle, mappings, fieldDescriptors, - } = await fetchIndexPatternDefinition(indexPatternId, context); + } = await fetchIndexPatternDefinition(indexPatternId, context, metaFields); - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors); + const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, metaFields); const docs = await fetchIndexPatternStats({ fromDate, toDate, @@ -128,7 +129,11 @@ async function fetchFieldExistence({ }; } -async function fetchIndexPatternDefinition(indexPatternId: string, context: RequestHandlerContext) { +async function fetchIndexPatternDefinition( + indexPatternId: string, + context: RequestHandlerContext, + metaFields: string[] +) { const savedObjectsClient = context.core.savedObjects.client; const requestClient = context.core.elasticsearch.legacy.client; const indexPattern = await savedObjectsClient.get( @@ -178,7 +183,8 @@ async function fetchIndexPatternDefinition(indexPatternId: string, context: Requ export function buildFieldList( indexPattern: SavedObject, mappings: MappingResult | {}, - fieldDescriptors: FieldDescriptor[] + fieldDescriptors: FieldDescriptor[], + metaFields: string[] ): Field[] { const aliasMap = Object.entries(Object.values(mappings)[0]?.mappings.properties ?? {}) .map(([name, v]) => ({ ...v, name })) @@ -204,6 +210,9 @@ export function buildFieldList( path: path.split('.'), lang: field.lang, script: field.script, + // id is a special case - it doesn't show up in the meta field list, + // but as it's not part of source, it has to be handled separately. + isMeta: metaFields.includes(field.name) || field.name === '_id', }; } ); @@ -312,7 +321,7 @@ function exists(obj: unknown, path: string[], i = 0): boolean { * Exported only for unit tests. */ export function existingFields( - docs: Array<{ _source: unknown; fields: unknown }>, + docs: Array<{ _source: unknown; fields: unknown; [key: string]: unknown }>, fields: Field[] ): string[] { const missingFields = new Set(fields); @@ -323,7 +332,14 @@ export function existingFields( } missingFields.forEach((field) => { - if (exists(field.isScript ? doc.fields : doc._source, field.path)) { + let fieldStore = doc._source; + if (field.isScript) { + fieldStore = doc.fields; + } + if (field.isMeta) { + fieldStore = doc; + } + if (exists(fieldStore, field.path)) { missingFields.delete(field); } }); diff --git a/x-pack/plugins/lens/server/routes/telemetry.ts b/x-pack/plugins/lens/server/routes/telemetry.ts index 7925416ff5df2..06a7091104871 100644 --- a/x-pack/plugins/lens/server/routes/telemetry.ts +++ b/x-pack/plugins/lens/server/routes/telemetry.ts @@ -15,7 +15,7 @@ export async function initLensUsageRoute(setup: CoreSetup) { const router = setup.http.createRouter(); router.post( { - path: `${BASE_API_URL}/telemetry`, + path: `${BASE_API_URL}/stats`, validate: { body: schema.object({ events: schema.mapOf(schema.string(), schema.mapOf(schema.string(), schema.number())), diff --git a/x-pack/plugins/lens/server/usage/collectors.ts b/x-pack/plugins/lens/server/usage/collectors.ts index 3f033bd3b03d0..c32fc0371ed8a 100644 --- a/x-pack/plugins/lens/server/usage/collectors.ts +++ b/x-pack/plugins/lens/server/usage/collectors.ts @@ -10,6 +10,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { TaskManagerStartContract } from '../../../task_manager/server'; import { LensUsage, LensTelemetryState } from './types'; +import { lensUsageSchema } from './schema'; export function registerLensUsageCollector( usageCollection: UsageCollectionSetup, @@ -20,9 +21,9 @@ export function registerLensUsageCollector( // mark lensUsageCollector as ready to collect when the TaskManager is ready isCollectorReady = true; }); - const lensUsageCollector = usageCollection.makeUsageCollector({ + const lensUsageCollector = usageCollection.makeUsageCollector({ type: 'lens', - fetch: async (): Promise => { + async fetch() { try { const docs = await getLatestTaskState(await taskManager); // get the accumulated state from the recurring task @@ -55,6 +56,7 @@ export function registerLensUsageCollector( } }, isReady: () => isCollectorReady, + schema: lensUsageSchema, }); usageCollection.registerCollector(lensUsageCollector); diff --git a/x-pack/plugins/lens/server/usage/schema.ts b/x-pack/plugins/lens/server/usage/schema.ts new file mode 100644 index 0000000000000..a35d4d91845ee --- /dev/null +++ b/x-pack/plugins/lens/server/usage/schema.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { LensUsage } from './types'; + +const eventsSchema: MakeSchemaFrom = { + app_query_change: { type: 'long' }, + indexpattern_field_info_click: { type: 'long' }, + loaded: { type: 'long' }, + app_filters_updated: { type: 'long' }, + app_date_change: { type: 'long' }, + save_failed: { type: 'long' }, + loaded_404: { type: 'long' }, + drop_total: { type: 'long' }, + chart_switch: { type: 'long' }, + suggestion_confirmed: { type: 'long' }, + suggestion_clicked: { type: 'long' }, + drop_onto_workspace: { type: 'long' }, + drop_non_empty: { type: 'long' }, + drop_empty: { type: 'long' }, + indexpattern_changed: { type: 'long' }, + indexpattern_filters_cleared: { type: 'long' }, + indexpattern_type_filter_toggled: { type: 'long' }, + indexpattern_existence_toggled: { type: 'long' }, + indexpattern_show_all_fields_clicked: { type: 'long' }, + drop_onto_dimension: { type: 'long' }, + indexpattern_dimension_removed: { type: 'long' }, + indexpattern_dimension_field_changed: { type: 'long' }, + xy_change_layer_display: { type: 'long' }, + xy_layer_removed: { type: 'long' }, + xy_layer_added: { type: 'long' }, + indexpattern_dimension_operation_terms: { type: 'long' }, + indexpattern_dimension_operation_date_histogram: { type: 'long' }, + indexpattern_dimension_operation_avg: { type: 'long' }, + indexpattern_dimension_operation_min: { type: 'long' }, + indexpattern_dimension_operation_max: { type: 'long' }, + indexpattern_dimension_operation_sum: { type: 'long' }, + indexpattern_dimension_operation_count: { type: 'long' }, + indexpattern_dimension_operation_cardinality: { type: 'long' }, + indexpattern_dimension_operation_filters: { type: 'long' }, +}; + +const suggestionEventsSchema: MakeSchemaFrom = { + back_to_current: { type: 'long' }, + reload: { type: 'long' }, +}; + +const savedSchema: MakeSchemaFrom = { + bar: { type: 'long' }, + bar_horizontal: { type: 'long' }, + line: { type: 'long' }, + area: { type: 'long' }, + bar_stacked: { type: 'long' }, + bar_percentage_stacked: { type: 'long' }, + bar_horizontal_stacked: { type: 'long' }, + bar_horizontal_percentage_stacked: { type: 'long' }, + area_stacked: { type: 'long' }, + area_percentage_stacked: { type: 'long' }, + lnsDatatable: { type: 'long' }, + lnsPie: { type: 'long' }, + lnsMetric: { type: 'long' }, +}; + +export const lensUsageSchema: MakeSchemaFrom = { + // LensClickUsage + events_30_days: eventsSchema, + events_90_days: eventsSchema, + suggestion_events_30_days: suggestionEventsSchema, + suggestion_events_90_days: suggestionEventsSchema, + + // LensVisualizationUsage + saved_overall_total: { type: 'long' }, + saved_30_days_total: { type: 'long' }, + saved_90_days_total: { type: 'long' }, + + saved_overall: savedSchema, + saved_30_days: savedSchema, + saved_90_days: savedSchema, +}; diff --git a/x-pack/plugins/licensing/public/services/feature_usage_service.ts b/x-pack/plugins/licensing/public/services/feature_usage_service.ts index c076bff58359f..461246844a33b 100644 --- a/x-pack/plugins/licensing/public/services/feature_usage_service.ts +++ b/x-pack/plugins/licensing/public/services/feature_usage_service.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import isDate from 'lodash/isDate'; +import { isDate } from 'lodash'; import type { HttpSetup, HttpStart } from 'src/core/public'; import { LicenseType } from '../../common/types'; diff --git a/x-pack/plugins/licensing/tsconfig.json b/x-pack/plugins/licensing/tsconfig.json new file mode 100644 index 0000000000000..9b8eb15dc4a9e --- /dev/null +++ b/x-pack/plugins/licensing/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "public/**/*", + "server/**/*", + "common/**/*", + "../../../typings/**/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index d72d04d2a1843..be891b6e59608 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -37,6 +37,7 @@ export const FONTS_API_PATH = `${GIS_API_PATH}/fonts`; export const API_ROOT_PATH = `/${GIS_API_PATH}`; export const MVT_GETTILE_API_PATH = 'mvt/getTile'; +export const MVT_GETGRIDTILE_API_PATH = 'mvt/getGridTile'; export const MVT_SOURCE_LAYER_NAME = 'source_layer'; export const KBN_TOO_MANY_FEATURES_PROPERTY = '__kbn_too_many_features__'; export const KBN_TOO_MANY_FEATURES_IMAGE_ID = '__kbn_too_many_features_image_id__'; @@ -165,8 +166,13 @@ export enum GRID_RESOLUTION { COARSE = 'COARSE', FINE = 'FINE', MOST_FINE = 'MOST_FINE', + SUPER_FINE = 'SUPER_FINE', } +export const SUPER_FINE_ZOOM_DELTA = 7; // (2 ^ SUPER_FINE_ZOOM_DELTA) ^ 2 = number of cells in a given tile +export const GEOTILE_GRID_AGG_NAME = 'gridSplit'; +export const GEOCENTROID_AGG_NAME = 'gridCentroid'; + export const TOP_TERM_PERCENTAGE_SUFFIX = '__percentage'; export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLabel', { @@ -230,8 +236,6 @@ export enum SCALING_TYPES { MVT = 'MVT', } -export const RGBA_0000 = 'rgba(0,0,0,0)'; - export enum MVT_FIELD_TYPE { STRING = 'String', NUMBER = 'Number', diff --git a/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.d.ts b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.d.ts new file mode 100644 index 0000000000000..b1c1b181d8130 --- /dev/null +++ b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.d.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 { Feature } from 'geojson'; +import { RENDER_AS } from '../constants'; + +export function convertCompositeRespToGeoJson(esResponse: any, renderAs: RENDER_AS): Feature[]; +export function convertRegularRespToGeoJson(esResponse: any, renderAs: RENDER_AS): Feature[]; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.js similarity index 79% rename from x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js rename to x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.js index 35dbebdfd3c8a..a8f32bb4e7f5d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.js @@ -5,12 +5,12 @@ */ import _ from 'lodash'; -import { RENDER_AS } from '../../../../common/constants'; -import { getTileBoundingBox } from './geo_tile_utils'; -import { extractPropertiesFromBucket } from '../../util/es_agg_utils'; -import { clamp } from '../../../../common/elasticsearch_geo_utils'; +import { RENDER_AS, GEOTILE_GRID_AGG_NAME, GEOCENTROID_AGG_NAME } from '../constants'; +import { getTileBoundingBox } from '../geo_tile_utils'; +import { extractPropertiesFromBucket } from './es_agg_utils'; +import { clamp } from './elasticsearch_geo_utils'; -const GRID_BUCKET_KEYS_TO_IGNORE = ['key', 'gridCentroid']; +const GRID_BUCKET_KEYS_TO_IGNORE = ['key', GEOCENTROID_AGG_NAME]; export function convertCompositeRespToGeoJson(esResponse, renderAs) { return convertToGeoJson( @@ -20,7 +20,7 @@ export function convertCompositeRespToGeoJson(esResponse, renderAs) { return _.get(esResponse, 'aggregations.compositeSplit.buckets', []); }, (gridBucket) => { - return gridBucket.key.gridSplit; + return gridBucket.key[GEOTILE_GRID_AGG_NAME]; } ); } @@ -30,7 +30,7 @@ export function convertRegularRespToGeoJson(esResponse, renderAs) { esResponse, renderAs, (esResponse) => { - return _.get(esResponse, 'aggregations.gridSplit.buckets', []); + return _.get(esResponse, `aggregations.${GEOTILE_GRID_AGG_NAME}.buckets`, []); }, (gridBucket) => { return gridBucket.key; @@ -49,7 +49,7 @@ function convertToGeoJson(esResponse, renderAs, pluckGridBuckets, pluckGridKey) type: 'Feature', geometry: rowToGeometry({ gridKey, - gridCentroid: gridBucket.gridCentroid, + [GEOCENTROID_AGG_NAME]: gridBucket[GEOCENTROID_AGG_NAME], renderAs, }), id: gridKey, diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.test.ts b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.test.ts similarity index 97% rename from x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.test.ts rename to x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.test.ts index 523cc86915010..ee40a1f2fc751 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/convert_to_geojson.test.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/convert_to_geojson.test.ts @@ -4,11 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../kibana_services', () => {}); - // @ts-ignore import { convertCompositeRespToGeoJson, convertRegularRespToGeoJson } from './convert_to_geojson'; -import { RENDER_AS } from '../../../../common/constants'; +import { RENDER_AS } from '../constants'; describe('convertCompositeRespToGeoJson', () => { const esResponse = { diff --git a/x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts similarity index 89% rename from x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts rename to x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts index e57efca94d95e..cff8ba119e1de 100644 --- a/x-pack/plugins/maps/common/elasticsearch_geo_utils.d.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.d.ts @@ -5,8 +5,8 @@ */ import { FeatureCollection, GeoJsonProperties } from 'geojson'; -import { MapExtent } from './descriptor_types'; -import { ES_GEO_FIELD_TYPE } from './constants'; +import { MapExtent } from '../descriptor_types'; +import { ES_GEO_FIELD_TYPE } from '../constants'; export function scaleBounds(bounds: MapExtent, scaleFactor: number): MapExtent; diff --git a/x-pack/plugins/maps/common/elasticsearch_geo_utils.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js similarity index 98% rename from x-pack/plugins/maps/common/elasticsearch_geo_utils.js rename to x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js index f2bf83ae18bb0..be214e3b01e67 100644 --- a/x-pack/plugins/maps/common/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js @@ -15,9 +15,9 @@ import { POLYGON_COORDINATES_EXTERIOR_INDEX, LON_INDEX, LAT_INDEX, -} from '../common/constants'; -import { getEsSpatialRelationLabel } from './i18n_getters'; -import { FILTERS } from '../../../../src/plugins/data/common'; +} from '../constants'; +import { getEsSpatialRelationLabel } from '../i18n_getters'; +import { FILTERS } from '../../../../../src/plugins/data/common'; import turfCircle from '@turf/circle'; const SPATIAL_FILTER_TYPE = FILTERS.SPATIAL_FILTER; diff --git a/x-pack/plugins/maps/common/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js similarity index 100% rename from x-pack/plugins/maps/common/elasticsearch_geo_utils.test.js rename to x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js diff --git a/x-pack/plugins/maps/public/classes/util/es_agg_utils.test.ts b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.test.ts similarity index 100% rename from x-pack/plugins/maps/public/classes/util/es_agg_utils.test.ts rename to x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.test.ts diff --git a/x-pack/plugins/maps/public/classes/util/es_agg_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts similarity index 92% rename from x-pack/plugins/maps/public/classes/util/es_agg_utils.ts rename to x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts index 329a2a6fc64fb..7828c3cc6410b 100644 --- a/x-pack/plugins/maps/public/classes/util/es_agg_utils.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { IndexPattern, IFieldType } from '../../../../../../src/plugins/data/public'; -import { TOP_TERM_PERCENTAGE_SUFFIX } from '../../../common/constants'; +import { IndexPattern, IFieldType } from '../../../../../src/plugins/data/common'; +import { TOP_TERM_PERCENTAGE_SUFFIX } from '../constants'; export function getField(indexPattern: IndexPattern, fieldName: string) { const field = indexPattern.fields.getByName(fieldName); diff --git a/x-pack/plugins/maps/common/elasticsearch_util/index.ts b/x-pack/plugins/maps/common/elasticsearch_util/index.ts new file mode 100644 index 0000000000000..ffb4a542374fa --- /dev/null +++ b/x-pack/plugins/maps/common/elasticsearch_util/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 * from './es_agg_utils'; +export * from './convert_to_geojson'; +export * from './elasticsearch_geo_utils'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.test.js b/x-pack/plugins/maps/common/geo_tile_utils.test.js similarity index 96% rename from x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.test.js rename to x-pack/plugins/maps/common/geo_tile_utils.test.js index 88a6ce048a178..ae2623e168766 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.test.js +++ b/x-pack/plugins/maps/common/geo_tile_utils.test.js @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../kibana_services', () => {}); - import { parseTileKey, getTileBoundingBox, expandToTileBoundaries } from './geo_tile_utils'; it('Should parse tile key', () => { diff --git a/x-pack/plugins/maps/common/geo_tile_utils.ts b/x-pack/plugins/maps/common/geo_tile_utils.ts new file mode 100644 index 0000000000000..c6e35224458c5 --- /dev/null +++ b/x-pack/plugins/maps/common/geo_tile_utils.ts @@ -0,0 +1,157 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { DECIMAL_DEGREES_PRECISION } from './constants'; +import { clampToLatBounds } from './elasticsearch_util'; +import { MapExtent } from './descriptor_types'; + +const ZOOM_TILE_KEY_INDEX = 0; +const X_TILE_KEY_INDEX = 1; +const Y_TILE_KEY_INDEX = 2; + +function getTileCount(zoom: number): number { + return Math.pow(2, zoom); +} + +export interface ESBounds { + top_left: { + lon: number; + lat: number; + }; + bottom_right: { + lon: number; + lat: number; + }; +} + +export function parseTileKey( + tileKey: string +): { x: number; y: number; zoom: number; tileCount: number } { + const tileKeyParts = tileKey.split('/'); + + if (tileKeyParts.length !== 3) { + throw new Error(`Invalid tile key, expecting "zoom/x/y" format but got ${tileKey}`); + } + + const zoom = parseInt(tileKeyParts[ZOOM_TILE_KEY_INDEX], 10); + const x = parseInt(tileKeyParts[X_TILE_KEY_INDEX], 10); + const y = parseInt(tileKeyParts[Y_TILE_KEY_INDEX], 10); + const tileCount = getTileCount(zoom); + + if (x >= tileCount) { + throw new Error( + `Tile key is malformed, expected x to be less than ${tileCount}, you provided ${x}` + ); + } + if (y >= tileCount) { + throw new Error( + `Tile key is malformed, expected y to be less than ${tileCount}, you provided ${y}` + ); + } + + return { x, y, zoom, tileCount }; +} + +function sinh(x: number): number { + return (Math.exp(x) - Math.exp(-x)) / 2; +} + +// Calculate the minimum precision required to adequtely draw the box +// bounds. +// +// ceil(abs(log10(tileSize))) tells us how many decimals of precision +// are minimally required to represent the number after rounding. +// +// We add one extra decimal level of precision because, at high zoom +// levels rounding exactly can cause the boxes to render as uneven sizes +// (some will be slightly larger and some slightly smaller) +function precisionRounding(v: number, minPrecision: number, binSize: number): number { + let precision = Math.ceil(Math.abs(Math.log10(binSize))) + 1; + precision = Math.max(precision, minPrecision); + return _.round(v, precision); +} + +export function tile2long(x: number, z: number): number { + const tileCount = getTileCount(z); + return tileToLongitude(x, tileCount); +} + +export function tile2lat(y: number, z: number): number { + const tileCount = getTileCount(z); + return tileToLatitude(y, tileCount); +} + +export function tileToESBbox(x: number, y: number, z: number): ESBounds { + const wLon = tile2long(x, z); + const sLat = tile2lat(y + 1, z); + const eLon = tile2long(x + 1, z); + const nLat = tile2lat(y, z); + + return { + top_left: { + lon: wLon, + lat: nLat, + }, + bottom_right: { + lon: eLon, + lat: sLat, + }, + }; +} + +export function tileToLatitude(y: number, tileCount: number) { + const radians = Math.atan(sinh(Math.PI - (2 * Math.PI * y) / tileCount)); + const lat = (180 / Math.PI) * radians; + return precisionRounding(lat, DECIMAL_DEGREES_PRECISION, 180 / tileCount); +} + +export function tileToLongitude(x: number, tileCount: number) { + const lon = (x / tileCount) * 360 - 180; + return precisionRounding(lon, DECIMAL_DEGREES_PRECISION, 360 / tileCount); +} + +export function getTileBoundingBox(tileKey: string) { + const { x, y, tileCount } = parseTileKey(tileKey); + + return { + top: tileToLatitude(y, tileCount), + bottom: tileToLatitude(y + 1, tileCount), + left: tileToLongitude(x, tileCount), + right: tileToLongitude(x + 1, tileCount), + }; +} + +function sec(value: number): number { + return 1 / Math.cos(value); +} + +function latitudeToTile(lat: number, tileCount: number) { + const radians = (clampToLatBounds(lat) * Math.PI) / 180; + const y = ((1 - Math.log(Math.tan(radians) + sec(radians)) / Math.PI) / 2) * tileCount; + return Math.floor(y); +} + +function longitudeToTile(lon: number, tileCount: number) { + const x = ((lon + 180) / 360) * tileCount; + return Math.floor(x); +} + +export function expandToTileBoundaries(extent: MapExtent, zoom: number): MapExtent { + const tileCount = getTileCount(zoom); + + const upperLeftX = longitudeToTile(extent.minLon, tileCount); + const upperLeftY = latitudeToTile(Math.min(extent.maxLat, 90), tileCount); + const lowerRightX = longitudeToTile(extent.maxLon, tileCount); + const lowerRightY = latitudeToTile(Math.max(extent.minLat, -90), tileCount); + + return { + minLon: tileToLongitude(upperLeftX, tileCount), + minLat: tileToLatitude(lowerRightY + 1, tileCount), + maxLon: tileToLongitude(lowerRightX + 1, tileCount), + maxLat: tileToLatitude(upperLeftY, tileCount), + }; +} diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 2876f3d668a69..14d8196900506 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -40,7 +40,7 @@ import { ILayer } from '../classes/layers/layer'; import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer'; import { DataMeta, MapExtent, MapFilters } from '../../common/descriptor_types'; import { DataRequestAbortError } from '../classes/util/data_request'; -import { scaleBounds, turfBboxToBounds } from '../../common/elasticsearch_geo_utils'; +import { scaleBounds, turfBboxToBounds } from '../../common/elasticsearch_util'; import { IVectorStyle } from '../classes/styles/vector/vector_style'; const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1; diff --git a/x-pack/plugins/maps/public/actions/map_actions.ts b/x-pack/plugins/maps/public/actions/map_actions.ts index b00594cb7fb23..09491e5c3a7b3 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.ts @@ -54,7 +54,7 @@ import { MapRefreshConfig, } from '../../common/descriptor_types'; import { INITIAL_LOCATION } from '../../common/constants'; -import { scaleBounds } from '../../common/elasticsearch_geo_utils'; +import { scaleBounds } from '../../common/elasticsearch_util'; export function setMapInitError(errorMessage: string) { return { diff --git a/x-pack/plugins/maps/public/classes/_index.scss b/x-pack/plugins/maps/public/classes/_index.scss index 29a5761255278..3fc31b4b2f66a 100644 --- a/x-pack/plugins/maps/public/classes/_index.scss +++ b/x-pack/plugins/maps/public/classes/_index.scss @@ -1 +1,2 @@ @import 'styles/index'; +@import 'layers/index'; diff --git a/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts index 7b184819b839b..8cff98205186f 100644 --- a/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/es_agg_field.ts @@ -12,7 +12,7 @@ import { IVectorSource } from '../sources/vector_source'; import { ESDocField } from './es_doc_field'; import { AGG_TYPE, FIELD_ORIGIN } from '../../../common/constants'; import { isMetricCountable } from '../util/is_metric_countable'; -import { getField, addFieldToDSL } from '../util/es_agg_utils'; +import { getField, addFieldToDSL } from '../../../common/elasticsearch_util'; import { TopTermPercentageField } from './top_term_percentage_field'; import { ITooltipProperty, TooltipProperty } from '../tooltips/tooltip_property'; import { ESAggTooltipProperty } from '../tooltips/es_agg_tooltip_property'; @@ -30,6 +30,7 @@ export class ESAggField implements IESAggField { private readonly _label?: string; private readonly _aggType: AGG_TYPE; private readonly _esDocField?: IField | undefined; + private readonly _canReadFromGeoJson: boolean; constructor({ label, @@ -37,18 +38,21 @@ export class ESAggField implements IESAggField { aggType, esDocField, origin, + canReadFromGeoJson = true, }: { label?: string; source: IESAggSource; aggType: AGG_TYPE; esDocField?: IField; origin: FIELD_ORIGIN; + canReadFromGeoJson?: boolean; }) { this._source = source; this._origin = origin; this._label = label; this._aggType = aggType; this._esDocField = esDocField; + this._canReadFromGeoJson = canReadFromGeoJson; } getSource(): IVectorSource { @@ -132,18 +136,19 @@ export class ESAggField implements IESAggField { } supportsAutoDomain(): boolean { - return true; + return this._canReadFromGeoJson ? true : this.supportsFieldMeta(); } canReadFromGeoJson(): boolean { - return true; + return this._canReadFromGeoJson; } } export function esAggFieldsFactory( aggDescriptor: AggDescriptor, source: IESAggSource, - origin: FIELD_ORIGIN + origin: FIELD_ORIGIN, + canReadFromGeoJson: boolean = true ): IESAggField[] { const aggField = new ESAggField({ label: aggDescriptor.label, @@ -153,12 +158,13 @@ export function esAggFieldsFactory( aggType: aggDescriptor.type, source, origin, + canReadFromGeoJson, }); const aggFields: IESAggField[] = [aggField]; if (aggDescriptor.field && aggDescriptor.type === AGG_TYPE.TERMS) { - aggFields.push(new TopTermPercentageField(aggField)); + aggFields.push(new TopTermPercentageField(aggField, canReadFromGeoJson)); } return aggFields; diff --git a/x-pack/plugins/maps/public/classes/fields/field.ts b/x-pack/plugins/maps/public/classes/fields/field.ts index 2c190d54f0265..658c2bba87847 100644 --- a/x-pack/plugins/maps/public/classes/fields/field.ts +++ b/x-pack/plugins/maps/public/classes/fields/field.ts @@ -21,12 +21,12 @@ export interface IField { getOrdinalFieldMetaRequest(): Promise; getCategoricalFieldMetaRequest(size: number): Promise; - // Determines whether Maps-app can automatically determine the domain of the field-values + // Whether Maps-app can automatically determine the domain of the field-values // if this is not the case (e.g. for .mvt tiled data), // then styling properties that require the domain to be known cannot use this property. supportsAutoDomain(): boolean; - // Determinse wheter Maps-app can automatically deterime the domain of the field-values + // Whether Maps-app can automatically determine the domain of the field-values // _without_ having to retrieve the data as GeoJson // e.g. for ES-sources, this would use the extended_stats API supportsFieldMeta(): boolean; diff --git a/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts b/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts index fc931b13619ef..50db04d08b2aa 100644 --- a/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/top_term_percentage_field.ts @@ -6,16 +6,17 @@ import { IESAggField } from './es_agg_field'; import { IVectorSource } from '../sources/vector_source'; -// @ts-ignore import { ITooltipProperty, TooltipProperty } from '../tooltips/tooltip_property'; import { TOP_TERM_PERCENTAGE_SUFFIX } from '../../../common/constants'; import { FIELD_ORIGIN } from '../../../common/constants'; export class TopTermPercentageField implements IESAggField { private readonly _topTermAggField: IESAggField; + private readonly _canReadFromGeoJson: boolean; - constructor(topTermAggField: IESAggField) { + constructor(topTermAggField: IESAggField, canReadFromGeoJson: boolean = true) { this._topTermAggField = topTermAggField; + this._canReadFromGeoJson = canReadFromGeoJson; } getSource(): IVectorSource { @@ -61,7 +62,7 @@ export class TopTermPercentageField implements IESAggField { } supportsAutoDomain(): boolean { - return true; + return this._canReadFromGeoJson; } supportsFieldMeta(): boolean { @@ -81,6 +82,6 @@ export class TopTermPercentageField implements IESAggField { } canReadFromGeoJson(): boolean { - return true; + return this._canReadFromGeoJson; } } diff --git a/x-pack/plugins/maps/public/classes/layers/_index.scss b/x-pack/plugins/maps/public/classes/layers/_index.scss new file mode 100644 index 0000000000000..530ac2734855d --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/_index.scss @@ -0,0 +1 @@ +@import 'layers'; diff --git a/x-pack/plugins/maps/public/classes/layers/_layers.scss b/x-pack/plugins/maps/public/classes/layers/_layers.scss new file mode 100644 index 0000000000000..54ab7d85ef170 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/_layers.scss @@ -0,0 +1,11 @@ +.mapLayersWizardIcon { + margin-top: $euiSizeS; + + &__highlight { + fill: $euiColorFullShade; + } + + &__background { + fill: $euiColorLightShade; + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx index 6e806f4530df2..d87302a6a9f2e 100644 --- a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/choropleth_layer_wizard.tsx @@ -9,13 +9,14 @@ import { i18n } from '@kbn/i18n'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; import { LayerWizard, RenderWizardArguments } from '../layer_wizard_registry'; import { LayerTemplate } from './layer_template'; +import { ChoroplethLayerIcon } from './cloropleth_layer_icon'; export const choroplethLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH], description: i18n.translate('xpack.maps.choropleth.desc', { defaultMessage: 'Shaded areas to compare statistics across boundaries', }), - icon: 'logoElasticsearch', + icon: ChoroplethLayerIcon, renderWizard: (renderWizardArguments: RenderWizardArguments) => { return ; }, diff --git a/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/cloropleth_layer_icon.tsx b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/cloropleth_layer_icon.tsx new file mode 100644 index 0000000000000..e0a0d450dfbff --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/choropleth_layer_wizard/cloropleth_layer_icon.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, { FunctionComponent } from 'react'; + +export const ChoroplethLayerIcon: FunctionComponent = () => ( + + + + + + +); diff --git a/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts new file mode 100644 index 0000000000000..8bf078806cfbc --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/create_region_map_layer_descriptor.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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/v4'; +import { + AggDescriptor, + ColorDynamicOptions, + LayerDescriptor, +} from '../../../common/descriptor_types'; +import { + AGG_TYPE, + COLOR_MAP_TYPE, + FIELD_ORIGIN, + SOURCE_TYPES, + STYLE_TYPE, + VECTOR_STYLES, +} from '../../../common/constants'; +import { VectorStyle } from '../styles/vector/vector_style'; +import { EMSFileSource } from '../sources/ems_file_source'; +// @ts-ignore +import { ESGeoGridSource } from '../sources/es_geo_grid_source'; +import { VectorLayer } from './vector_layer/vector_layer'; +import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults'; +import { NUMERICAL_COLOR_PALETTES } from '../styles/color_palettes'; +import { getJoinAggKey } from '../../../common/get_agg_key'; + +const defaultDynamicProperties = getDefaultDynamicProperties(); + +export function createAggDescriptor(metricAgg: string, metricFieldName?: string): AggDescriptor { + const aggTypeKey = Object.keys(AGG_TYPE).find((key) => { + return AGG_TYPE[key as keyof typeof AGG_TYPE] === metricAgg; + }); + const aggType = aggTypeKey ? AGG_TYPE[aggTypeKey as keyof typeof AGG_TYPE] : undefined; + + return aggType && metricFieldName + ? { type: aggType, field: metricFieldName } + : { type: AGG_TYPE.COUNT }; +} + +export function createRegionMapLayerDescriptor({ + label, + emsLayerId, + leftFieldName, + termsFieldName, + colorSchema, + indexPatternId, + indexPatternTitle, + metricAgg, + metricFieldName, +}: { + label: string; + emsLayerId?: string; + leftFieldName?: string; + termsFieldName?: string; + colorSchema: string; + indexPatternId?: string; + indexPatternTitle?: string; + metricAgg: string; + metricFieldName?: string; +}): LayerDescriptor | null { + if (!indexPatternId || !emsLayerId || !leftFieldName || !termsFieldName) { + return null; + } + + const metricsDescriptor = createAggDescriptor(metricAgg, metricFieldName); + const joinId = uuid(); + const joinKey = getJoinAggKey({ + aggType: metricsDescriptor.type, + aggFieldName: metricsDescriptor.field ? metricsDescriptor.field : '', + rightSourceId: joinId, + }); + const colorPallette = NUMERICAL_COLOR_PALETTES.find((pallette) => { + return pallette.value.toLowerCase() === colorSchema.toLowerCase(); + }); + return VectorLayer.createDescriptor({ + label, + joins: [ + { + leftField: leftFieldName, + right: { + type: SOURCE_TYPES.ES_TERM_SOURCE, + id: joinId, + indexPatternId, + indexPatternTitle: indexPatternTitle ? indexPatternTitle : indexPatternId, + term: termsFieldName, + metrics: [metricsDescriptor], + }, + }, + ], + sourceDescriptor: EMSFileSource.createDescriptor({ + id: emsLayerId, + tooltipProperties: ['name', leftFieldName], + }), + style: VectorStyle.createDescriptor({ + [VECTOR_STYLES.FILL_COLOR]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + field: { + name: joinKey, + origin: FIELD_ORIGIN.JOIN, + }, + color: colorPallette ? colorPallette.value : 'Yellow to Red', + type: COLOR_MAP_TYPE.ORDINAL, + fieldMetaOptions: { + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions) + .fieldMetaOptions, + isEnabled: false, + }, + }, + }, + }), + }); +} diff --git a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.test.ts new file mode 100644 index 0000000000000..18e5f462bb310 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.test.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 { createAggDescriptor } from './create_tile_map_layer_descriptor'; + +describe('createAggDescriptor', () => { + test('Should allow supported metric aggs', () => { + expect(createAggDescriptor('Scaled Circle Markers', 'sum', 'bytes')).toEqual({ + type: 'sum', + field: 'bytes', + }); + }); + + test('Should fallback to count when field not provided', () => { + expect(createAggDescriptor('Scaled Circle Markers', 'sum', undefined)).toEqual({ + type: 'count', + }); + }); + + test('Should fallback to count when metric agg is not supported in maps', () => { + expect(createAggDescriptor('Scaled Circle Markers', 'top_hits', 'bytes')).toEqual({ + type: 'count', + }); + }); + + describe('heatmap', () => { + test('Should allow countable metric aggs', () => { + expect(createAggDescriptor('Heatmap', 'sum', 'bytes')).toEqual({ + type: 'sum', + field: 'bytes', + }); + }); + + test('Should fallback to count for non-countable metric aggs', () => { + expect(createAggDescriptor('Heatmap', 'avg', 'bytes')).toEqual({ + type: 'count', + }); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts new file mode 100644 index 0000000000000..05a8620e436d5 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/create_tile_map_layer_descriptor.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + AggDescriptor, + ColorDynamicOptions, + LayerDescriptor, + SizeDynamicOptions, + VectorStylePropertiesDescriptor, +} from '../../../common/descriptor_types'; +import { + AGG_TYPE, + COLOR_MAP_TYPE, + FIELD_ORIGIN, + GRID_RESOLUTION, + RENDER_AS, + STYLE_TYPE, + VECTOR_STYLES, +} from '../../../common/constants'; +import { VectorStyle } from '../styles/vector/vector_style'; +// @ts-ignore +import { ESGeoGridSource } from '../sources/es_geo_grid_source'; +import { VectorLayer } from './vector_layer/vector_layer'; +// @ts-ignore +import { HeatmapLayer } from './heatmap_layer/heatmap_layer'; +import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults'; +import { NUMERICAL_COLOR_PALETTES } from '../styles/color_palettes'; +import { getSourceAggKey } from '../../../common/get_agg_key'; +import { isMetricCountable } from '../util/is_metric_countable'; + +const defaultDynamicProperties = getDefaultDynamicProperties(); + +function isHeatmap(mapType: string): boolean { + return mapType.toLowerCase() === 'heatmap'; +} + +function getGeoGridRequestType(mapType: string): RENDER_AS { + if (isHeatmap(mapType)) { + return RENDER_AS.HEATMAP; + } + + if (mapType.toLowerCase() === 'shaded geohash grid') { + return RENDER_AS.GRID; + } + + return RENDER_AS.POINT; +} + +export function createAggDescriptor( + mapType: string, + metricAgg: string, + metricFieldName?: string +): AggDescriptor { + const aggTypeKey = Object.keys(AGG_TYPE).find((key) => { + return AGG_TYPE[key as keyof typeof AGG_TYPE] === metricAgg; + }); + const aggType = aggTypeKey ? AGG_TYPE[aggTypeKey as keyof typeof AGG_TYPE] : undefined; + + return aggType && metricFieldName && (!isHeatmap(mapType) || isMetricCountable(aggType)) + ? { type: aggType, field: metricFieldName } + : { type: AGG_TYPE.COUNT }; +} + +export function createTileMapLayerDescriptor({ + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, +}: { + label: string; + mapType: string; + colorSchema: string; + indexPatternId?: string; + geoFieldName?: string; + metricAgg: string; + metricFieldName?: string; +}): LayerDescriptor | null { + if (!indexPatternId || !geoFieldName) { + return null; + } + + const metricsDescriptor = createAggDescriptor(mapType, metricAgg, metricFieldName); + const geoGridSourceDescriptor = ESGeoGridSource.createDescriptor({ + indexPatternId, + geoField: geoFieldName, + metrics: [metricsDescriptor], + requestType: getGeoGridRequestType(mapType), + resolution: GRID_RESOLUTION.MOST_FINE, + }); + + if (isHeatmap(mapType)) { + return HeatmapLayer.createDescriptor({ + label, + sourceDescriptor: geoGridSourceDescriptor, + }); + } + + const metricSourceKey = getSourceAggKey({ + aggType: metricsDescriptor.type, + aggFieldName: metricsDescriptor.field, + }); + const metricStyleField = { + name: metricSourceKey, + origin: FIELD_ORIGIN.SOURCE, + }; + + const colorPallette = NUMERICAL_COLOR_PALETTES.find((pallette) => { + return pallette.value.toLowerCase() === colorSchema.toLowerCase(); + }); + const styleProperties: VectorStylePropertiesDescriptor = { + [VECTOR_STYLES.FILL_COLOR]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions), + field: metricStyleField, + color: colorPallette ? colorPallette.value : 'Yellow to Red', + type: COLOR_MAP_TYPE.ORDINAL, + fieldMetaOptions: { + ...(defaultDynamicProperties[VECTOR_STYLES.FILL_COLOR]!.options as ColorDynamicOptions) + .fieldMetaOptions, + isEnabled: false, + }, + }, + }, + [VECTOR_STYLES.LINE_COLOR]: { + type: STYLE_TYPE.STATIC, + options: { + color: '#3d3d3d', + }, + }, + }; + if (mapType.toLowerCase() === 'scaled circle markers') { + styleProperties[VECTOR_STYLES.ICON_SIZE] = { + type: STYLE_TYPE.DYNAMIC, + options: { + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions), + maxSize: 18, + field: metricStyleField, + fieldMetaOptions: { + ...(defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE]!.options as SizeDynamicOptions) + .fieldMetaOptions, + isEnabled: false, + }, + }, + }; + } + + return VectorLayer.createDescriptor({ + label, + sourceDescriptor: geoGridSourceDescriptor, + style: VectorStyle.createDescriptor(styleProperties), + }); +} diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 8026f48fe6093..cd720063c6703 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -423,7 +423,7 @@ export class AbstractLayer implements ILayer { renderSourceSettingsEditor({ onChange }: SourceEditorArgs) { const source = this.getSourceForEditing(); - return source.renderSourceSettingsEditor({ onChange }); + return source.renderSourceSettingsEditor({ onChange, currentLayerType: this._descriptor.type }); } getPrevRequestToken(dataId: string): symbol | undefined { diff --git a/x-pack/plugins/maps/public/classes/layers/layer_wizard_registry.ts b/x-pack/plugins/maps/public/classes/layers/layer_wizard_registry.ts index 0eb1d2c3b222c..278a3c0388b01 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer_wizard_registry.ts +++ b/x-pack/plugins/maps/public/classes/layers/layer_wizard_registry.ts @@ -5,7 +5,7 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { ReactElement } from 'react'; +import { ReactElement, FunctionComponent } from 'react'; import { LayerDescriptor } from '../../../common/descriptor_types'; import { LAYER_WIZARD_CATEGORY } from '../../../common/constants'; @@ -28,7 +28,7 @@ export type LayerWizard = { categories: LAYER_WIZARD_CATEGORY[]; checkVisibility?: () => Promise; description: string; - icon: string; + icon: string | FunctionComponent; prerequisiteSteps?: Array<{ id: string; label: string }>; renderWizard(renderWizardArguments: RenderWizardArguments): ReactElement; title: string; diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts index 85601cfc17e8f..bdd86d78b5300 100644 --- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts +++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts @@ -29,7 +29,6 @@ import { OBSERVABILITY_LAYER_TYPE } from './layer_select'; import { OBSERVABILITY_METRIC_TYPE } from './metric_select'; import { DISPLAY } from './display_select'; import { VectorStyle } from '../../../styles/vector/vector_style'; -// @ts-ignore import { EMSFileSource } from '../../../sources/ems_file_source'; // @ts-ignore import { ESGeoGridSource } from '../../../sources/es_geo_grid_source'; diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx index 5f73a9e23431b..38e13a68437c7 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx @@ -72,9 +72,7 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc async getEMSFileLayer(): Promise { const emsFileLayers = await getEmsFileLayers(); - const emsFileLayer = emsFileLayers.find( - (fileLayer) => fileLayer.getId() === this._descriptor.id - ); + const emsFileLayer = emsFileLayers.find((fileLayer) => fileLayer.hasId(this._descriptor.id)); if (!emsFileLayer) { throw new Error( i18n.translate('xpack.maps.source.emsFile.unableToFindIdErrorMessage', { diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index a9c886617d3af..be947d79f4e39 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -31,14 +31,25 @@ export interface IESAggSource extends IESSource { export class AbstractESAggSource extends AbstractESSource { private readonly _metricFields: IESAggField[]; + private readonly _canReadFromGeoJson: boolean; - constructor(descriptor: AbstractESAggSourceDescriptor, inspectorAdapters: Adapters) { + constructor( + descriptor: AbstractESAggSourceDescriptor, + inspectorAdapters: Adapters, + canReadFromGeoJson = true + ) { super(descriptor, inspectorAdapters); this._metricFields = []; + this._canReadFromGeoJson = canReadFromGeoJson; if (descriptor.metrics) { descriptor.metrics.forEach((aggDescriptor: AggDescriptor) => { this._metricFields.push( - ...esAggFieldsFactory(aggDescriptor, this, this.getOriginForField()) + ...esAggFieldsFactory( + aggDescriptor, + this, + this.getOriginForField(), + this._canReadFromGeoJson + ) ); }); } @@ -72,7 +83,12 @@ export class AbstractESAggSource extends AbstractESSource { const metrics = this._metricFields.filter((esAggField) => esAggField.isValid()); // Handle case where metrics is empty because older saved object state is empty array or there are no valid aggs. return metrics.length === 0 - ? esAggFieldsFactory({ type: AGG_TYPE.COUNT }, this, this.getOriginForField()) + ? esAggFieldsFactory( + { type: AGG_TYPE.COUNT }, + this, + this.getOriginForField(), + this._canReadFromGeoJson + ) : metrics; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap new file mode 100644 index 0000000000000..ca9775594a9d7 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/resolution_editor.test.tsx.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`resolution editor should add super-fine option 1`] = ` + + + +`; + +exports[`resolution editor should omit super-fine option 1`] = ` + + + +`; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap new file mode 100644 index 0000000000000..dfce6b36396a7 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -0,0 +1,121 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`source editor geo_grid_source default vector layer config should allow super-fine option 1`] = ` + + + +
+ +
+
+ + +
+ + + +
+ +
+
+ + + +
+ +
+`; + +exports[`source editor geo_grid_source should put limitations based on heatmap-rendering selection should not allow super-fine option for heatmaps and should not allow multiple metrics 1`] = ` + + + +
+ +
+
+ + +
+ + + +
+ +
+
+ + + +
+ +
+`; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_icon.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_icon.tsx new file mode 100644 index 0000000000000..818ff789da19b --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_icon.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, { FunctionComponent } from 'react'; + +export const ClustersLayerIcon: FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx index ee97fdd0a2bf6..5d0a414cd0d18 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/clusters_layer_wizard.tsx @@ -29,13 +29,14 @@ import { STYLE_TYPE, } from '../../../../common/constants'; import { NUMERICAL_COLOR_PALETTES } from '../../styles/color_palettes'; +import { ClustersLayerIcon } from './clusters_layer_icon'; export const clustersLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH], description: i18n.translate('xpack.maps.source.esGridClustersDescription', { defaultMessage: 'Geospatial data grouped in grids with metrics for each gridded cell', }), - icon: 'logoElasticsearch', + icon: ClustersLayerIcon, renderWizard: ({ previewLayers }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: Partial) => { if (!sourceConfig) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts index 2ce4353fca13c..ada76b8e4e674 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts @@ -5,11 +5,17 @@ */ import { AbstractESAggSource } from '../es_agg_source'; -import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types'; +import { + ESGeoGridSourceDescriptor, + MapFilters, + MapQuery, + VectorSourceSyncMeta, +} from '../../../../common/descriptor_types'; import { GRID_RESOLUTION } from '../../../../common/constants'; import { IField } from '../../fields/field'; +import { ITiledSingleLayerVectorSource } from '../vector_source'; -export class ESGeoGridSource extends AbstractESAggSource { +export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingleLayerVectorSource { static createDescriptor({ indexPatternId, geoField, @@ -19,8 +25,27 @@ export class ESGeoGridSource extends AbstractESAggSource { constructor(sourceDescriptor: ESGeoGridSourceDescriptor, inspectorAdapters: unknown); + readonly _descriptor: ESGeoGridSourceDescriptor; + getFieldNames(): string[]; getGridResolution(): GRID_RESOLUTION; getGeoGridPrecision(zoom: number): number; createField({ fieldName }: { fieldName: string }): IField; + + getLayerName(): string; + + getUrlTemplateWithMeta( + searchFilters: MapFilters & { + applyGlobalQuery: boolean; + fieldNames: string[]; + geogridPrecision?: number; + sourceQuery: MapQuery; + sourceMeta: VectorSourceSyncMeta; + } + ): Promise<{ + layerName: string; + urlTemplate: string; + minSourceZoom: number; + maxSourceZoom: number; + }>; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js index aa167cb577672..89258f04612fd 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js @@ -7,7 +7,11 @@ import React from 'react'; import uuid from 'uuid/v4'; -import { convertCompositeRespToGeoJson, convertRegularRespToGeoJson } from './convert_to_geojson'; +import { + convertCompositeRespToGeoJson, + convertRegularRespToGeoJson, + makeESBbox, +} from '../../../../common/elasticsearch_util'; import { UpdateSourceEditor } from './update_source_editor'; import { SOURCE_TYPES, @@ -15,13 +19,20 @@ import { RENDER_AS, GRID_RESOLUTION, VECTOR_SHAPE_TYPE, + MVT_SOURCE_LAYER_NAME, + GIS_API_PATH, + MVT_GETGRIDTILE_API_PATH, + GEOTILE_GRID_AGG_NAME, + GEOCENTROID_AGG_NAME, } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source'; import { DataRequestAbortError } from '../../util/data_request'; import { registerSource } from '../source_registry'; -import { makeESBbox } from '../../../../common/elasticsearch_geo_utils'; + +import rison from 'rison-node'; +import { getHttp } from '../../../kibana_services'; export const MAX_GEOTILE_LEVEL = 29; @@ -48,9 +59,14 @@ export class ESGeoGridSource extends AbstractESAggSource { }; } - renderSourceSettingsEditor({ onChange }) { + constructor(descriptor, inspectorAdapters) { + super(descriptor, inspectorAdapters, descriptor.resolution !== GRID_RESOLUTION.SUPER_FINE); + } + + renderSourceSettingsEditor({ onChange, currentLayerType }) { return ( { @@ -96,59 +99,67 @@ describe('ESGeoGridSource', () => { }; }; - describe('getGeoJsonWithMeta', () => { - let mockSearchSource: unknown; - beforeEach(async () => { - mockSearchSource = new MockSearchSource(); - const mockSearchService = { - searchSource: { - async create() { - return mockSearchSource as SearchSource; - }, - createEmpty() { - return mockSearchSource as SearchSource; - }, + let mockSearchSource: unknown; + beforeEach(async () => { + mockSearchSource = new MockSearchSource(); + const mockSearchService = { + searchSource: { + async create() { + return mockSearchSource as SearchSource; }, - }; + createEmpty() { + return mockSearchSource as SearchSource; + }, + }, + }; - // @ts-expect-error - getIndexPatternService.mockReturnValue(mockIndexPatternService); - // @ts-expect-error - getSearchService.mockReturnValue(mockSearchService); + // @ts-expect-error + getIndexPatternService.mockReturnValue(mockIndexPatternService); + // @ts-expect-error + getSearchService.mockReturnValue(mockSearchService); + // @ts-expect-error + getHttp.mockReturnValue({ + basePath: { + prepend(path: string) { + return `rootdir${path};`; + }, + }, }); + }); - const extent: MapExtent = { - minLon: -160, - minLat: -80, - maxLon: 160, - maxLat: 80, - }; + const extent: MapExtent = { + minLon: -160, + minLat: -80, + maxLon: 160, + maxLat: 80, + }; - const mapFilters: VectorSourceRequestMeta = { - geogridPrecision: 4, - filters: [], - timeFilters: { - from: 'now', - to: '15m', - mode: 'relative', - }, - extent, - applyGlobalQuery: true, - fieldNames: [], - buffer: extent, - sourceQuery: { - query: '', - language: 'KQL', - queryLastTriggeredAt: '2019-04-25T20:53:22.331Z', - }, - sourceMeta: null, - zoom: 0, - }; + const vectorSourceRequestMeta: VectorSourceRequestMeta = { + geogridPrecision: 4, + filters: [], + timeFilters: { + from: 'now', + to: '15m', + mode: 'relative', + }, + extent, + applyGlobalQuery: true, + fieldNames: [], + buffer: extent, + sourceQuery: { + query: '', + language: 'KQL', + queryLastTriggeredAt: '2019-04-25T20:53:22.331Z', + }, + sourceMeta: null, + zoom: 0, + }; + describe('getGeoJsonWithMeta', () => { it('Should configure the SearchSource correctly', async () => { const { data, meta } = await geogridSource.getGeoJsonWithMeta( 'foobarLayer', - mapFilters, + vectorSourceRequestMeta, () => {} ); @@ -215,5 +226,48 @@ describe('ESGeoGridSource', () => { it('should use heuristic to derive precision', () => { expect(geogridSource.getGeoGridPrecision(10)).toBe(12); }); + + it('Should not return valid precision for super-fine resolution', () => { + const superFineSource = new ESGeoGridSource( + { + id: 'foobar', + indexPatternId: 'fooIp', + geoField: geoFieldName, + metrics: [], + resolution: GRID_RESOLUTION.SUPER_FINE, + type: SOURCE_TYPES.ES_GEO_GRID, + requestType: RENDER_AS.HEATMAP, + }, + {} + ); + expect(superFineSource.getGeoGridPrecision(10)).toBe(NaN); + }); + }); + + describe('ITiledSingleLayerVectorSource', () => { + it('getLayerName', () => { + expect(geogridSource.getLayerName()).toBe('source_layer'); + }); + + it('getMinZoom', () => { + expect(geogridSource.getMinZoom()).toBe(0); + }); + + it('getMaxZoom', () => { + expect(geogridSource.getMaxZoom()).toBe(24); + }); + + it('getUrlTemplateWithMeta', async () => { + const urlTemplateWithMeta = await geogridSource.getUrlTemplateWithMeta( + vectorSourceRequestMeta + ); + + expect(urlTemplateWithMeta.layerName).toBe('source_layer'); + expect(urlTemplateWithMeta.minSourceZoom).toBe(0); + expect(urlTemplateWithMeta.maxSourceZoom).toBe(24); + expect(urlTemplateWithMeta.urlTemplate).toBe( + "rootdir/api/maps/mvt/getGridTile;?x={x}&y={y}&z={z}&geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!((geo_bounding_box:(bar:(bottom_right:!(180,-82.67628),top_left:!(-180,82.67628)))))),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point" + ); + }); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js deleted file mode 100644 index 89b24522e4275..0000000000000 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/geo_tile_utils.js +++ /dev/null @@ -1,115 +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 { DECIMAL_DEGREES_PRECISION } from '../../../../common/constants'; -import { clampToLatBounds } from '../../../../common/elasticsearch_geo_utils'; - -const ZOOM_TILE_KEY_INDEX = 0; -const X_TILE_KEY_INDEX = 1; -const Y_TILE_KEY_INDEX = 2; - -function getTileCount(zoom) { - return Math.pow(2, zoom); -} - -export function parseTileKey(tileKey) { - const tileKeyParts = tileKey.split('/'); - - if (tileKeyParts.length !== 3) { - throw new Error(`Invalid tile key, expecting "zoom/x/y" format but got ${tileKey}`); - } - - const zoom = parseInt(tileKeyParts[ZOOM_TILE_KEY_INDEX], 10); - const x = parseInt(tileKeyParts[X_TILE_KEY_INDEX], 10); - const y = parseInt(tileKeyParts[Y_TILE_KEY_INDEX], 10); - const tileCount = getTileCount(zoom); - - if (x >= tileCount) { - throw new Error( - `Tile key is malformed, expected x to be less than ${tileCount}, you provided ${x}` - ); - } - if (y >= tileCount) { - throw new Error( - `Tile key is malformed, expected y to be less than ${tileCount}, you provided ${y}` - ); - } - - return { x, y, zoom, tileCount }; -} - -function sinh(x) { - return (Math.exp(x) - Math.exp(-x)) / 2; -} - -// Calculate the minimum precision required to adequtely draw the box -// bounds. -// -// ceil(abs(log10(tileSize))) tells us how many decimals of precision -// are minimally required to represent the number after rounding. -// -// We add one extra decimal level of precision because, at high zoom -// levels rounding exactly can cause the boxes to render as uneven sizes -// (some will be slightly larger and some slightly smaller) -function precisionRounding(v, minPrecision, binSize) { - let precision = Math.ceil(Math.abs(Math.log10(binSize))) + 1; - precision = Math.max(precision, minPrecision); - return _.round(v, precision); -} - -function tileToLatitude(y, tileCount) { - const radians = Math.atan(sinh(Math.PI - (2 * Math.PI * y) / tileCount)); - const lat = (180 / Math.PI) * radians; - return precisionRounding(lat, DECIMAL_DEGREES_PRECISION, 180 / tileCount); -} - -function tileToLongitude(x, tileCount) { - const lon = (x / tileCount) * 360 - 180; - return precisionRounding(lon, DECIMAL_DEGREES_PRECISION, 360 / tileCount); -} - -export function getTileBoundingBox(tileKey) { - const { x, y, tileCount } = parseTileKey(tileKey); - - return { - top: tileToLatitude(y, tileCount), - bottom: tileToLatitude(y + 1, tileCount), - left: tileToLongitude(x, tileCount), - right: tileToLongitude(x + 1, tileCount), - }; -} - -function sec(value) { - return 1 / Math.cos(value); -} - -function latitudeToTile(lat, tileCount) { - const radians = (clampToLatBounds(lat) * Math.PI) / 180; - const y = ((1 - Math.log(Math.tan(radians) + sec(radians)) / Math.PI) / 2) * tileCount; - return Math.floor(y); -} - -function longitudeToTile(lon, tileCount) { - const x = ((lon + 180) / 360) * tileCount; - return Math.floor(x); -} - -export function expandToTileBoundaries(extent, zoom) { - const tileCount = getTileCount(zoom); - - const upperLeftX = longitudeToTile(extent.minLon, tileCount); - const upperLeftY = latitudeToTile(Math.min(extent.maxLat, 90), tileCount); - const lowerRightX = longitudeToTile(extent.maxLon, tileCount); - const lowerRightY = latitudeToTile(Math.max(extent.minLat, -90), tileCount); - - return { - minLon: tileToLongitude(upperLeftX, tileCount), - minLat: tileToLatitude(lowerRightY + 1, tileCount), - maxLon: tileToLongitude(lowerRightX + 1, tileCount), - maxLat: tileToLatitude(upperLeftY, tileCount), - }; -} diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_icon.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_icon.tsx new file mode 100644 index 0000000000000..5f4835e386911 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_icon.tsx @@ -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 React, { FunctionComponent } from 'react'; + +export const HeatmapLayerIcon: FunctionComponent = () => ( + + + + + +); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_wizard.tsx index 92a0f1006ea43..652514a3b9d34 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/heatmap_layer_wizard.tsx @@ -15,13 +15,14 @@ import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_re import { HeatmapLayer } from '../../layers/heatmap_layer/heatmap_layer'; import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types'; import { LAYER_WIZARD_CATEGORY, RENDER_AS } from '../../../../common/constants'; +import { HeatmapLayerIcon } from './heatmap_layer_icon'; export const heatmapLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH], description: i18n.translate('xpack.maps.source.esGridHeatmapDescription', { defaultMessage: 'Geospatial data grouped in grids to show density', }), - icon: 'logoElasticsearch', + icon: HeatmapLayerIcon, renderWizard: ({ previewLayers }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: Partial) => { if (!sourceConfig) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.js index 28c24f58a0efc..71133cb25280c 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.js @@ -9,7 +9,7 @@ import { GRID_RESOLUTION } from '../../../../common/constants'; import { EuiSelect, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -const OPTIONS = [ +const BASE_OPTIONS = [ { value: GRID_RESOLUTION.COARSE, text: i18n.translate('xpack.maps.source.esGrid.coarseDropdownOption', { @@ -30,7 +30,18 @@ const OPTIONS = [ }, ]; -export function ResolutionEditor({ resolution, onChange }) { +export function ResolutionEditor({ resolution, onChange, includeSuperFine }) { + const options = [...BASE_OPTIONS]; + + if (includeSuperFine) { + options.push({ + value: GRID_RESOLUTION.SUPER_FINE, + text: i18n.translate('xpack.maps.source.esGrid.superFineDropDownOption', { + defaultMessage: 'super fine (beta)', + }), + }); + } + return ( onChange(e.target.value)} compressed diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.test.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.test.tsx new file mode 100644 index 0000000000000..369203dbe16c0 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/resolution_editor.test.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 { shallow } from 'enzyme'; + +// @ts-expect-error +import { ResolutionEditor } from './resolution_editor'; +import { GRID_RESOLUTION } from '../../../../common/constants'; + +const defaultProps = { + resolution: GRID_RESOLUTION.COARSE, + onChange: () => {}, + includeSuperFine: false, +}; + +describe('resolution editor', () => { + test('should omit super-fine option', () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + test('should add super-fine option', () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.js index ac7d809c40f61..7e885c291b952 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.js @@ -6,7 +6,7 @@ import React, { Fragment, Component } from 'react'; -import { RENDER_AS } from '../../../../common/constants'; +import { GRID_RESOLUTION, LAYER_TYPE } from '../../../../common/constants'; import { MetricsEditor } from '../../../components/metrics_editor'; import { getIndexPatternService } from '../../../kibana_services'; import { ResolutionEditor } from './resolution_editor'; @@ -62,8 +62,25 @@ export class UpdateSourceEditor extends Component { this.props.onChange({ propName: 'metrics', value: metrics }); }; - _onResolutionChange = (e) => { - this.props.onChange({ propName: 'resolution', value: e }); + _onResolutionChange = (resolution) => { + let newLayerType; + if ( + this.props.currentLayerType === LAYER_TYPE.VECTOR || + this.props.currentLayerType === LAYER_TYPE.TILED_VECTOR + ) { + newLayerType = + resolution === GRID_RESOLUTION.SUPER_FINE ? LAYER_TYPE.TILED_VECTOR : LAYER_TYPE.VECTOR; + } else if (this.props.currentLayerType === LAYER_TYPE.HEATMAP) { + if (resolution === GRID_RESOLUTION.SUPER_FINE) { + throw new Error('Heatmap does not support SUPER_FINE resolution'); + } else { + newLayerType = LAYER_TYPE.HEATMAP; + } + } else { + throw new Error('Unexpected layer-type'); + } + + this.props.onChange({ propName: 'resolution', value: resolution, newLayerType }); }; _onRequestTypeSelect = (requestType) => { @@ -72,13 +89,13 @@ export class UpdateSourceEditor extends Component { _renderMetricsPanel() { const metricsFilter = - this.props.renderAs === RENDER_AS.HEATMAP + this.props.currentLayerType === LAYER_TYPE.HEATMAP ? (metric) => { //these are countable metrics, where blending heatmap color blobs make sense return isMetricCountable(metric.value); } : null; - const allowMultipleMetrics = this.props.renderAs !== RENDER_AS.HEATMAP; + const allowMultipleMetrics = this.props.currentLayerType !== LAYER_TYPE.HEATMAP; return ( @@ -115,6 +132,7 @@ export class UpdateSourceEditor extends Component { diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.tsx new file mode 100644 index 0000000000000..ceb79230bc832 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.test.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 React from 'react'; +import { shallow } from 'enzyme'; + +// @ts-expect-error +import { UpdateSourceEditor } from './update_source_editor'; +import { GRID_RESOLUTION, LAYER_TYPE, RENDER_AS } from '../../../../common/constants'; + +const defaultProps = { + currentLayerType: LAYER_TYPE.VECTOR, + indexPatternId: 'foobar', + onChange: () => {}, + metrics: [], + renderAs: RENDER_AS.POINT, + resolution: GRID_RESOLUTION.COARSE, +}; + +describe('source editor geo_grid_source', () => { + describe('default vector layer config', () => { + test('should allow super-fine option', async () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); + }); + + describe('should put limitations based on heatmap-rendering selection', () => { + test('should not allow super-fine option for heatmaps and should not allow multiple metrics', async () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/convert_to_lines.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/convert_to_lines.js index 96a7f50cdf523..24ac6d31bc645 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/convert_to_lines.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/convert_to_lines.js @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import { extractPropertiesFromBucket } from '../../util/es_agg_utils'; +import { extractPropertiesFromBucket } from '../../../../common/elasticsearch_util'; const LAT_INDEX = 0; const LON_INDEX = 1; diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js index 9ec54335d4e78..0360208ef8370 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.js @@ -16,7 +16,7 @@ import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { convertToLines } from './convert_to_lines'; import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source'; import { registerSource } from '../source_registry'; -import { turfBboxToBounds } from '../../../../common/elasticsearch_geo_utils'; +import { turfBboxToBounds } from '../../../../common/elasticsearch_util'; import { DataRequestAbortError } from '../../util/data_request'; const MAX_GEOTILE_LEVEL = 29; diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_icon.tsx b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_icon.tsx new file mode 100644 index 0000000000000..ed5d38bba0fba --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_icon.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; + +export const Point2PointLayerIcon: FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx index fee84d0208978..74e690d4d3204 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/point_2_point_layer_wizard.tsx @@ -23,13 +23,14 @@ import { NUMERICAL_COLOR_PALETTES } from '../../styles/color_palettes'; import { CreateSourceEditor } from './create_source_editor'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { ColorDynamicOptions, SizeDynamicOptions } from '../../../../common/descriptor_types'; +import { Point2PointLayerIcon } from './point_2_point_layer_icon'; export const point2PointLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.ELASTICSEARCH], description: i18n.translate('xpack.maps.source.pewPewDescription', { defaultMessage: 'Aggregated data paths between the source and destination', }), - icon: 'logoElasticsearch', + icon: Point2PointLayerIcon, renderWizard: ({ previewLayers }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: unknown) => { if (!sourceConfig) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_icon.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_icon.tsx new file mode 100644 index 0000000000000..dcd4985f44280 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_icon.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; + +export const EsDocumentsLayerIcon: FunctionComponent = () => ( + + + + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx index 249b9a2454d7d..af2061d6c541f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_documents_layer_wizard.tsx @@ -15,6 +15,7 @@ import { BlendedVectorLayer } from '../../layers/blended_vector_layer/blended_ve import { VectorLayer } from '../../layers/vector_layer/vector_layer'; import { LAYER_WIZARD_CATEGORY, SCALING_TYPES } from '../../../../common/constants'; import { TiledVectorLayer } from '../../layers/tiled_vector_layer/tiled_vector_layer'; +import { EsDocumentsLayerIcon } from './es_documents_layer_icon'; export function createDefaultLayerDescriptor(sourceConfig: unknown, mapColors: string[]) { const sourceDescriptor = ESSearchSource.createDescriptor(sourceConfig); @@ -33,7 +34,7 @@ export const esDocumentsLayerWizardConfig: LayerWizard = { description: i18n.translate('xpack.maps.source.esSearchDescription', { defaultMessage: 'Points, lines, and polygons from Elasticsearch', }), - icon: 'logoElasticsearch', + icon: EsDocumentsLayerIcon, renderWizard: ({ previewLayers, mapColors }: RenderWizardArguments) => { const onSourceConfigChange = (sourceConfig: unknown) => { if (!sourceConfig) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js index df83bd1cf5e60..edcafae54d54c 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.js @@ -10,7 +10,7 @@ import rison from 'rison-node'; import { AbstractESSource } from '../es_source'; import { getSearchService, getHttp } from '../../../kibana_services'; -import { hitsToGeoJson } from '../../../../common/elasticsearch_geo_utils'; +import { hitsToGeoJson, getField, addFieldToDSL } from '../../../../common/elasticsearch_util'; import { UpdateSourceEditor } from './update_source_editor'; import { SOURCE_TYPES, @@ -31,7 +31,7 @@ import uuid from 'uuid/v4'; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants'; import { ESDocField } from '../../fields/es_doc_field'; -import { getField, addFieldToDSL } from '../../util/es_agg_utils'; + import { registerSource } from '../source_registry'; export const sourceTitle = i18n.translate('xpack.maps.source.esSearchTitle', { diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js index d51ca46fd98ff..ab56ceeab4e77 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js @@ -11,14 +11,14 @@ import { getTimeFilter, getSearchService, } from '../../../kibana_services'; -import { createExtentFilter } from '../../../../common/elasticsearch_geo_utils'; +import { createExtentFilter } from '../../../../common/elasticsearch_util'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; import { copyPersistentState } from '../../../reducers/util'; import { DataRequestAbortError } from '../../util/data_request'; -import { expandToTileBoundaries } from '../es_geo_grid_source/geo_tile_utils'; +import { expandToTileBoundaries } from '../../../../common/geo_tile_utils'; import { search } from '../../../../../../../src/plugins/data/public'; export class AbstractESSource extends AbstractVectorSource { diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js index b4ad256c1598a..359d22d2c44ce 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js @@ -16,7 +16,11 @@ import { import { getJoinAggKey } from '../../../../common/get_agg_key'; import { ESDocField } from '../../fields/es_doc_field'; import { AbstractESAggSource } from '../es_agg_source'; -import { getField, addFieldToDSL, extractPropertiesFromBucket } from '../../util/es_agg_utils'; +import { + getField, + addFieldToDSL, + extractPropertiesFromBucket, +} from '../../../../common/elasticsearch_util'; const TERMS_AGG_NAME = 'join'; diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index 7e7a7bd8f049d..946381817b8fc 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -18,6 +18,7 @@ import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view' export type SourceEditorArgs = { onChange: (...args: OnSourceChangeArgs[]) => void; + currentLayerType?: string; }; export type ImmutableSourceProperty = { @@ -50,7 +51,7 @@ export interface ISource { getImmutableProperties(): Promise; getAttributions(): Promise; isESSource(): boolean; - renderSourceSettingsEditor({ onChange }: SourceEditorArgs): ReactElement | null; + renderSourceSettingsEditor(sourceEditorArgs: SourceEditorArgs): ReactElement | null; supportsFitToBounds(): Promise; showJoinEditor(): boolean; getJoinsDisabledReason(): string | null; @@ -126,7 +127,7 @@ export class AbstractSource implements ISource { return []; } - renderSourceSettingsEditor({ onChange }: SourceEditorArgs): ReactElement | null { + renderSourceSettingsEditor(sourceEditorArgs: SourceEditorArgs): ReactElement | null { return null; } diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx index a300225178526..c75698805225f 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/heatmap_style.tsx @@ -86,6 +86,7 @@ export class HeatmapStyle implements IStyle { } else if (resolution === GRID_RESOLUTION.MOST_FINE) { radius = 32; } else { + // SUPER_FINE or any other is not supported. const errorMessage = i18n.translate('xpack.maps.style.heatmap.resolutionStyleErrorMessage', { defaultMessage: `Resolution param not recognized: {resolution}`, values: { resolution }, diff --git a/x-pack/plugins/maps/public/classes/styles/vector/style_util.ts b/x-pack/plugins/maps/public/classes/styles/vector/style_util.ts index d190a62e6f300..49d6ccdeb9316 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/style_util.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/style_util.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { MB_LOOKUP_FUNCTION, VECTOR_SHAPE_TYPE } from '../../../../common/constants'; +import { MB_LOOKUP_FUNCTION, VECTOR_SHAPE_TYPE, VECTOR_STYLES } from '../../../../common/constants'; import { Category } from '../../../../common/descriptor_types'; export function getOtherCategoryLabel() { @@ -14,8 +14,8 @@ export function getOtherCategoryLabel() { }); } -export function getComputedFieldName(styleName: string, fieldName: string) { - return `${getComputedFieldNamePrefix(fieldName)}__${styleName}`; +export function getComputedFieldName(styleName: VECTOR_STYLES, fieldName: string) { + return `${getComputedFieldNamePrefix(fieldName)}__${styleName as string}`; } export function getComputedFieldNamePrefix(fieldName: string) { diff --git a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js index 98267965fd30f..33a0f1c5bf088 100644 --- a/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import { URL_MAX_LENGTH } from '../../../../../../../src/core/public'; -import { createSpatialFilterWithGeometry } from '../../../../common/elasticsearch_geo_utils'; +import { createSpatialFilterWithGeometry } from '../../../../common/elasticsearch_util'; import { GEO_JSON_TYPE } from '../../../../common/constants'; import { GeometryFilterForm } from '../../../components/geometry_filter_form'; diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index 49675ac6a3924..0356a8267c18a 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -15,7 +15,7 @@ import { createSpatialFilterWithGeometry, getBoundingBoxGeometry, roundCoordinates, -} from '../../../../../common/elasticsearch_geo_utils'; +} from '../../../../../common/elasticsearch_util'; import { DrawTooltip } from './draw_tooltip'; const DRAW_RECTANGLE = 'draw_rectangle'; diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index eede1edf40cc4..ddc48cfc9c329 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -23,7 +23,7 @@ import sprites1 from '@elastic/maki/dist/sprite@1.png'; import sprites2 from '@elastic/maki/dist/sprite@2.png'; import { DrawControl } from './draw_control'; import { TooltipControl } from './tooltip_control'; -import { clampToLatBounds, clampToLonBounds } from '../../../../common/elasticsearch_geo_utils'; +import { clampToLatBounds, clampToLonBounds } from '../../../../common/elasticsearch_util'; import { getInitialView } from './get_initial_view'; import { getPreserveDrawingBuffer } from '../../../kibana_services'; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts index 03752a1c3e11e..9bced75b613d7 100644 --- a/x-pack/plugins/maps/public/lazy_load_bundle/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/index.ts @@ -48,6 +48,44 @@ interface LazyLoadedMapModules { registerLayerWizard: (layerWizard: LayerWizard) => void; registerSource(entry: SourceRegistryEntry): void; getIndexPatternsFromIds: (indexPatternIds: string[]) => Promise; + createTileMapLayerDescriptor: ({ + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, + }: { + label: string; + mapType: string; + colorSchema: string; + indexPatternId?: string; + geoFieldName?: string; + metricAgg: string; + metricFieldName?: string; + }) => LayerDescriptor | null; + createRegionMapLayerDescriptor: ({ + label, + emsLayerId, + leftFieldName, + termsFieldName, + colorSchema, + indexPatternId, + indexPatternTitle, + metricAgg, + metricFieldName, + }: { + label: string; + emsLayerId?: string; + leftFieldName?: string; + termsFieldName?: string; + colorSchema: string; + indexPatternId?: string; + indexPatternTitle?: string; + metricAgg: string; + metricFieldName?: string; + }) => LayerDescriptor | null; } export async function lazyLoadMapModules(): Promise { @@ -72,6 +110,8 @@ export async function lazyLoadMapModules(): Promise { registerLayerWizard, registerSource, getIndexPatternsFromIds, + createTileMapLayerDescriptor, + createRegionMapLayerDescriptor, } = await import('./lazy'); resolve({ @@ -90,6 +130,8 @@ export async function lazyLoadMapModules(): Promise { registerLayerWizard, registerSource, getIndexPatternsFromIds, + createTileMapLayerDescriptor, + createRegionMapLayerDescriptor, }); }); return loadModulesPromise; diff --git a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts index 28f5acdc17656..782d645dc230a 100644 --- a/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts +++ b/x-pack/plugins/maps/public/lazy_load_bundle/lazy/index.ts @@ -20,3 +20,5 @@ export * from '../../classes/layers/solution_layers/security'; export { registerLayerWizard } from '../../classes/layers/layer_wizard_registry'; export { registerSource } from '../../classes/sources/source_registry'; export { getIndexPatternsFromIds } from '../../index_pattern_util'; +export { createTileMapLayerDescriptor } from '../../classes/layers/create_tile_map_layer_descriptor'; +export { createRegionMapLayerDescriptor } from '../../classes/layers/create_region_map_layer_descriptor'; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 8f49598cf2a8d..696964f0258d4 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -32,7 +32,11 @@ import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public'; -import { createMapsUrlGenerator } from './url_generator'; +import { + createMapsUrlGenerator, + createRegionMapUrlGenerator, + createTileMapUrlGenerator, +} from './url_generator'; import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action'; import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; @@ -97,15 +101,18 @@ export class MapsPlugin setKibanaCommonConfig(plugins.mapsLegacy.config); setMapAppConfig(config); setKibanaVersion(this._initializerContext.env.packageInfo.version); - plugins.share.urlGenerators.registerUrlGenerator( - createMapsUrlGenerator(async () => { - const [coreStart] = await core.getStartServices(); - return { - appBasePath: coreStart.application.getUrlForApp('maps'), - useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), - }; - }) - ); + + // register url generators + const getStartServices = async () => { + const [coreStart] = await core.getStartServices(); + return { + appBasePath: coreStart.application.getUrlForApp('maps'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + }; + }; + plugins.share.urlGenerators.registerUrlGenerator(createMapsUrlGenerator(getStartServices)); + plugins.share.urlGenerators.registerUrlGenerator(createTileMapUrlGenerator(getStartServices)); + plugins.share.urlGenerators.registerUrlGenerator(createRegionMapUrlGenerator(getStartServices)); plugins.inspector.registerView(MapView); if (plugins.home) { diff --git a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx index 47f41f2b76f3e..8a0eb8db4d7aa 100644 --- a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx @@ -67,17 +67,17 @@ export function getTopNavConfig({ savedMap.description = newDescription; savedMap.copyOnSave = newCopyOnSave; - let id; + let savedObjectId; try { savedMap.syncWithStore(); - id = await savedMap.save({ + savedObjectId = await savedMap.save({ confirmOverwrite: false, isTitleDuplicateConfirmed, onTitleDuplicate, }); // id not returned when save fails because of duplicate title check. // return and let user confirm duplicate title. - if (!id) { + if (!savedObjectId) { return {}; } } catch (err) { @@ -105,7 +105,7 @@ export function getTopNavConfig({ getCoreChrome().docTitle.change(savedMap.title); setBreadcrumbs(); - goToSpecifiedPath(`/map/${id}${window.location.hash}`); + goToSpecifiedPath(`/map/${savedObjectId}${window.location.hash}`); const newlyCreated = newCopyOnSave || isNewMap; if (newlyCreated && !returnToOrigin) { @@ -113,14 +113,14 @@ export function getTopNavConfig({ } else if (!!originatingApp && returnToOrigin) { if (newlyCreated && stateTransfer) { stateTransfer.navigateToWithEmbeddablePackage(originatingApp, { - state: { id, type: MAP_SAVED_OBJECT_TYPE }, + state: { input: { savedObjectId }, type: MAP_SAVED_OBJECT_TYPE }, }); } else { getNavigateToApp()(originatingApp); } } - return { id }; + return { id: savedObjectId }; } if (hasSaveAndReturnConfig) { diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 03e0f753812c9..db4371e9cd590 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -31,7 +31,7 @@ import { SPATIAL_FILTERS_LAYER_ID, } from '../../common/constants'; // @ts-ignore -import { extractFeaturesFromFilters } from '../../common/elasticsearch_geo_utils'; +import { extractFeaturesFromFilters } from '../../common/elasticsearch_util'; import { MapStoreState } from '../reducers/store'; import { DataRequestDescriptor, diff --git a/x-pack/plugins/maps/public/url_generator.test.ts b/x-pack/plugins/maps/public/url_generator.test.ts index a44f8d952fde1..880d5a5e03b43 100644 --- a/x-pack/plugins/maps/public/url_generator.test.ts +++ b/x-pack/plugins/maps/public/url_generator.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import rison from 'rison-node'; + import { createMapsUrlGenerator } from './url_generator'; import { LAYER_TYPE, SOURCE_TYPES, SCALING_TYPES } from '../common/constants'; import { esFilters } from '../../../../src/plugins/data/public'; @@ -63,12 +63,11 @@ describe('visualize url generator', () => { }, }, ]; - const encodedLayers = rison.encode_array(initialLayers); const url = await generator.createUrl!({ initialLayers, }); expect(url).toMatchInlineSnapshot( - `"test/app/maps/map#/?_g=()&_a=()&initialLayers=${encodedLayers}"` + `"test/app/maps/map#/?_g=()&_a=()&initialLayers=(id%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CsourceDescriptor%3A(geoField%3Atest%2Cid%3A'13823000-99b9-11ea-9eb6-d9e8adceb647'%2CindexPatternId%3A'90943e30-9a47-11e8-b64d-95841ca0b247'%2Clabel%3A'Sample%20Data'%2CscalingType%3ALIMIT%2CtooltipProperties%3A!()%2Ctype%3AES_SEARCH)%2Ctype%3AVECTOR%2Cvisible%3A!t)"` ); }); diff --git a/x-pack/plugins/maps/public/url_generator.ts b/x-pack/plugins/maps/public/url_generator.ts index 3fbb361342c7a..7f7f3f2c60327 100644 --- a/x-pack/plugins/maps/public/url_generator.ts +++ b/x-pack/plugins/maps/public/url_generator.ts @@ -16,11 +16,14 @@ import { setStateToKbnUrl } from '../../../../src/plugins/kibana_utils/public'; import { UrlGeneratorsDefinition } from '../../../../src/plugins/share/public'; import { LayerDescriptor } from '../common/descriptor_types'; import { INITIAL_LAYERS_KEY } from '../common/constants'; +import { lazyLoadMapModules } from './lazy_load_bundle'; const STATE_STORAGE_KEY = '_a'; const GLOBAL_STATE_STORAGE_KEY = '_g'; export const MAPS_APP_URL_GENERATOR = 'MAPS_APP_URL_GENERATOR'; +export const MAPS_APP_TILE_MAP_URL_GENERATOR = 'MAPS_APP_TILE_MAP_URL_GENERATOR'; +export const MAPS_APP_REGION_MAP_URL_GENERATOR = 'MAPS_APP_REGION_MAP_URL_GENERATOR'; export interface MapsUrlGeneratorState { /** @@ -59,51 +62,175 @@ export interface MapsUrlGeneratorState { hash?: boolean; } +type GetStartServices = () => Promise<{ + appBasePath: string; + useHashedUrl: boolean; +}>; + +async function createMapUrl({ + getStartServices, + mapId, + filters, + query, + refreshInterval, + timeRange, + initialLayers, + hash, +}: MapsUrlGeneratorState & { getStartServices: GetStartServices }): Promise { + const startServices = await getStartServices(); + const useHash = hash ?? startServices.useHashedUrl; + const appBasePath = startServices.appBasePath; + + const appState: { + query?: Query; + filters?: Filter[]; + vis?: unknown; + } = {}; + const queryState: QueryState = {}; + + if (query) appState.query = query; + if (filters && filters.length) + appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f)); + + if (timeRange) queryState.time = timeRange; + if (filters && filters.length) + queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f)); + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + let url = `${appBasePath}/map#/${mapId || ''}`; + url = setStateToKbnUrl(GLOBAL_STATE_STORAGE_KEY, queryState, { useHash }, url); + url = setStateToKbnUrl(STATE_STORAGE_KEY, appState, { useHash }, url); + + if (initialLayers && initialLayers.length) { + // @ts-ignore + const risonEncodedInitialLayers = rison.encode_array(initialLayers); + url = `${url}&${INITIAL_LAYERS_KEY}=${encodeURIComponent(risonEncodedInitialLayers)}`; + } + + return url; +} + export const createMapsUrlGenerator = ( - getStartServices: () => Promise<{ - appBasePath: string; - useHashedUrl: boolean; - }> + getStartServices: GetStartServices ): UrlGeneratorsDefinition => ({ id: MAPS_APP_URL_GENERATOR, + createUrl: async (mapsUrlGeneratorState: MapsUrlGeneratorState): Promise => { + return createMapUrl({ ...mapsUrlGeneratorState, getStartServices }); + }, +}); + +export const createTileMapUrlGenerator = ( + getStartServices: GetStartServices +): UrlGeneratorsDefinition => ({ + id: MAPS_APP_TILE_MAP_URL_GENERATOR, + createUrl: async ({ + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, + filters, + query, + timeRange, + hash, + }: { + label: string; + mapType: string; + colorSchema: string; + indexPatternId?: string; + geoFieldName?: string; + metricAgg: string; + metricFieldName?: string; + timeRange?: TimeRange; + filters?: Filter[]; + query?: Query; + hash?: boolean; + }): Promise => { + const mapModules = await lazyLoadMapModules(); + const initialLayers = []; + const tileMapLayerDescriptor = mapModules.createTileMapLayerDescriptor({ + label, + mapType, + colorSchema, + indexPatternId, + geoFieldName, + metricAgg, + metricFieldName, + }); + if (tileMapLayerDescriptor) { + initialLayers.push(tileMapLayerDescriptor); + } + + return createMapUrl({ + initialLayers, + filters, + query, + timeRange, + hash: true, + getStartServices, + }); + }, +}); + +export const createRegionMapUrlGenerator = ( + getStartServices: GetStartServices +): UrlGeneratorsDefinition => ({ + id: MAPS_APP_REGION_MAP_URL_GENERATOR, createUrl: async ({ - mapId, + label, + emsLayerId, + leftFieldName, + termsFieldName, + colorSchema, + indexPatternId, + indexPatternTitle, + metricAgg, + metricFieldName, filters, query, - refreshInterval, timeRange, - initialLayers, hash, - }: MapsUrlGeneratorState): Promise => { - const startServices = await getStartServices(); - const useHash = hash ?? startServices.useHashedUrl; - const appBasePath = startServices.appBasePath; - - const appState: { - query?: Query; - filters?: Filter[]; - vis?: unknown; - } = {}; - const queryState: QueryState = {}; - - if (query) appState.query = query; - if (filters && filters.length) - appState.filters = filters?.filter((f) => !esFilters.isFilterPinned(f)); - - if (timeRange) queryState.time = timeRange; - if (filters && filters.length) - queryState.filters = filters?.filter((f) => esFilters.isFilterPinned(f)); - if (refreshInterval) queryState.refreshInterval = refreshInterval; - - let url = `${appBasePath}/map#/${mapId || ''}`; - url = setStateToKbnUrl(GLOBAL_STATE_STORAGE_KEY, queryState, { useHash }, url); - url = setStateToKbnUrl(STATE_STORAGE_KEY, appState, { useHash }, url); - - if (initialLayers && initialLayers.length) { - // @ts-ignore - url = `${url}&${INITIAL_LAYERS_KEY}=${rison.encode_array(initialLayers)}`; + }: { + label: string; + emsLayerId?: string; + leftFieldName?: string; + termsFieldName?: string; + colorSchema: string; + indexPatternId?: string; + indexPatternTitle?: string; + metricAgg: string; + metricFieldName?: string; + timeRange?: TimeRange; + filters?: Filter[]; + query?: Query; + hash?: boolean; + }): Promise => { + const mapModules = await lazyLoadMapModules(); + const initialLayers = []; + const regionMapLayerDescriptor = mapModules.createRegionMapLayerDescriptor({ + label, + emsLayerId, + leftFieldName, + termsFieldName, + colorSchema, + indexPatternId, + indexPatternTitle, + metricAgg, + metricFieldName, + }); + if (regionMapLayerDescriptor) { + initialLayers.push(regionMapLayerDescriptor); } - return url; + return createMapUrl({ + initialLayers, + filters, + query, + timeRange, + hash: true, + getStartServices, + }); }, }); diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 8688bbe549f51..2af6413da039b 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -6,11 +6,12 @@ import _ from 'lodash'; import { + SavedObject, SavedObjectAttribute, SavedObjectAttributes, SavedObjectsClientContract, } from 'kibana/server'; -import { IFieldType, IIndexPattern } from 'src/plugins/data/public'; +import { IFieldType, IndexPatternAttributes } from 'src/plugins/data/public'; import { ES_GEO_FIELD_TYPE, LAYER_TYPE, @@ -64,7 +65,9 @@ function getUniqueLayerCounts(layerCountsList: ILayerTypeCount[], mapsCount: num }, {}); } -function getIndexPatternsWithGeoFieldCount(indexPatterns: IIndexPattern[]) { +function getIndexPatternsWithGeoFieldCount( + indexPatterns: Array> +) { const fieldLists = indexPatterns.map((indexPattern) => indexPattern.attributes && indexPattern.attributes.fields ? JSON.parse(indexPattern.attributes.fields) @@ -112,7 +115,7 @@ function getEMSLayerCount(layerLists: LayerDescriptor[][]): ILayerTypeCount[] { } function isFieldGeoShape( - indexPatterns: IIndexPattern[], + indexPatterns: Array>, indexPatternId: string, geoField: string | undefined ): boolean { @@ -120,9 +123,11 @@ function isFieldGeoShape( return false; } - const matchIndexPattern = indexPatterns.find((indexPattern: IIndexPattern) => { - return indexPattern.id === indexPatternId; - }); + const matchIndexPattern = indexPatterns.find( + (indexPattern: SavedObject) => { + return indexPattern.id === indexPatternId; + } + ); if (!matchIndexPattern) { return false; @@ -140,7 +145,10 @@ function isFieldGeoShape( return !!matchField && matchField.type === ES_GEO_FIELD_TYPE.GEO_SHAPE; } -function isGeoShapeAggLayer(indexPatterns: IIndexPattern[], layer: LayerDescriptor): boolean { +function isGeoShapeAggLayer( + indexPatterns: Array>, + layer: LayerDescriptor +): boolean { if (layer.sourceDescriptor === null) { return false; } @@ -176,7 +184,7 @@ function isGeoShapeAggLayer(indexPatterns: IIndexPattern[], layer: LayerDescript function getGeoShapeAggCount( layerLists: LayerDescriptor[][], - indexPatterns: IIndexPattern[] + indexPatterns: Array> ): number { const countsPerMap: number[] = layerLists.map((layerList: LayerDescriptor[]) => { const geoShapeAggLayers = layerList.filter((layerDescriptor) => { @@ -204,7 +212,7 @@ export function buildMapsTelemetry({ settings, }: { mapSavedObjects: MapSavedObject[]; - indexPatternSavedObjects: IIndexPattern[]; + indexPatternSavedObjects: Array>; settings: SavedObjectAttribute; }): SavedObjectAttributes { const layerLists: LayerDescriptor[][] = getLayerLists(mapSavedObjects); @@ -283,10 +291,12 @@ export async function getMapsTelemetry(config: MapsConfigType) { const savedObjectsClient = getInternalRepository(); // @ts-ignore const mapSavedObjects: MapSavedObject[] = await getMapSavedObjects(savedObjectsClient); - const indexPatternSavedObjects: IIndexPattern[] = (await getIndexPatternSavedObjects( + const indexPatternSavedObjects: Array> = (await getIndexPatternSavedObjects( // @ts-ignore savedObjectsClient - )) as IIndexPattern[]; + )) as Array>; const settings: SavedObjectAttribute = { showMapVisualizationTypes: config.showMapVisualizationTypes, }; diff --git a/x-pack/plugins/maps/server/mvt/__tests__/json/0_0_0_gridagg.json b/x-pack/plugins/maps/server/mvt/__tests__/json/0_0_0_gridagg.json new file mode 100644 index 0000000000000..0945dc57fa512 --- /dev/null +++ b/x-pack/plugins/maps/server/mvt/__tests__/json/0_0_0_gridagg.json @@ -0,0 +1 @@ +{"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":10000,"relation":"gte"},"max_score":null,"hits":[]},"aggregations":{"gridSplit":{"buckets":[{"key":"7/37/48","doc_count":42637,"avg_of_TOTAL_AV":{"value":5398920.390458991},"gridCentroid":{"location":{"lat":40.77936432658204,"lon":-73.96795676049909},"count":42637}}]}}} diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_docs.pbf similarity index 100% rename from x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0.pbf rename to x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_docs.pbf diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf new file mode 100644 index 0000000000000..f2289865b8022 Binary files /dev/null and b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_asgrid.pbf differ diff --git a/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf new file mode 100644 index 0000000000000..54b0791ccd136 Binary files /dev/null and b/x-pack/plugins/maps/server/mvt/__tests__/pbf/0_0_0_grid_aspoint.pbf differ diff --git a/x-pack/plugins/maps/server/mvt/__tests__/tile_es_responses.ts b/x-pack/plugins/maps/server/mvt/__tests__/tile_es_responses.ts new file mode 100644 index 0000000000000..9fbaba21e71d5 --- /dev/null +++ b/x-pack/plugins/maps/server/mvt/__tests__/tile_es_responses.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as path from 'path'; +import * as fs from 'fs'; + +export const TILE_SEARCHES = { + '0.0.0': { + countResponse: { + count: 1, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + }, + searchResponse: loadJson('./json/0_0_0_search.json'), + }, +}; + +export const TILE_GRIDAGGS = { + '0.0.0': { + gridAggResponse: loadJson('./json/0_0_0_gridagg.json'), + }, +}; + +function loadJson(filePath: string) { + const absolutePath = path.resolve(__dirname, filePath); + const rawContents = fs.readFileSync(absolutePath); + return JSON.parse((rawContents as unknown) as string); +} diff --git a/x-pack/plugins/maps/server/mvt/__tests__/tile_searches.ts b/x-pack/plugins/maps/server/mvt/__tests__/tile_searches.ts deleted file mode 100644 index 317d6434cf81e..0000000000000 --- a/x-pack/plugins/maps/server/mvt/__tests__/tile_searches.ts +++ /dev/null @@ -1,28 +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 * as path from 'path'; -import * as fs from 'fs'; - -const search000path = path.resolve(__dirname, './json/0_0_0_search.json'); -const search000raw = fs.readFileSync(search000path); -const search000json = JSON.parse((search000raw as unknown) as string); - -export const TILE_SEARCHES = { - '0.0.0': { - countResponse: { - count: 1, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - }, - searchResponse: search000json, - }, - '1.1.0': {}, -}; diff --git a/x-pack/plugins/maps/server/mvt/get_tile.test.ts b/x-pack/plugins/maps/server/mvt/get_tile.test.ts index b9c928d594539..76c1741ab2ad0 100644 --- a/x-pack/plugins/maps/server/mvt/get_tile.test.ts +++ b/x-pack/plugins/maps/server/mvt/get_tile.test.ts @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getTile } from './get_tile'; -import { TILE_SEARCHES } from './__tests__/tile_searches'; +import { getGridTile, getTile } from './get_tile'; +import { TILE_GRIDAGGS, TILE_SEARCHES } from './__tests__/tile_es_responses'; import { Logger } from 'src/core/server'; import * as path from 'path'; import * as fs from 'fs'; +import { ES_GEO_FIELD_TYPE, RENDER_AS } from '../../common/constants'; describe('getTile', () => { const mockCallElasticsearch = jest.fn(); @@ -51,13 +52,84 @@ describe('getTile', () => { callElasticsearch: mockCallElasticsearch, }); - if (tile === null) { - throw new Error('Tile should be created'); - } + compareTiles('./__tests__/pbf/0_0_0_docs.pbf', tile); + }); +}); + +describe('getGridTile', () => { + const mockCallElasticsearch = jest.fn(); + + const geometryFieldName = 'geometry'; + + // For mock-purposes only. The ES-call response is mocked in 0_0_0_gridagg.json file + const requestBody = { + _source: { excludes: [] }, + aggs: { + gridSplit: { + aggs: { + // eslint-disable-next-line @typescript-eslint/naming-convention + avg_of_TOTAL_AV: { avg: { field: 'TOTAL_AV' } }, + gridCentroid: { geo_centroid: { field: geometryFieldName } }, + }, + geotile_grid: { + bounds: null, + field: geometryFieldName, + precision: null, + shard_size: 65535, + size: 65535, + }, + }, + }, + docvalue_fields: [], + query: { + bool: { + filter: [], + }, + }, + script_fields: {}, + size: 0, + stored_fields: ['*'], + }; - const expectedPath = path.resolve(__dirname, './__tests__/pbf/0_0_0.pbf'); - const expectedBin = fs.readFileSync(expectedPath, 'binary'); - const expectedTile = Buffer.from(expectedBin, 'binary'); - expect(expectedTile.equals(tile)).toBe(true); + beforeEach(() => { + mockCallElasticsearch.mockReset(); + mockCallElasticsearch.mockImplementation((type) => { + return TILE_GRIDAGGS['0.0.0'].gridAggResponse; + }); + }); + + const defaultParams = { + x: 0, + y: 0, + z: 0, + index: 'manhattan', + requestBody, + geometryFieldName, + logger: ({ + info: () => {}, + } as unknown) as Logger, + callElasticsearch: mockCallElasticsearch, + requestType: RENDER_AS.POINT, + geoFieldType: ES_GEO_FIELD_TYPE.GEO_POINT, + }; + + test('0.0.0 tile (clusters)', async () => { + const tile = await getGridTile(defaultParams); + compareTiles('./__tests__/pbf/0_0_0_grid_aspoint.pbf', tile); + }); + + test('0.0.0 tile (grids)', async () => { + const tile = await getGridTile({ ...defaultParams, requestType: RENDER_AS.GRID }); + compareTiles('./__tests__/pbf/0_0_0_grid_asgrid.pbf', tile); }); }); + +function compareTiles(expectedRelativePath: string, actualTile: Buffer | null) { + if (actualTile === null) { + throw new Error('Tile should be created'); + } + const expectedPath = path.resolve(__dirname, expectedRelativePath); + const expectedBin = fs.readFileSync(expectedPath, 'binary'); + const expectedTile = Buffer.from(expectedBin, 'binary'); + expect(expectedTile.equals(actualTile)).toBe(true); +} diff --git a/x-pack/plugins/maps/server/mvt/get_tile.ts b/x-pack/plugins/maps/server/mvt/get_tile.ts index 9621f7f174a30..dd88be7f80c2e 100644 --- a/x-pack/plugins/maps/server/mvt/get_tile.ts +++ b/x-pack/plugins/maps/server/mvt/get_tile.ts @@ -13,22 +13,89 @@ import { Feature, FeatureCollection, Polygon } from 'geojson'; import { ES_GEO_FIELD_TYPE, FEATURE_ID_PROPERTY_NAME, + GEOTILE_GRID_AGG_NAME, KBN_TOO_MANY_FEATURES_PROPERTY, + MAX_ZOOM, MVT_SOURCE_LAYER_NAME, + RENDER_AS, + SUPER_FINE_ZOOM_DELTA, } from '../../common/constants'; -import { hitsToGeoJson } from '../../common/elasticsearch_geo_utils'; +import { hitsToGeoJson } from '../../common/elasticsearch_util'; import { flattenHit } from './util'; +import { convertRegularRespToGeoJson } from '../../common/elasticsearch_util'; +import { ESBounds, tile2lat, tile2long, tileToESBbox } from '../../common/geo_tile_utils'; -interface ESBounds { - top_left: { - lon: number; - lat: number; - }; - bottom_right: { - lon: number; - lat: number; - }; +export async function getGridTile({ + logger, + callElasticsearch, + index, + geometryFieldName, + x, + y, + z, + requestBody = {}, + requestType = RENDER_AS.POINT, + geoFieldType = ES_GEO_FIELD_TYPE.GEO_POINT, +}: { + x: number; + y: number; + z: number; + geometryFieldName: string; + index: string; + callElasticsearch: (type: string, ...args: any[]) => Promise; + logger: Logger; + requestBody: any; + requestType: RENDER_AS; + geoFieldType: ES_GEO_FIELD_TYPE; +}): Promise { + const esBbox: ESBounds = tileToESBbox(x, y, z); + try { + let bboxFilter; + if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_POINT) { + bboxFilter = { + geo_bounding_box: { + [geometryFieldName]: esBbox, + }, + }; + } else if (geoFieldType === ES_GEO_FIELD_TYPE.GEO_SHAPE) { + const geojsonPolygon = tileToGeoJsonPolygon(x, y, z); + bboxFilter = { + geo_shape: { + [geometryFieldName]: { + shape: geojsonPolygon, + relation: 'INTERSECTS', + }, + }, + }; + } else { + throw new Error(`${geoFieldType} is not valid geo field-type`); + } + requestBody.query.bool.filter.push(bboxFilter); + + requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.precision = Math.min( + z + SUPER_FINE_ZOOM_DELTA, + MAX_ZOOM + ); + requestBody.aggs[GEOTILE_GRID_AGG_NAME].geotile_grid.bounds = esBbox; + + const esGeotileGridQuery = { + index, + body: requestBody, + }; + + const gridAggResult = await callElasticsearch('search', esGeotileGridQuery); + const features: Feature[] = convertRegularRespToGeoJson(gridAggResult, requestType); + const featureCollection: FeatureCollection = { + features, + type: 'FeatureCollection', + }; + + return createMvtTile(featureCollection, z, x, y); + } catch (e) { + logger.warn(`Cannot generate grid-tile for ${z}/${x}/${y}: ${e.message}`); + return null; + } } export async function getTile({ @@ -149,26 +216,7 @@ export async function getTile({ type: 'FeatureCollection', }; - const tileIndex = geojsonvt(featureCollection, { - maxZoom: 24, // max zoom to preserve detail on; can't be higher than 24 - tolerance: 3, // simplification tolerance (higher means simpler) - extent: 4096, // tile extent (both width and height) - buffer: 64, // tile buffer on each side - debug: 0, // logging level (0 to disable, 1 or 2) - lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features - promoteId: null, // name of a feature property to promote to feature.id. Cannot be used with `generateId` - generateId: false, // whether to generate feature ids. Cannot be used with `promoteId` - indexMaxZoom: 5, // max zoom in the initial tile index - indexMaxPoints: 100000, // max number of points per tile in the index - }); - const tile = tileIndex.getTile(z, x, y); - - if (tile) { - const pbf = vtpbf.fromGeojsonVt({ [MVT_SOURCE_LAYER_NAME]: tile }, { version: 2 }); - return Buffer.from(pbf); - } else { - return null; - } + return createMvtTile(featureCollection, z, x, y); } catch (e) { logger.warn(`Cannot generate tile for ${z}/${x}/${y}: ${e.message}`); return null; @@ -195,15 +243,6 @@ function tileToGeoJsonPolygon(x: number, y: number, z: number): Polygon { }; } -function tile2long(x: number, z: number): number { - return (x / Math.pow(2, z)) * 360 - 180; -} - -function tile2lat(y: number, z: number): number { - const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z); - return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); -} - function esBboxToGeoJsonPolygon(esBounds: ESBounds): Polygon { let minLon = esBounds.top_left.lon; const maxLon = esBounds.bottom_right.lon; @@ -224,3 +263,31 @@ function esBboxToGeoJsonPolygon(esBounds: ESBounds): Polygon { ], }; } + +function createMvtTile( + featureCollection: FeatureCollection, + z: number, + x: number, + y: number +): Buffer | null { + const tileIndex = geojsonvt(featureCollection, { + maxZoom: 24, // max zoom to preserve detail on; can't be higher than 24 + tolerance: 3, // simplification tolerance (higher means simpler) + extent: 4096, // tile extent (both width and height) + buffer: 64, // tile buffer on each side + debug: 0, // logging level (0 to disable, 1 or 2) + lineMetrics: false, // whether to enable line metrics tracking for LineString/MultiLineString features + promoteId: null, // name of a feature property to promote to feature.id. Cannot be used with `generateId` + generateId: false, // whether to generate feature ids. Cannot be used with `promoteId` + indexMaxZoom: 5, // max zoom in the initial tile index + indexMaxPoints: 100000, // max number of points per tile in the index + }); + const tile = tileIndex.getTile(z, x, y); + + if (tile) { + const pbf = vtpbf.fromGeojsonVt({ [MVT_SOURCE_LAYER_NAME]: tile }, { version: 2 }); + return Buffer.from(pbf); + } else { + return null; + } +} diff --git a/x-pack/plugins/maps/server/mvt/mvt_routes.ts b/x-pack/plugins/maps/server/mvt/mvt_routes.ts index 32c14a355ba2a..266a240b53017 100644 --- a/x-pack/plugins/maps/server/mvt/mvt_routes.ts +++ b/x-pack/plugins/maps/server/mvt/mvt_routes.ts @@ -6,10 +6,21 @@ import rison from 'rison-node'; import { schema } from '@kbn/config-schema'; -import { Logger } from 'src/core/server'; +import { + KibanaRequest, + KibanaResponseFactory, + Logger, + RequestHandlerContext, +} from 'src/core/server'; import { IRouter } from 'src/core/server'; -import { MVT_GETTILE_API_PATH, API_ROOT_PATH } from '../../common/constants'; -import { getTile } from './get_tile'; +import { + MVT_GETTILE_API_PATH, + API_ROOT_PATH, + MVT_GETGRIDTILE_API_PATH, + ES_GEO_FIELD_TYPE, + RENDER_AS, +} from '../../common/constants'; +import { getGridTile, getTile } from './get_tile'; const CACHE_TIMEOUT = 0; // Todo. determine good value. Unsure about full-implications (e.g. wrt. time-based data). @@ -28,46 +39,93 @@ export function initMVTRoutes({ router, logger }: { logger: Logger; router: IRou }), }, }, - async (context, request, response) => { + async ( + context: RequestHandlerContext, + request: KibanaRequest, unknown>, + response: KibanaResponseFactory + ) => { const { query } = request; + const requestBodyDSL = rison.decode(query.requestBody as string); - const callElasticsearch = async (type: string, ...args: any[]): Promise => { - return await context.core.elasticsearch.legacy.client.callAsCurrentUser(type, ...args); - }; + const tile = await getTile({ + logger, + callElasticsearch: makeCallElasticsearch(context), + geometryFieldName: query.geometryFieldName as string, + x: query.x as number, + y: query.y as number, + z: query.z as number, + index: query.index as string, + requestBody: requestBodyDSL as any, + }); - const requestBodyDSL = rison.decode(query.requestBody); + return sendResponse(response, tile); + } + ); - const tile = await getTile({ + router.get( + { + path: `${API_ROOT_PATH}/${MVT_GETGRIDTILE_API_PATH}`, + validate: { + query: schema.object({ + x: schema.number(), + y: schema.number(), + z: schema.number(), + geometryFieldName: schema.string(), + requestBody: schema.string(), + index: schema.string(), + requestType: schema.string(), + geoFieldType: schema.string(), + }), + }, + }, + async ( + context: RequestHandlerContext, + request: KibanaRequest, unknown>, + response: KibanaResponseFactory + ) => { + const { query } = request; + const requestBodyDSL = rison.decode(query.requestBody as string); + + const tile = await getGridTile({ logger, - callElasticsearch, - geometryFieldName: query.geometryFieldName, - x: query.x, - y: query.y, - z: query.z, - index: query.index, - requestBody: requestBodyDSL, + callElasticsearch: makeCallElasticsearch(context), + geometryFieldName: query.geometryFieldName as string, + x: query.x as number, + y: query.y as number, + z: query.z as number, + index: query.index as string, + requestBody: requestBodyDSL as any, + requestType: query.requestType as RENDER_AS, + geoFieldType: query.geoFieldType as ES_GEO_FIELD_TYPE, }); - if (tile) { - return response.ok({ - body: tile, - headers: { - 'content-disposition': 'inline', - 'content-length': `${tile.length}`, - 'Content-Type': 'application/x-protobuf', - 'Cache-Control': `max-age=${CACHE_TIMEOUT}`, - }, - }); - } else { - return response.ok({ - headers: { - 'content-disposition': 'inline', - 'content-length': '0', - 'Content-Type': 'application/x-protobuf', - 'Cache-Control': `max-age=${CACHE_TIMEOUT}`, - }, - }); - } + return sendResponse(response, tile); } ); } + +function sendResponse(response: KibanaResponseFactory, tile: any) { + const headers = { + 'content-disposition': 'inline', + 'content-length': tile ? `${tile.length}` : `0`, + 'Content-Type': 'application/x-protobuf', + 'Cache-Control': `max-age=${CACHE_TIMEOUT}`, + }; + + if (tile) { + return response.ok({ + body: tile, + headers, + }); + } else { + return response.ok({ + headers, + }); + } +} + +function makeCallElasticsearch(context: RequestHandlerContext) { + return async (type: string, ...args: any[]): Promise => { + return context.core.elasticsearch.legacy.client.callAsCurrentUser(type, ...args); + }; +} diff --git a/x-pack/plugins/maps_legacy_licensing/public/plugin.ts b/x-pack/plugins/maps_legacy_licensing/public/plugin.ts index eaf527f856bc5..ed7321b2c0e74 100644 --- a/x-pack/plugins/maps_legacy_licensing/public/plugin.ts +++ b/x-pack/plugins/maps_legacy_licensing/public/plugin.ts @@ -26,12 +26,10 @@ export type MapsLegacyLicensingStart = ReturnType; export class MapsLegacyLicensing implements Plugin { public setup(core: CoreSetup, plugins: MapsLegacyLicensingSetupDependencies) { - const { - licensing, - mapsLegacy: { serviceSettings }, - } = plugins; + const { licensing, mapsLegacy } = plugins; if (licensing) { - licensing.license$.subscribe((license: ILicense) => { + licensing.license$.subscribe(async (license: ILicense) => { + const serviceSettings = await mapsLegacy.getServiceSettings(); const { uid, isActive } = license; if (isActive && license.hasAtLeast('basic')) { serviceSettings.setQueryParams({ license: uid }); diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 9a415ac0718b3..d808e4277f075 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -7,3 +7,4 @@ export { SearchResponse7 } from './types/es_client'; export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from './constants/anomalies'; export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; +export { composeValidators, patternValidator } from './util/validators'; diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index d176c22bdbb62..95d06e62f9ef0 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -167,6 +167,7 @@ export interface DataFrameAnalyticsExplorationQueryState { ml: { jobId: JobId; analysisType: DataFrameAnalysisConfigType; + defaultIsTraining?: boolean; }; } @@ -176,6 +177,7 @@ export type DataFrameAnalyticsExplorationUrlState = MLPageState< jobId: JobId; analysisType: DataFrameAnalysisConfigType; globalState?: MlCommonGlobalState; + defaultIsTraining?: boolean; } >; diff --git a/x-pack/plugins/ml/common/util/job_utils.ts b/x-pack/plugins/ml/common/util/job_utils.ts index 9729240567c24..878f5a2c71cb9 100644 --- a/x-pack/plugins/ml/common/util/job_utils.ts +++ b/x-pack/plugins/ml/common/util/job_utils.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import isEmpty from 'lodash/isEmpty'; -import isEqual from 'lodash/isEqual'; -import each from 'lodash/each'; -import pick from 'lodash/pick'; +import { isEmpty, isEqual, each, pick } from 'lodash'; import semver from 'semver'; import moment, { Duration } from 'moment'; diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js index d5025fd3c3649..0527b8f6d9f60 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js +++ b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js @@ -10,7 +10,7 @@ * getting the annotations via props (used in Anomaly Explorer and Single Series Viewer). */ -import uniq from 'lodash/uniq'; +import { uniq } from 'lodash'; import PropTypes from 'prop-types'; import React, { Component, Fragment } from 'react'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js index 378ee82805173..0a2c67a3b0dcb 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js @@ -9,7 +9,7 @@ */ import PropTypes from 'prop-types'; -import get from 'lodash/get'; +import { get } from 'lodash'; import React, { Component } from 'react'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 57f3a08713ffe..1f8c8633afa47 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -7,7 +7,7 @@ import { EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; import React from 'react'; -import get from 'lodash/get'; +import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js index c4ef45800dc3d..cd3875f8cbd2a 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js @@ -11,8 +11,7 @@ import PropTypes from 'prop-types'; import React, { Component, Fragment } from 'react'; -import get from 'lodash/get'; -import pick from 'lodash/pick'; +import { get, pick } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/influencers_cell.js b/x-pack/plugins/ml/public/application/components/anomalies_table/influencers_cell.js index abdb0961351ab..a1ca22eb292ce 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/influencers_cell.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/influencers_cell.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import each from 'lodash/each'; +import { each } from 'lodash'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js index 6025dd1c7433e..d898734f34c93 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; +import { cloneDeep } from 'lodash'; import moment from 'moment'; import rison from 'rison-node'; import PropTypes from 'prop-types'; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index 6aad5d53c3a3c..1949a3c339161 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -188,6 +188,40 @@ export const DataGrid: FC = memo( ); } + let errorCallout; + + if (status === INDEX_STATUS.ERROR) { + // if it's a searchBar syntax error leave the table visible so they can try again + if (errorMessage && !errorMessage.includes('failed to create query')) { + errorCallout = ( + +

{errorMessage}

+
+ ); + } else { + errorCallout = ( + + + {errorMessage} + + + ); + } + } + return (
{isWithHeader(props) && ( @@ -211,19 +245,9 @@ export const DataGrid: FC = memo( )} - {status === INDEX_STATUS.ERROR && ( + {errorCallout !== undefined && (
- - - {errorMessage} - - + {errorCallout}
)} diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx index ad5915b39d521..e37efe60f8018 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -12,7 +12,6 @@ import React, { FC, useState, useCallback, useMemo, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import {} from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlyout, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index d22bba7738db4..49f3f2311a938 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -67,6 +67,17 @@ export const defaultSearchQuery = { match_all: {}, }; +export const getDefaultTrainingFilterQuery = (resultsField: string, isTraining: boolean) => ({ + bool: { + minimum_should_match: 1, + should: [ + { + match: { [`${resultsField}.is_training`]: isTraining }, + }, + ], + }, +}); + export interface SearchQuery { track_total_hits?: boolean; query: SavedSearchQuery; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts index 83eebccd310e3..7ba3e910ddd32 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/index.ts @@ -8,6 +8,7 @@ export { getAnalysisType, getDependentVar, getPredictionFieldName, + getDefaultTrainingFilterQuery, isOutlierAnalysis, refreshAnalyticsList$, useRefreshAnalyticsList, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step_footer/create_step_footer.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step_footer/create_step_footer.tsx index 9fe5963593229..bfa63e21e6c94 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step_footer/create_step_footer.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_step_footer/create_step_footer.tsx @@ -82,7 +82,11 @@ export const CreateStepFooter: FC = ({ jobId, jobType, showProgress }) => jobStats.state === DATA_FRAME_TASK_STATE.STOPPED ) { clearInterval(interval); - setJobFinished(true); + // Check job has started. Jobs that fail to start will also have STOPPED state + setJobFinished( + progressStats.currentPhase === progressStats.totalPhases && + progressStats.progress === 100 + ); } } else { clearInterval(interval); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx index 2e3a5d89367ce..28ef3898cde97 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx @@ -13,20 +13,20 @@ import { EvaluatePanel } from './evaluate_panel'; interface Props { jobId: string; + defaultIsTraining?: boolean; } -export const ClassificationExploration: FC = ({ jobId }) => { - return ( - - ); -}; +export const ClassificationExploration: FC = ({ jobId, defaultIsTraining }) => ( + +); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx index 84b44ef0d349f..f3fc65d264e62 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx @@ -27,9 +27,15 @@ interface Props { jobId: string; title: string; EvaluatePanel: FC; + defaultIsTraining?: boolean; } -export const ExplorationPageWrapper: FC = ({ jobId, title, EvaluatePanel }) => { +export const ExplorationPageWrapper: FC = ({ + jobId, + title, + EvaluatePanel, + defaultIsTraining, +}) => { const { indexPattern, isInitialized, @@ -70,6 +76,7 @@ export const ExplorationPageWrapper: FC = ({ jobId, title, EvaluatePanel needsDestIndexPattern={needsDestIndexPattern} setEvaluateSearchQuery={setSearchQuery} title={title} + defaultIsTraining={defaultIsTraining} /> )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx index 8c158c1ca14a0..c2f3e71e2e492 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx @@ -6,10 +6,12 @@ import React, { Dispatch, FC, SetStateAction, useEffect, useState } from 'react'; -import { EuiCode, EuiInputPopover } from '@elastic/eui'; +import { EuiButtonGroup, EuiCode, EuiFlexGroup, EuiFlexItem, EuiInputPopover } from '@elastic/eui'; +import { EuiButtonGroupIdToSelectedMap } from '@elastic/eui/src/components/button/button_group/button_group'; import { i18n } from '@kbn/i18n'; +import { Dictionary } from '../../../../../../../common/types/common'; import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/common/index_patterns'; import { esKuery, @@ -21,6 +23,7 @@ import { import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search'; import { SavedSearchQuery } from '../../../../../contexts/ml'; +import { removeFilterFromQueryString } from '../../../../../explorer/explorer_utils'; interface ErrorMessage { query: string; @@ -32,6 +35,11 @@ interface ExplorationQueryBarProps { setSearchQuery: Dispatch>; includeQueryString?: boolean; defaultQueryString?: string; + filters?: { + options: Array<{ id: string; label: string }>; + columnId: string; + key: Dictionary; + }; } export const ExplorationQueryBar: FC = ({ @@ -39,12 +47,14 @@ export const ExplorationQueryBar: FC = ({ setSearchQuery, includeQueryString = false, defaultQueryString, + filters, }) => { // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState({ query: '', language: SEARCH_QUERY_LANGUAGE.KUERY, }); + const [idToSelectedMap, setIdToSelectedMap] = useState({}); const [errorMessage, setErrorMessage] = useState(undefined); @@ -52,10 +62,15 @@ export const ExplorationQueryBar: FC = ({ if (defaultQueryString !== undefined) { setSearchInput({ query: defaultQueryString, language: SEARCH_QUERY_LANGUAGE.KUERY }); } - }, []); + }, [defaultQueryString !== undefined]); const searchChangeHandler = (query: Query) => setSearchInput(query); - const searchSubmitHandler = (query: Query) => { + const searchSubmitHandler = (query: Query, filtering?: boolean) => { + // If moved to querying manually, clear filter selection. + if (filtering === undefined) { + setIdToSelectedMap({}); + } + try { switch (query.language) { case SEARCH_QUERY_LANGUAGE.KUERY: @@ -83,32 +98,93 @@ export const ExplorationQueryBar: FC = ({ } }; + const handleFilterUpdate = (optionId: string, currentIdToSelectedMap: any) => { + let newQuery = ''; + const filterValue = filters?.key[optionId]; + const filterQueryString = `${filters?.columnId}:${filterValue}`; + const regex = new RegExp(`${filters?.columnId}\s?:\s?(true|false)`, 'g'); + + // Toggling selected optionId to 'off' - remove column id query from filter + if (currentIdToSelectedMap[optionId] === false) { + newQuery = + searchInput.query !== '' + ? removeFilterFromQueryString( + searchInput.query as string, + filters?.columnId!, + String(filterValue) + ) + : ''; + } else if (currentIdToSelectedMap[optionId] === true) { + // Toggling selected optionId to 'on' + if (searchInput.query === '') { + newQuery = filterQueryString; + } else if (searchInput.query.match(regex) !== null) { + // If query already contains columnId filter - replace with incoming value from filter selection + newQuery = searchInput.query.replace(regex, filterQueryString); + } else { + // Otherwise just add filter query to the end of existing query + newQuery = `${searchInput.query} and ${filterQueryString}`; + } + } + // Add the filter query to the search input and setSearchQuery + const newSearchInput = { ...searchInput, query: newQuery }; + + setSearchInput(newSearchInput); + searchSubmitHandler(newSearchInput, true); + }; + return ( setErrorMessage(undefined)} input={ - + + + + + {filters && filters.options && ( + + { + const newIdToSelectedMap = { [optionId]: !idToSelectedMap[optionId] }; + setIdToSelectedMap(newIdToSelectedMap); + handleFilterUpdate(optionId, newIdToSelectedMap); + }} + /> + + )} + } isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} > diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx index 84b1c4241aaf2..bd4079272c56e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/exploration_results_table.tsx @@ -6,15 +6,7 @@ import React, { Fragment, FC, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiPanel, - EuiSpacer, - EuiText, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui'; import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; @@ -25,10 +17,10 @@ import { getToastNotifications } from '../../../../../util/dependency_cache'; import { DataFrameAnalyticsConfig, MAX_COLUMNS, - INDEX_STATUS, SEARCH_SIZE, defaultSearchQuery, getAnalysisType, + getDefaultTrainingFilterQuery, } from '../../../../common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/use_columns'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; @@ -39,6 +31,7 @@ import { IndexPatternPrompt } from '../index_pattern_prompt'; import { useExplorationResults } from './use_exploration_results'; import { useMlKibana } from '../../../../../contexts/kibana'; import { DataFrameAnalysisConfigType } from '../../../../../../../common/types/data_frame_analytics'; +import { useUrlState } from '../../../../../util/url_state'; const showingDocs = i18n.translate( 'xpack.ml.dataframe.analytics.explorationResults.documentsShownHelpText', @@ -55,6 +48,25 @@ const showingFirstDocs = i18n.translate( } ); +const filters = { + options: [ + { + id: 'training', + label: i18n.translate('xpack.ml.dataframe.analytics.explorationResults.trainingSubsetLabel', { + defaultMessage: 'Training', + }), + }, + { + id: 'testing', + label: i18n.translate('xpack.ml.dataframe.analytics.explorationResults.testingSubsetLabel', { + defaultMessage: 'Testing', + }), + }, + ], + columnId: 'ml.is_training', + key: { training: true, testing: false }, +}; + interface Props { indexPattern: IndexPattern; jobConfig: DataFrameAnalyticsConfig; @@ -62,6 +74,7 @@ interface Props { needsDestIndexPattern: boolean; setEvaluateSearchQuery: React.Dispatch>; title: string; + defaultIsTraining?: boolean; } export const ExplorationResultsTable: FC = React.memo( @@ -72,18 +85,36 @@ export const ExplorationResultsTable: FC = React.memo( needsDestIndexPattern, setEvaluateSearchQuery, title, + defaultIsTraining, }) => { const { services: { mlServices: { mlApiServices }, }, } = useMlKibana(); + const [globalState, setGlobalState] = useUrlState('_g'); const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); + const [defaultQueryString, setDefaultQueryString] = useState(); useEffect(() => { setEvaluateSearchQuery(searchQuery); }, [JSON.stringify(searchQuery)]); + useEffect(() => { + if (defaultIsTraining !== undefined) { + // Apply defaultIsTraining filter + setSearchQuery( + getDefaultTrainingFilterQuery(jobConfig.dest.results_field, defaultIsTraining) + ); + setDefaultQueryString(`${jobConfig.dest.results_field}.is_training : ${defaultIsTraining}`); + // Clear defaultIsTraining from url + setGlobalState('ml', { + analysisType: globalState.ml.analysisType, + jobId: globalState.ml.jobId, + }); + } + }, []); + const analysisType = getAnalysisType(jobConfig.analysis); const classificationData = useExplorationResults( @@ -95,43 +126,11 @@ export const ExplorationResultsTable: FC = React.memo( ); const docFieldsCount = classificationData.columnsWithCharts.length; - const { - columnsWithCharts, - errorMessage, - status, - tableItems, - visibleColumns, - } = classificationData; + const { columnsWithCharts, tableItems, visibleColumns } = classificationData; if (jobConfig === undefined || classificationData === undefined) { return null; } - // if it's a searchBar syntax error leave the table visible so they can try again - if (status === INDEX_STATUS.ERROR && !errorMessage.includes('failed to create query')) { - return ( - - - - - - {jobStatus !== undefined && ( - - {getTaskStateBadge(jobStatus)} - - )} - - -

{errorMessage}

-
-
- ); - } return ( = React.memo( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx index 2b1c40f0eb734..e165ee54acab8 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx @@ -8,14 +8,7 @@ import React, { useState, FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiPanel, - EuiSpacer, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiPanel, EuiSpacer } from '@elastic/eui'; import { useColorRange, @@ -27,7 +20,7 @@ import { DataGrid } from '../../../../../components/data_grid'; import { SavedSearchQuery } from '../../../../../contexts/ml'; import { getToastNotifications } from '../../../../../util/dependency_cache'; -import { defaultSearchQuery, useResultsViewConfig, INDEX_STATUS } from '../../../../common'; +import { defaultSearchQuery, useResultsViewConfig } from '../../../../common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/use_columns'; @@ -54,7 +47,7 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); const outlierData = useOutlierData(indexPattern, jobConfig, searchQuery); - const { columnsWithCharts, errorMessage, status, tableItems } = outlierData; + const { columnsWithCharts, tableItems } = outlierData; const colorRange = useColorRange( COLOR_RANGE.BLUE, @@ -62,24 +55,6 @@ export const OutlierExploration: FC = React.memo(({ jobId }) = jobConfig !== undefined ? getFeatureCount(jobConfig.dest.results_field, tableItems) : 1 ); - // if it's a searchBar syntax error leave the table visible so they can try again - if (status === INDEX_STATUS.ERROR && !errorMessage.includes('failed to create query')) { - return ( - - - -

{errorMessage}

-
-
- ); - } - return ( {jobConfig !== undefined && needsDestIndexPattern && ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx index 36d91f6f41d44..40279ecc6ffa4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx @@ -14,17 +14,17 @@ import { EvaluatePanel } from './evaluate_panel'; interface Props { jobId: string; + defaultIsTraining?: boolean; } -export const RegressionExploration: FC = ({ jobId }) => { - return ( - - ); -}; +export const RegressionExploration: FC = ({ jobId, defaultIsTraining }) => ( + +); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx index f4f01330271fc..4620bbd969fab 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx @@ -32,7 +32,8 @@ import { DataFrameAnalysisConfigType } from '../../../../../common/types/data_fr export const Page: FC<{ jobId: string; analysisType: DataFrameAnalysisConfigType; -}> = ({ jobId, analysisType }) => ( + defaultIsTraining?: boolean; +}> = ({ jobId, analysisType, defaultIsTraining }) => ( @@ -70,10 +71,10 @@ export const Page: FC<{ )} {analysisType === ANALYSIS_CONFIG_TYPE.REGRESSION && ( - + )} {analysisType === ANALYSIS_CONFIG_TYPE.CLASSIFICATION && ( - + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx index ce24892c9de45..b4efca39e200c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx @@ -6,7 +6,7 @@ import { EuiToolTip } from '@elastic/eui'; import React, { FC } from 'react'; -import { isEqual, cloneDeep } from 'lodash'; +import { cloneDeep, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { IIndexPattern } from 'src/plugins/data/common'; import { DeepReadonly } from '../../../../../../../common/types/common'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index 052068c30b84c..c2018463fdf49 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -6,8 +6,7 @@ import { useState } from 'react'; import { Direction, EuiBasicTableProps, EuiTableSortingType } from '@elastic/eui'; -import sortBy from 'lodash/sortBy'; -import get from 'lodash/get'; +import { sortBy, get } from 'lodash'; const PAGE_SIZE = 10; const PAGE_SIZE_OPTIONS = [10, 25, 50]; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts index 178638322bacd..59c6f7249408d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/reducer.ts @@ -10,7 +10,7 @@ import { memoize } from 'lodash'; import numeral from '@elastic/numeral'; import { isValidIndexName } from '../../../../../../../common/util/es_utils'; -import { collapseLiteralStrings } from '../../../../../../../../../../src/plugins/es_ui_shared/public'; +import { collapseLiteralStrings } from '../../../../../../../shared_imports'; import { Action, ACTION } from './actions'; import { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 161dde51df43e..1c8bfafeb10ff 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -12,6 +12,7 @@ import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; +import { DuplicateIndexPatternError } from '../../../../../../../../../../src/plugins/data/public'; import { useRefreshAnalyticsList, @@ -130,19 +131,25 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const indexPatternName = destinationIndex; try { - const newIndexPattern = await mlContext.indexPatterns.make(); + await mlContext.indexPatterns.createAndSave( + { + title: indexPatternName, + }, + false, + true + ); - Object.assign(newIndexPattern, { - id: '', - title: indexPatternName, + addRequestMessage({ + message: i18n.translate( + 'xpack.ml.dataframe.analytics.create.createIndexPatternSuccessMessage', + { + defaultMessage: 'Kibana index pattern {indexPatternName} created.', + values: { indexPatternName }, + } + ), }); - - const id = await newIndexPattern.create(); - - await mlContext.indexPatterns.clearCache(); - - // id returns false if there's a duplicate index pattern. - if (id === false) { + } catch (e) { + if (e instanceof DuplicateIndexPatternError) { addRequestMessage({ error: i18n.translate( 'xpack.ml.dataframe.analytics.create.duplicateIndexPatternErrorMessageError', @@ -158,34 +165,17 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { } ), }); - return; - } - - // check if there's a default index pattern, if not, - // set the newly created one as the default index pattern. - if (!mlContext.kibanaConfig.get('defaultIndex')) { - await mlContext.kibanaConfig.set('defaultIndex', id); + } else { + addRequestMessage({ + error: extractErrorMessage(e), + message: i18n.translate( + 'xpack.ml.dataframe.analytics.create.createIndexPatternErrorMessage', + { + defaultMessage: 'An error occurred creating the Kibana index pattern:', + } + ), + }); } - - addRequestMessage({ - message: i18n.translate( - 'xpack.ml.dataframe.analytics.create.createIndexPatternSuccessMessage', - { - defaultMessage: 'Kibana index pattern {indexPatternName} created.', - values: { indexPatternName }, - } - ), - }); - } catch (e) { - addRequestMessage({ - error: extractErrorMessage(e), - message: i18n.translate( - 'xpack.ml.dataframe.analytics.create.createIndexPatternErrorMessage', - { - defaultMessage: 'An error occurred creating the Kibana index pattern:', - } - ), - }); } }; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx index 831ae8de8081a..78bf07435053d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/geo_point.tsx @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import debounce from 'lodash/debounce'; +import { debounce } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { ChangeEvent, Component, Fragment } from 'react'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts index 5e7de14f451c2..1cc513e778b2f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/combined_fields/utils.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; +import { cloneDeep } from 'lodash'; import uuid from 'uuid/v4'; import { CombinedField } from './types'; import { @@ -54,7 +54,7 @@ export function addCombinedFieldsToPipeline( pipeline: IngestPipeline, combinedFields: CombinedField[] ) { - const updatedPipeline = _.cloneDeep(pipeline); + const updatedPipeline = cloneDeep(pipeline); combinedFields.forEach((combinedField) => { updatedPipeline.processors.push({ set: { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js index 08b61a5fa4eed..2ad0c9b1ac263 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js @@ -215,7 +215,7 @@ export class ImportView extends Component { // mappings, use this field as the time field. // This relies on the field being populated by // the ingest pipeline on ingest - if (mappings[DEFAULT_TIME_FIELD] !== undefined) { + if (mappings.properties[DEFAULT_TIME_FIELD] !== undefined) { timeFieldName = DEFAULT_TIME_FIELD; this.setState({ timeFieldName }); } @@ -615,34 +615,16 @@ export class ImportView extends Component { } } -async function createKibanaIndexPattern( - indexPatternName, - indexPatterns, - timeFieldName, - kibanaConfig -) { +async function createKibanaIndexPattern(indexPatternName, indexPatterns, timeFieldName) { try { - const emptyPattern = await indexPatterns.make(); - - Object.assign(emptyPattern, { - id: '', + const emptyPattern = await indexPatterns.createAndSave({ title: indexPatternName, timeFieldName, }); - const id = await emptyPattern.create(); - - await indexPatterns.clearCache(); - - // check if there's a default index pattern, if not, - // set the newly created one as the default index pattern. - if (!kibanaConfig.get('defaultIndex')) { - await kibanaConfig.set('defaultIndex', id); - } - return { success: true, - id, + id: emptyPattern.id, }; } catch (error) { return { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js index a6fda86f27a7c..bf6b48fa18b47 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js @@ -11,12 +11,7 @@ * and manages the layout of the charts in the containing div. */ -import get from 'lodash/get'; -import each from 'lodash/each'; -import find from 'lodash/find'; -import sortBy from 'lodash/sortBy'; -import map from 'lodash/map'; -import reduce from 'lodash/reduce'; +import { get, each, find, sortBy, map, reduce } from 'lodash'; import { buildConfig } from './explorer_chart_config_builder'; import { chartLimits, getChartType } from '../../util/chart_utils'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js index a7d422d161108..5e6901408422b 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; +import { cloneDeep } from 'lodash'; import mockAnomalyChartRecords from './__mocks__/mock_anomaly_chart_records.json'; import mockDetectorsByJob from './__mocks__/mock_detectors_by_job.json'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx index 05e082711f619..359dc11ca08d1 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx @@ -10,9 +10,7 @@ import React from 'react'; import './_explorer.scss'; -import isEqual from 'lodash/isEqual'; -import uniq from 'lodash/uniq'; -import get from 'lodash/get'; +import { isEqual, uniq, get } from 'lodash'; import d3 from 'd3'; import moment from 'moment'; import DragSelect from 'dragselect'; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts index 81b5167cc00bd..94690b74a6f8f 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts @@ -190,3 +190,9 @@ export declare interface AppStateSelectedCells { showTopFieldValues?: boolean; viewByFieldName?: string; } + +export declare const removeFilterFromQueryString: ( + currentQueryString: string, + fieldName: string, + fieldValue: string +) => string; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js index 08830decc9449..c309e1f4ef8e8 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js @@ -8,7 +8,7 @@ * utils for Anomaly Explorer. */ -import { chain, get, union, uniq } from 'lodash'; +import { get, union, sortBy, uniq } from 'lodash'; import moment from 'moment-timezone'; import { @@ -279,17 +279,17 @@ export function getViewBySwimlaneOptions({ const selectedJobIds = selectedJobs.map((d) => d.id); // Unique influencers for the selected job(s). - const viewByOptions = chain( - mlJobService.jobs.reduce((reducedViewByOptions, job) => { - if (selectedJobIds.some((jobId) => jobId === job.job_id)) { - return reducedViewByOptions.concat(job.analysis_config.influencers || []); - } - return reducedViewByOptions; - }, []) - ) - .uniq() - .sortBy((fieldName) => fieldName.toLowerCase()) - .value(); + const viewByOptions = sortBy( + uniq( + mlJobService.jobs.reduce((reducedViewByOptions, job) => { + if (selectedJobIds.some((jobId) => jobId === job.job_id)) { + return reducedViewByOptions.concat(job.analysis_config.influencers || []); + } + return reducedViewByOptions; + }, []) + ), + (fieldName) => fieldName.toLowerCase() + ); viewByOptions.push(VIEW_BY_JOB_LABEL); let viewBySwimlaneOptions = viewByOptions; diff --git a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js index 87c2219f4d441..a0e9c33e42dfa 100644 --- a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js @@ -296,11 +296,10 @@ export function getTestUrl(job, customUrl) { return new Promise((resolve, reject) => { ml.results .anomalySearch({ - rest_total_hits_as_int: true, body, }) .then((resp) => { - if (resp.hits.total > 0) { + if (resp.hits.total.value > 0) { const record = resp.hits.hits[0]._source; testUrl = replaceTokensInUrlValue(customUrl, bucketSpanSecs, record, 'timestamp'); resolve(testUrl); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 8bc0057b27d6d..fb64d4767eac0 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import sortBy from 'lodash/sortBy'; +import { sortBy } from 'lodash'; import moment from 'moment'; import { toLocaleString } from '../../../../util/string_utils'; diff --git a/x-pack/plugins/ml/public/application/management/index.ts b/x-pack/plugins/ml/public/application/management/index.ts index 72073dfd26a97..e633aef59297e 100644 --- a/x-pack/plugins/ml/public/application/management/index.ts +++ b/x-pack/plugins/ml/public/application/management/index.ts @@ -12,27 +12,25 @@ import { i18n } from '@kbn/i18n'; -import { CoreSetup } from 'kibana/public'; -import { ManagementSetup } from 'src/plugins/management/public'; -import { MlStartDependencies } from '../../plugin'; +import type { CoreSetup } from 'kibana/public'; +import type { ManagementSetup } from 'src/plugins/management/public'; +import type { MlStartDependencies } from '../../plugin'; -import { ManagementAppMountParams } from '../../../../../../src/plugins/management/public'; +import type { ManagementAppMountParams } from '../../../../../../src/plugins/management/public'; export function registerManagementSection( - management: ManagementSetup | undefined, + management: ManagementSetup, core: CoreSetup ) { - if (management !== undefined) { - return management.sections.section.insightsAndAlerting.registerApp({ - id: 'jobsListLink', - title: i18n.translate('xpack.ml.management.jobsListTitle', { - defaultMessage: 'Machine Learning Jobs', - }), - order: 2, - async mount(params: ManagementAppMountParams) { - const { mountApp } = await import('./jobs_list'); - return mountApp(core, params); - }, - }); - } + return management.sections.section.insightsAndAlerting.registerApp({ + id: 'jobsListLink', + title: i18n.translate('xpack.ml.management.jobsListTitle', { + defaultMessage: 'Machine Learning Jobs', + }), + order: 2, + async mount(params: ManagementAppMountParams) { + const { mountApp } = await import('./jobs_list'); + return mountApp(core, params); + }, + }); } diff --git a/x-pack/plugins/ml/public/application/overview/index.ts b/x-pack/plugins/ml/public/application/overview/index.ts deleted file mode 100644 index 7d99bb1094015..0000000000000 --- a/x-pack/plugins/ml/public/application/overview/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { OverviewPage } from './overview_page'; diff --git a/x-pack/plugins/ml/public/application/overview/overview_page.tsx b/x-pack/plugins/ml/public/application/overview/overview_page.tsx index 9a852c491ee27..8219ba8731bd3 100644 --- a/x-pack/plugins/ml/public/application/overview/overview_page.tsx +++ b/x-pack/plugins/ml/public/application/overview/overview_page.tsx @@ -40,3 +40,7 @@ export const OverviewPage: FC = () => { ); }; + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default OverviewPage; diff --git a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx index f9f2ebe48f4aa..b2d2a92617922 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/data_frame_analytics/analytics_job_exploration.tsx @@ -67,10 +67,11 @@ const PageWrapper: FC = ({ location, deps }) => { } const jobId: string = globalState.ml.jobId; const analysisType: DataFrameAnalysisConfigType = globalState.ml.analysisType; + const defaultIsTraining: boolean | undefined = globalState.ml.defaultIsTraining; return ( - + ); }; diff --git a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx index 0e07b0edfbe56..8ce51f4f5dea9 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/overview.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/overview.tsx @@ -4,16 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC } from 'react'; +import React, { FC, Suspense } from 'react'; import { i18n } from '@kbn/i18n'; import { Redirect } from 'react-router-dom'; -import { NavigateToPath } from '../../contexts/kibana'; +import type { NavigateToPath } from '../../contexts/kibana'; import { MlRoute, PageLoader, PageProps } from '../router'; import { useResolver } from '../use_resolver'; -import { OverviewPage } from '../../overview'; import { checkFullLicense } from '../../license'; import { checkGetJobsCapabilitiesResolver } from '../../capabilities/check_capabilities'; @@ -22,6 +21,8 @@ import { loadMlServerInfo } from '../../services/ml_server_info'; import { useTimefilter } from '../../contexts/kibana'; import { breadcrumbOnClickFactory, getBreadcrumbWithUrlForApp } from '../breadcrumbs'; +const OverviewPage = React.lazy(() => import('../../overview/overview_page')); + export const overviewRouteFactory = ( navigateToPath: NavigateToPath, basePath: string @@ -52,7 +53,10 @@ const PageWrapper: FC = ({ deps }) => { return ( - + {/* No fallback yet, we don't show a loading spinner on an outer level until context is available either. */} + + + ); }; diff --git a/x-pack/plugins/ml/public/application/services/forecast_service.js b/x-pack/plugins/ml/public/application/services/forecast_service.js index c13e265b4655c..36d676fe25b35 100644 --- a/x-pack/plugins/ml/public/application/services/forecast_service.js +++ b/x-pack/plugins/ml/public/application/services/forecast_service.js @@ -6,9 +6,7 @@ // Service for carrying out requests to run ML forecasts and to obtain // data on forecasts that have been performed. -import get from 'lodash/get'; -import find from 'lodash/find'; -import each from 'lodash/each'; +import { get, find, each } from 'lodash'; import { map } from 'rxjs/operators'; import { ml } from './ml_api_service'; @@ -52,7 +50,6 @@ function getForecastsSummary(job, query, earliestMs, maxResults) { ml.results .anomalySearch({ size: maxResults, - rest_total_hits_as_int: true, body: { query: { bool: { @@ -63,7 +60,7 @@ function getForecastsSummary(job, query, earliestMs, maxResults) { }, }) .then((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { obj.forecasts = resp.hits.hits.map((hit) => hit._source); } @@ -346,7 +343,6 @@ function getForecastRequestStats(job, forecastId) { ml.results .anomalySearch({ size: 1, - rest_total_hits_as_int: true, body: { query: { bool: { @@ -356,7 +352,7 @@ function getForecastRequestStats(job, forecastId) { }, }) .then((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { obj.stats = resp.hits.hits[0]._source; } resolve(obj); diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index ea97492ae0f5a..939ad34e77a39 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; -import each from 'lodash/each'; -import find from 'lodash/find'; -import get from 'lodash/get'; -import isNumber from 'lodash/isNumber'; +import { cloneDeep, each, find, get, isNumber } from 'lodash'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/ml/public/application/services/mapping_service.js b/x-pack/plugins/ml/public/application/services/mapping_service.js index 251bb0bce5690..b14456c4b229c 100644 --- a/x-pack/plugins/ml/public/application/services/mapping_service.js +++ b/x-pack/plugins/ml/public/application/services/mapping_service.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import each from 'lodash/each'; +import { each } from 'lodash'; import { ml } from './ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts index 22f878a337f51..e1c322910e237 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts @@ -13,8 +13,7 @@ // Returned response contains a results property containing the requested aggregation. import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import each from 'lodash/each'; -import get from 'lodash/get'; +import { each, get } from 'lodash'; import { Dictionary } from '../../../../common/types/common'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { JobId } from '../../../../common/types/anomaly_detection_jobs'; @@ -403,7 +402,6 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) { return mlApiServices.results .anomalySearch$({ index: ML_RESULTS_INDEX_PATTERN, - rest_total_hits_as_int: true, size: maxResults !== undefined ? maxResults : 100, body: { query: { @@ -428,7 +426,7 @@ export function resultsServiceRxProvider(mlApiServices: MlApiServices) { }) .pipe( map((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { each(resp.hits.hits, (hit: any) => { obj.records.push(hit._source); }); diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.js b/x-pack/plugins/ml/public/application/services/results_service/results_service.js index fd48845494dfd..df993079c6771 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/results_service.js +++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import each from 'lodash/each'; -import get from 'lodash/get'; +import { each, get } from 'lodash'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { escapeForElasticsearchQuery } from '../../util/string_utils'; @@ -726,7 +725,6 @@ export function resultsServiceProvider(mlApiServices) { mlApiServices.results .anomalySearch({ size: maxResults !== undefined ? maxResults : 100, - rest_total_hits_as_int: true, body: { _source: ['job_id', 'detector_index', 'influencers', 'record_score'], query: { @@ -750,7 +748,7 @@ export function resultsServiceProvider(mlApiServices) { }, }) .then((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { each(resp.hits.hits, (hit) => { obj.records.push(hit._source); }); @@ -858,7 +856,6 @@ export function resultsServiceProvider(mlApiServices) { mlApiServices.results .anomalySearch({ size: maxResults !== undefined ? maxResults : 100, - rest_total_hits_as_int: true, body: { query: { bool: { @@ -881,7 +878,7 @@ export function resultsServiceProvider(mlApiServices) { }, }) .then((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { each(resp.hits.hits, (hit) => { obj.records.push(hit._source); }); @@ -983,7 +980,6 @@ export function resultsServiceProvider(mlApiServices) { mlApiServices.results .anomalySearch({ size: maxResults !== undefined ? maxResults : 100, - rest_total_hits_as_int: true, body: { query: { bool: { @@ -1006,7 +1002,7 @@ export function resultsServiceProvider(mlApiServices) { }, }) .then((resp) => { - if (resp.hits.total !== 0) { + if (resp.hits.total.value > 0) { each(resp.hits.hits, (hit) => { obj.records.push(hit._source); }); @@ -1059,7 +1055,6 @@ export function resultsServiceProvider(mlApiServices) { mlApiServices .esSearch({ index, - rest_total_hits_as_int: true, size: 0, body: { query: { @@ -1091,7 +1086,7 @@ export function resultsServiceProvider(mlApiServices) { const time = dataForTime.key; obj.results[time] = dataForTime.doc_count; }); - obj.total = resp.hits.total; + obj.total = resp.hits.total.value; resolve(obj); }) @@ -1228,13 +1223,13 @@ export function resultsServiceProvider(mlApiServices) { .esSearch({ index, body, - rest_total_hits_as_int: true, + track_total_hits: true, }) .then((resp) => { // Because of the sampling, results of metricFunctions which use sum or count // can be significantly skewed. Taking into account totalHits we calculate a // a factor to normalize results for these metricFunctions. - const totalHits = get(resp, ['hits', 'total'], 0); + const totalHits = resp.hits.total.value; const successfulShards = get(resp, ['_shards', 'successful'], 0); let normalizeFactor = 1; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap index fed435d47dfc6..ad76bb9115617 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/calendar_form/__snapshots__/calendar_form.test.js.snap @@ -56,6 +56,7 @@ exports[`CalendarForm Renders calendar form 1`] = ` labelType="label" > - +

{description}

@@ -116,6 +116,7 @@ export const CalendarForm = ({ value={calendarId} onChange={onCalendarIdChange} disabled={isEdit === true || saving === true} + data-test-subj="mlCalendarIdInput" /> @@ -132,6 +133,7 @@ export const CalendarForm = ({ value={description} onChange={onDescriptionChange} disabled={isEdit === true || saving === true} + data-test-subj="mlCalendarDescriptionInput" /> diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js index d80e248674a8f..0b5d2b7b5a3ea 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/new_event_modal/new_event_modal.js @@ -257,7 +257,12 @@ export class NewEventModal extends Component { return ( - + @@ -293,13 +299,18 @@ export class NewEventModal extends Component { - + - + c.calendar_id).join(', '), + }} /> } onCancel={this.closeDestroyModal} @@ -130,18 +135,7 @@ export class CalendarsListUI extends Component { } buttonColor="danger" defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON} - > -

- c.calendar_id).join(', '), - }} - /> -

-
+ /> ); } diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap index 6e9cd17deabee..969406724537d 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/__snapshots__/add_item_popover.test.js.snap @@ -7,7 +7,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto button={ @@ -71,6 +72,7 @@ exports[`AddItemPopover calls addItems with multiple items on clicking Add butto grow={false} > @@ -93,7 +95,7 @@ exports[`AddItemPopover opens the popover onButtonClick 1`] = ` button={ @@ -157,6 +160,7 @@ exports[`AddItemPopover opens the popover onButtonClick 1`] = ` grow={false} > @@ -179,7 +183,7 @@ exports[`AddItemPopover renders the popover 1`] = ` button={ @@ -243,6 +248,7 @@ exports[`AddItemPopover renders the popover 1`] = ` grow={false} > diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/add_item_popover.js b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/add_item_popover.js index 07e060d87b36a..53a3877e2f1bd 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/add_item_popover.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/add_item_popover/add_item_popover.js @@ -84,7 +84,7 @@ export class AddItemPopover extends Component { iconSide="right" onClick={this.onButtonClick} isDisabled={this.props.canCreateFilter === false} - data-test-subj="mlFilterListAddItemButton" + data-test-subj="mlFilterListOpenNewItemsPopoverButton" > } > - + @@ -127,6 +131,7 @@ export class AddItemPopover extends Component { } + data-test-subj="mlFilterListDeleteConfirmation" defaultFocusedButton="confirm" onCancel={[Function]} onConfirm={[Function]} diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js b/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js index 75fdce8e2bac8..5aafe79645f6a 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/delete_filter_list_modal/delete_filter_list_modal.js @@ -86,6 +86,7 @@ export class DeleteFilterListModal extends Component { } buttonColor="danger" defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON} + data-test-subj={'mlFilterListDeleteConfirmation'} /> ); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap index 9904e90a5afae..268b93923a432 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/__snapshots__/edit_description_popover.test.js.snap @@ -47,6 +47,7 @@ exports[`FilterListUsagePopover opens the popover onButtonClick 1`] = ` labelType="label" > diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/edit_description_popover.js b/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/edit_description_popover.js index 06ace034ca819..b7bcb201f2438 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/edit_description_popover.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/components/edit_description_popover/edit_description_popover.js @@ -102,6 +102,7 @@ export class EditDescriptionPopover extends Component { name="filter_list_description" value={value} onChange={this.onChange} + data-test-subj={'mlFilterListDescriptionInput'} /> diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap index c2fab64473228..f6a4f76975553 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/__snapshots__/edit_filter_list.test.js.snap @@ -80,6 +80,7 @@ exports[`EditFilterList adds new items to filter list 1`] = ` grow={false} > - +

A test filter list

@@ -180,6 +183,7 @@ exports[`EditFilterListHeader renders the header when creating a new filter list labelType="label" > - +

A test filter list

diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js index 681c54ca9eee0..9ea470a388f02 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/edit/edit_filter_list.js @@ -362,7 +362,10 @@ export class EditFilterListUI extends Component { /> - this.returnToFiltersList()}> + this.returnToFiltersList()} + > updateNewFilterId(e.target.value)} + data-test-subj={'mlNewFilterListIdInput'} /> ); @@ -96,7 +97,7 @@ export const EditFilterListHeader = ({ if (description !== undefined && description.length > 0) { descriptionField = ( - +

{description}

); diff --git a/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.js b/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.js index ed992b4e866ff..9e1457483cb2c 100644 --- a/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.js +++ b/x-pack/plugins/ml/public/application/settings/filter_lists/list/table.js @@ -214,7 +214,7 @@ export function FilterListsTable({ isSelectable={true} data-test-subj="mlFilterListsTable" rowProps={(item) => ({ - 'data-test-subj': `mlFilterListsRow row-${item.filter_id}`, + 'data-test-subj': `mlFilterListRow row-${item.filter_id}`, })} />
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index d825844ffa14b..bf99320fdfd4a 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -9,7 +9,7 @@ */ import PropTypes from 'prop-types'; -import get from 'lodash/get'; +import { get } from 'lodash'; import React, { Component } from 'react'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index c1afb2994c92f..1d166b7be9bc1 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -12,10 +12,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import isEqual from 'lodash/isEqual'; -import reduce from 'lodash/reduce'; -import each from 'lodash/each'; -import get from 'lodash/get'; +import { isEqual, reduce, each, get } from 'lodash'; import d3 from 'd3'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts index 5149fecb0ec26..e43ba8c87083a 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseries_search_service.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import each from 'lodash/each'; -import find from 'lodash/find'; -import get from 'lodash/get'; -import filter from 'lodash/filter'; +import { each, find, get, filter } from 'lodash'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -21,7 +18,7 @@ import { buildConfigFromDetector } from '../util/chart_config_builder'; import { mlResultsService } from '../services/results_service'; import { ModelPlotOutput } from '../services/results_service/result_service_rx'; import { Job } from '../../../common/types/anomaly_detection_jobs'; -import { EntityField } from '../..'; +import { EntityField } from '../../../common/util/anomaly_utils'; function getMetricData( job: Job, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js index 7d14bb43ef811..d24794382128d 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/timeseriesexplorer_utils.js @@ -10,9 +10,7 @@ * Viewer dashboard. */ -import each from 'lodash/each'; -import get from 'lodash/get'; -import find from 'lodash/find'; +import { each, get, find } from 'lodash'; import moment from 'moment-timezone'; import { isTimeSeriesViewJob } from '../../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/util/chart_config_builder.js b/x-pack/plugins/ml/public/application/util/chart_config_builder.js index bc63404a106db..62e64b3d4092e 100644 --- a/x-pack/plugins/ml/public/application/util/chart_config_builder.js +++ b/x-pack/plugins/ml/public/application/util/chart_config_builder.js @@ -9,7 +9,7 @@ * in the source metric data. */ -import get from 'lodash/get'; +import { get } from 'lodash'; import { mlFunctionToESAggregation } from '../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index 2586dfe45345e..341f3a877dd86 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -4,25 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DataPublicPluginSetup } from 'src/plugins/data/public'; -import { +import type { DataPublicPluginSetup } from 'src/plugins/data/public'; +import type { IUiSettingsClient, ChromeStart, SavedObjectsClientContract, ApplicationStart, HttpStart, I18nStart, -} from 'kibana/public'; -import { IndexPatternsContract, DataPublicPluginStart } from 'src/plugins/data/public'; -import { DocLinksStart, ToastsStart, OverlayStart, ChromeRecentlyAccessed, IBasePath, } from 'kibana/public'; -import { SharePluginStart } from 'src/plugins/share/public'; -import { SecurityPluginSetup } from '../../../../security/public'; +import type { IndexPatternsContract, DataPublicPluginStart } from 'src/plugins/data/public'; +import type { SharePluginStart } from 'src/plugins/share/public'; +import type { SecurityPluginSetup } from '../../../../security/public'; export interface DependencyCache { timefilter: DataPublicPluginSetup['query']['timefilter'] | null; diff --git a/x-pack/plugins/ml/public/application/util/index_utils.ts b/x-pack/plugins/ml/public/application/util/index_utils.ts index 192552b25d15a..42be3dd8252f9 100644 --- a/x-pack/plugins/ml/public/application/util/index_utils.ts +++ b/x-pack/plugins/ml/public/application/util/index_utils.ts @@ -104,7 +104,11 @@ export function getQueryFromSavedSearch(savedSearch: SavedSearchSavedObject) { export function getIndexPatternById(id: string): Promise { if (indexPatternsContract !== null) { - return indexPatternsContract.get(id); + if (id) { + return indexPatternsContract.get(id); + } else { + return indexPatternsContract.create({}); + } } else { throw new Error('Index patterns are not initialized!'); } diff --git a/x-pack/plugins/ml/public/application/util/time_buckets.js b/x-pack/plugins/ml/public/application/util/time_buckets.js index 15b8f24804ec8..bfc7c8c84001a 100644 --- a/x-pack/plugins/ml/public/application/util/time_buckets.js +++ b/x-pack/plugins/ml/public/application/util/time_buckets.js @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import isPlainObject from 'lodash/isPlainObject'; -import isString from 'lodash/isString'; -import ary from 'lodash/ary'; -import sortBy from 'lodash/sortBy'; -import assign from 'lodash/assign'; +import { isPlainObject, isString, ary, sortBy, assign } from 'lodash'; import moment from 'moment'; import dateMath from '@elastic/datemath'; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx index e837cabf0b494..6e67ff1aef03d 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { Suspense } from 'react'; import ReactDOM from 'react-dom'; import { CoreStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { Subject } from 'rxjs'; import { Embeddable, IContainer } from '../../../../../../src/plugins/embeddable/public'; -import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container'; -import { JobId } from '../../../common/types/anomaly_detection_jobs'; -import { MlDependencies } from '../../application/app'; +import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; +import type { MlDependencies } from '../../application/app'; import { SWIM_LANE_SELECTION_TRIGGER } from '../../ui_actions'; import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, @@ -59,15 +59,17 @@ export class AnomalySwimlaneEmbeddable extends Embeddable< ReactDOM.render( - + + + , node ); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts index 9d2fd07e11be5..8a977ed5820c1 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts @@ -6,15 +6,15 @@ import { i18n } from '@kbn/i18n'; -import { StartServicesAccessor } from 'kibana/public'; +import type { StartServicesAccessor } from 'kibana/public'; -import { +import type { EmbeddableFactoryDefinition, IContainer, } from '../../../../../../src/plugins/embeddable/public'; import { HttpService } from '../../application/services/http_service'; -import { MlPluginStart, MlStartDependencies } from '../../plugin'; -import { MlDependencies } from '../../application/app'; +import type { MlPluginStart, MlStartDependencies } from '../../plugin'; +import type { MlDependencies } from '../../application/app'; import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, AnomalySwimlaneEmbeddableInput, diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx index 60681446ac7aa..0291fa1564a2d 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx @@ -145,3 +145,7 @@ export const EmbeddableSwimLaneContainer: FC = (
); }; + +// required for dynamic import using React.lazy() +// eslint-disable-next-line import/no-default-export +export default EmbeddableSwimLaneContainer; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container_lazy.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container_lazy.tsx new file mode 100644 index 0000000000000..faeb9cbb3c8f2 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container_lazy.tsx @@ -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 React from 'react'; + +export const EmbeddableSwimLaneContainer = React.lazy( + () => import('./embeddable_swim_lane_container') +); diff --git a/x-pack/plugins/ml/public/embeddables/index.ts b/x-pack/plugins/ml/public/embeddables/index.ts index cc4bec0b67836..06cb5afa71cff 100644 --- a/x-pack/plugins/ml/public/embeddables/index.ts +++ b/x-pack/plugins/ml/public/embeddables/index.ts @@ -5,8 +5,8 @@ */ import { AnomalySwimlaneEmbeddableFactory } from './anomaly_swimlane'; -import { MlCoreSetup } from '../plugin'; -import { EmbeddableSetup } from '../../../../../src/plugins/embeddable/public'; +import type { MlCoreSetup } from '../plugin'; +import type { EmbeddableSetup } from '../../../../../src/plugins/embeddable/public'; export * from './constants'; export * from './types'; diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 93ec79d9b8310..b2979e44d0927 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -4,20 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreStart } from 'kibana/public'; -import { JobId } from '../../common/types/anomaly_detection_jobs'; -import { SwimlaneType } from '../application/explorer/explorer_constants'; -import { Filter } from '../../../../../src/plugins/data/common/es_query/filters'; -import { Query, RefreshInterval, TimeRange } from '../../../../../src/plugins/data/common/query'; -import { +import type { CoreStart } from 'kibana/public'; +import type { JobId } from '../../common/types/anomaly_detection_jobs'; +import type { SwimlaneType } from '../application/explorer/explorer_constants'; +import type { Filter } from '../../../../../src/plugins/data/common/es_query/filters'; +import type { + Query, + RefreshInterval, + TimeRange, +} from '../../../../../src/plugins/data/common/query'; +import type { EmbeddableInput, EmbeddableOutput, IEmbeddable, } from '../../../../../src/plugins/embeddable/public'; -import { AnomalyDetectorService } from '../application/services/anomaly_detector_service'; -import { AnomalyTimelineService } from '../application/services/anomaly_timeline_service'; -import { MlDependencies } from '../application/app'; -import { AppStateSelectedCells } from '../application/explorer/explorer_utils'; +import type { AnomalyDetectorService } from '../application/services/anomaly_detector_service'; +import type { AnomalyTimelineService } from '../application/services/anomaly_timeline_service'; +import type { MlDependencies } from '../application/app'; +import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; export interface AnomalySwimlaneEmbeddableCustomInput { jobIds: JobId[]; diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index 80308977735d2..c43df1e1a3d2c 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +// Be careful adding exports to this file, it may increase the bundle size of +// the ML plugin's page load bundle. You should either just export types or +// use `getMlSharedImports()` to export static code. + import { PluginInitializer, PluginInitializerContext } from 'kibana/public'; import { MlPlugin, @@ -20,5 +24,29 @@ export const plugin: PluginInitializer< MlStartDependencies > = (initializerContext: PluginInitializerContext) => new MlPlugin(initializerContext); -export { MlPluginSetup, MlPluginStart }; -export * from './shared'; +export type { MlPluginSetup, MlPluginStart }; +export type { + AnomaliesTableRecord, + DataRecognizerConfigResponse, + Influencer, + JobExistResult, + JobStat, + MlCapabilitiesResponse, + MlSummaryJob, + UseIndexDataReturnType, + EsSorting, + RenderCellValue, +} from './shared'; + +// Static exports +export { getSeverityColor, getSeverityType } from '../common/util/anomaly_utils'; +export { ANOMALY_SEVERITY } from '../common'; + +// Bundled shared exports +// Exported this way so the code doesn't end up in ML's page load bundle +export const getMlSharedImports = async () => { + return await import('./shared'); +}; +// Helper to get Type returned by getMlSharedImports. +type AwaitReturnType = T extends PromiseLike ? U : T; +export type GetMlSharedImportsReturnType = AwaitReturnType>; diff --git a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts index 6a44756412fe3..9a900c456d516 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import isEmpty from 'lodash/isEmpty'; -import { +import { isEmpty } from 'lodash'; +import type { AnomalyDetectionQueryState, AnomalyDetectionUrlState, ExplorerAppState, diff --git a/x-pack/plugins/ml/public/ml_url_generator/common.ts b/x-pack/plugins/ml/public/ml_url_generator/common.ts index f929e513e618a..a03497092d3b3 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/common.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/common.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import isEmpty from 'lodash/isEmpty'; +import { isEmpty } from 'lodash'; import { MlGenericUrlState } from '../../common/types/ml_url_generator'; import { setStateToKbnUrl } from '../../../../../src/plugins/kibana_utils/public'; diff --git a/x-pack/plugins/ml/public/ml_url_generator/data_frame_analytics_urls_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/data_frame_analytics_urls_generator.ts index 88761edf241a9..2408290e76773 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/data_frame_analytics_urls_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/data_frame_analytics_urls_generator.ts @@ -61,12 +61,13 @@ export function createDataFrameAnalyticsExplorationUrl( let url = `${appBasePath}/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`; if (mlUrlGeneratorState) { - const { jobId, analysisType, globalState } = mlUrlGeneratorState; + const { jobId, analysisType, defaultIsTraining, globalState } = mlUrlGeneratorState; const queryState: DataFrameAnalyticsExplorationQueryState = { ml: { jobId, analysisType, + defaultIsTraining, }, ...globalState, }; diff --git a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts index abec5cc2b7d1e..704135f5546b1 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup } from 'kibana/public'; -import { +import type { CoreSetup } from 'kibana/public'; +import type { SharePluginSetup, UrlGeneratorsDefinition, UrlGeneratorState, } from '../../../../../src/plugins/share/public'; -import { MlStartDependencies } from '../plugin'; +import type { MlStartDependencies } from '../plugin'; import { ML_PAGES, ML_APP_URL_GENERATOR } from '../../common/constants/ml_url_generator'; -import { MlUrlGeneratorState } from '../../common/types/ml_url_generator'; +import type { MlUrlGeneratorState } from '../../common/types/ml_url_generator'; import { createAnomalyDetectionJobManagementUrl, createAnomalyDetectionCreateJobSelectType, diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 8feef489fdde1..034ed090e2212 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { +import type { AppMountParameters, CoreSetup, CoreStart, @@ -14,29 +14,26 @@ import { } from 'kibana/public'; import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; -import { ManagementSetup } from 'src/plugins/management/public'; -import { SharePluginSetup, SharePluginStart } from 'src/plugins/share/public'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; - -import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { IndexPatternManagementSetup } from 'src/plugins/index_pattern_management/public'; -import { EmbeddableSetup } from 'src/plugins/embeddable/public'; + +import type { ManagementSetup } from 'src/plugins/management/public'; +import type { SharePluginSetup, SharePluginStart } from 'src/plugins/share/public'; +import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { HomePublicPluginSetup } from 'src/plugins/home/public'; +import type { IndexPatternManagementSetup } from 'src/plugins/index_pattern_management/public'; +import type { EmbeddableSetup } from 'src/plugins/embeddable/public'; + import { AppStatus, AppUpdater, DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; -import { MlCardState } from '../../../../src/plugins/index_pattern_management/public'; -import { SecurityPluginSetup } from '../../security/public'; -import { LicensingPluginSetup } from '../../licensing/public'; -import { registerManagementSection } from './application/management'; -import { LicenseManagementUIPluginSetup } from '../../license_management/public'; -import { setDependencyCache } from './application/util/dependency_cache'; +import type { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public'; +import type { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; + +import type { LicenseManagementUIPluginSetup } from '../../license_management/public'; +import type { LicensingPluginSetup } from '../../licensing/public'; +import type { SecurityPluginSetup } from '../../security/public'; + import { PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; -import { registerFeature } from './register_feature'; -import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public'; -import { registerMlUiActions } from './ui_actions'; -import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; -import { registerUrlGenerator } from './ml_url_generator'; -import { isFullLicense, isMlEnabled } from '../common/license'; -import { registerEmbeddables } from './embeddables'; + +import { setDependencyCache } from './application/util/dependency_cache'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -101,12 +98,21 @@ export class MlPlugin implements Plugin { }, }); - const managementApp = registerManagementSection(pluginsSetup.management, core); - const licensing = pluginsSetup.licensing.license$.pipe(take(1)); licensing.subscribe(async (license) => { const [coreStart] = await core.getStartServices(); + const { + isFullLicense, + isMlEnabled, + registerEmbeddables, + registerFeature, + registerManagementSection, + registerMlUiActions, + registerUrlGenerator, + MlCardState, + } = await import('./register_helper'); + if (isMlEnabled(license)) { // add ML to home page if (pluginsSetup.home) { @@ -129,22 +135,17 @@ export class MlPlugin implements Plugin { // register various ML plugin features which require a full license if (isFullLicense(license)) { - if (canManageMLJobs && managementApp) { - managementApp.enable(); + if (canManageMLJobs && pluginsSetup.management !== undefined) { + registerManagementSection(pluginsSetup.management, core).enable(); } registerEmbeddables(pluginsSetup.embeddable, core); registerMlUiActions(pluginsSetup.uiActions, core); - } else if (managementApp) { - managementApp.disable(); } } else { // if ml is disabled in elasticsearch, disable ML in kibana this.appUpdater.next(() => ({ status: AppStatus.inaccessible, })); - if (managementApp) { - managementApp.disable(); - } } }); diff --git a/x-pack/plugins/ml/public/register_helper.ts b/x-pack/plugins/ml/public/register_helper.ts new file mode 100644 index 0000000000000..97574e296d1eb --- /dev/null +++ b/x-pack/plugins/ml/public/register_helper.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { MlCardState } from '../../../../src/plugins/index_pattern_management/public'; + +export { isFullLicense, isMlEnabled } from '../common/license'; + +export { registerEmbeddables } from './embeddables'; +export { registerFeature } from './register_feature'; +export { registerManagementSection } from './application/management'; +export { registerMlUiActions } from './ui_actions'; +export { registerUrlGenerator } from './ml_url_generator'; diff --git a/x-pack/plugins/ml/server/index.ts b/x-pack/plugins/ml/server/index.ts index 4c27854ec719b..af77468fa57d7 100644 --- a/x-pack/plugins/ml/server/index.ts +++ b/x-pack/plugins/ml/server/index.ts @@ -6,7 +6,7 @@ import { PluginInitializerContext } from 'kibana/server'; import { MlServerPlugin } from './plugin'; -export { MlPluginSetup, MlPluginStart } from './plugin'; +export type { MlPluginSetup, MlPluginStart } from './plugin'; export * from './shared'; export const plugin = (ctx: PluginInitializerContext) => new MlServerPlugin(ctx); diff --git a/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts b/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts index d9ebccd554733..06577d6937101 100644 --- a/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts +++ b/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import isEmpty from 'lodash/isEmpty'; +import { isEmpty } from 'lodash'; import { ISavedObjectsRepository } from 'kibana/server'; import { getInternalRepository } from './internal_repository'; diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts index 24f1d6951c940..d45532e956f42 100644 --- a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts +++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts @@ -5,8 +5,7 @@ */ import Boom from 'boom'; -import each from 'lodash/each'; -import get from 'lodash/get'; +import { each, get } from 'lodash'; import { IScopedClusterClient } from 'kibana/server'; import { ANNOTATION_EVENT_USER, ANNOTATION_TYPE } from '../../../common/constants/annotations'; diff --git a/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js b/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js index 1d59db8fa564f..3edc675c06c0e 100644 --- a/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js +++ b/x-pack/plugins/ml/server/models/bucket_span_estimator/bucket_span_estimator.js @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; -import each from 'lodash/each'; -import remove from 'lodash/remove'; -import sortBy from 'lodash/sortBy'; -import get from 'lodash/get'; +import { cloneDeep, each, remove, sortBy, get } from 'lodash'; import { mlLog } from '../../client/log'; diff --git a/x-pack/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js b/x-pack/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js index 981ffe9618d9f..5ff71b61bec54 100644 --- a/x-pack/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js +++ b/x-pack/plugins/ml/server/models/bucket_span_estimator/polled_data_checker.js @@ -10,7 +10,7 @@ * And a minimum bucket span */ -import get from 'lodash/get'; +import { get } from 'lodash'; export function polledDataCheckerFactory({ asCurrentUser }) { class PolledDataChecker { diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_audit_messages.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_audit_messages.ts index 0f4cac37d2e8f..ae42469cc1af2 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_audit_messages.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/analytics_audit_messages.ts @@ -71,7 +71,6 @@ export function analyticsAuditMessagesProvider({ asInternalUser }: IScopedCluste const { body } = await asInternalUser.search({ index: ML_NOTIFICATION_INDEX_PATTERN, ignore_unavailable: true, - rest_total_hits_as_int: true, size: SIZE, body: { sort: [{ timestamp: { order: 'desc' } }, { job_id: { order: 'asc' } }], @@ -80,7 +79,7 @@ export function analyticsAuditMessagesProvider({ asInternalUser }: IScopedCluste }); let messages = []; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { messages = body.hits.hits.map((hit: Message) => hit._source); messages.reverse(); } diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts index d1a4df768a6ae..394dff1408134 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/index_patterns.ts @@ -5,13 +5,13 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { IIndexPattern } from 'src/plugins/data/server'; +import { IndexPatternAttributes } from 'src/plugins/data/server'; export class IndexPatternHandler { constructor(private savedObjectsClient: SavedObjectsClientContract) {} // returns a id based on an index pattern name async getIndexPatternId(indexName: string) { - const response = await this.savedObjectsClient.find({ + const response = await this.savedObjectsClient.find({ type: 'index-pattern', perPage: 10, search: `"${indexName}"`, diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index 820fcfa9253b6..141e78a91cf0d 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -248,12 +248,11 @@ export class DataRecognizer { const { body } = await this._asCurrentUser.search({ index, - rest_total_hits_as_int: true, size, body: searchBody, }); - return body.hits.total !== 0; + return body.hits.total.value > 0; } async listModules() { diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json index b4fb242f16522..40c47352371d4 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json @@ -14,7 +14,11 @@ "use_null": true } ], - "influencers": ["event.dataset", "mlcategory"] + "influencers": ["event.dataset", "mlcategory"], + "per_partition_categorization": { + "enabled": true, + "stop_on_warn": false + } }, "analysis_limits": { "model_memory_limit": "100mb", @@ -29,6 +33,6 @@ }, "custom_settings": { "created_by": "ml-module-logs-ui-categories", - "job_revision": 0 + "job_revision": 1 } } diff --git a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts index 95c4e79150059..1f59e990096a4 100644 --- a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts +++ b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts @@ -5,10 +5,7 @@ */ import { IScopedClusterClient } from 'kibana/server'; -import get from 'lodash/get'; -import each from 'lodash/each'; -import last from 'lodash/last'; -import find from 'lodash/find'; +import { get, each, last, find } from 'lodash'; import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/server'; import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types'; import { getSafeAggregationName } from '../../../common/util/job_utils'; @@ -630,12 +627,12 @@ export class DataVisualizer { const { body } = await this._asCurrentUser.search({ index, - rest_total_hits_as_int: true, + track_total_hits: true, size, body: searchBody, }); const aggregations = body.aggregations; - const totalCount = get(body, ['hits', 'total'], 0); + const totalCount = body.hits.total.value; const stats = { totalCount, aggregatableExistsFields: [] as FieldData[], @@ -697,11 +694,10 @@ export class DataVisualizer { const { body } = await this._asCurrentUser.search({ index, - rest_total_hits_as_int: true, size, body: searchBody, }); - return body.hits.total > 0; + return body.hits.total.value > 0; } async getDocumentCountStats( @@ -1167,7 +1163,6 @@ export class DataVisualizer { const { body } = await this._asCurrentUser.search({ index, - rest_total_hits_as_int: true, size, body: searchBody, }); @@ -1175,7 +1170,7 @@ export class DataVisualizer { fieldName: field, examples: [] as any[], }; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { const hits = body.hits.hits; for (let i = 0; i < hits.length; i++) { // Look in the _source for the field value. diff --git a/x-pack/plugins/ml/server/models/job_audit_messages/job_audit_messages.js b/x-pack/plugins/ml/server/models/job_audit_messages/job_audit_messages.js index 3fd5ebf3f68f4..e8d21c7355ff7 100644 --- a/x-pack/plugins/ml/server/models/job_audit_messages/job_audit_messages.js +++ b/x-pack/plugins/ml/server/models/job_audit_messages/job_audit_messages.js @@ -102,7 +102,6 @@ export function jobAuditMessagesProvider({ asInternalUser }) { const { body } = await asInternalUser.search({ index: ML_NOTIFICATION_INDEX_PATTERN, ignore_unavailable: true, - rest_total_hits_as_int: true, size: SIZE, body: { sort: [{ timestamp: { order: 'desc' } }, { job_id: { order: 'asc' } }], @@ -111,7 +110,7 @@ export function jobAuditMessagesProvider({ asInternalUser }) { }); let messages = []; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { messages = body.hits.hits.map((hit) => hit._source); } return messages; @@ -153,7 +152,6 @@ export function jobAuditMessagesProvider({ asInternalUser }) { const { body } = await asInternalUser.search({ index: ML_NOTIFICATION_INDEX_PATTERN, ignore_unavailable: true, - rest_total_hits_as_int: true, size: 0, body: { query, @@ -196,7 +194,7 @@ export function jobAuditMessagesProvider({ asInternalUser }) { let messagesPerJob = []; const jobMessages = []; if ( - body.hits.total !== 0 && + body.hits.total.value > 0 && body.aggregations && body.aggregations.levelsPerJob && body.aggregations.levelsPerJob.buckets && diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/line_chart.ts b/x-pack/plugins/ml/server/models/job_service/new_job/line_chart.ts index 128b28a223445..93f7c81c10023 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/line_chart.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/line_chart.ts @@ -89,7 +89,7 @@ function processSearchResults(resp: any, fields: string[]): ProcessedResults { return { success: true, results: tempResults, - totalResults: resp.hits.total, + totalResults: resp.hits.total.value, }; } @@ -107,7 +107,7 @@ function getSearchJsonFromConfig( const json = { index: indexPatternTitle, size: 0, - rest_total_hits_as_int: true, + track_total_hits: true, body: { query: {}, aggs: { diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/population_chart.ts b/x-pack/plugins/ml/server/models/job_service/new_job/population_chart.ts index 71e81158d8885..e614f887e29bc 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/population_chart.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/population_chart.ts @@ -118,7 +118,7 @@ function processSearchResults(resp: any, fields: string[]): ProcessedResults { return { success: true, results: tempResults, - totalResults: resp.hits.total, + totalResults: resp.hits.total.value, }; } @@ -135,7 +135,7 @@ function getPopulationSearchJsonFromConfig( const json = { index: indexPatternTitle, size: 0, - rest_total_hits_as_int: true, + track_total_hits: true, body: { query: {}, aggs: { diff --git a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_cardinality.json b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_cardinality.json index 8d408ff0310c9..8b9b1de453c11 100644 --- a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_cardinality.json +++ b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_cardinality.json @@ -2,6 +2,9 @@ "took": 0, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, - "hits": { "total": 86274, "max_score": 0, "hits": [] }, - "aggregations": { "airline_cardinality": { "value": 19 }, "airline_count": { "doc_count": 86274 } } + "hits": { "total": { "value": 86274, "relation": "eq" }, "max_score": 0, "hits": [] }, + "aggregations": { + "airline_cardinality": { "value": 19 }, + "airline_count": { "doc_count": 86274 } + } } diff --git a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_search_response.json b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_search_response.json index 7bb449921a101..72536f8fdc08b 100644 --- a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_search_response.json +++ b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_farequote_search_response.json @@ -1 +1 @@ -{"took":41,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":86274,"max_score":0,"hits":[]},"aggregations":{"non_empty_buckets":{"buckets":[{"key_as_string":"2017-02-07T00:00:00.000Z","key":1486425600000,"doc_count":23},{"key_as_string":"2017-02-07T00:01:00.000Z","key":1486425660000,"doc_count":10},{"key_as_string":"2017-02-07T00:02:00.000Z","key":1486425720000,"doc_count":6},{"key_as_string":"2017-02-07T00:03:00.000Z","key":1486425780000,"doc_count":11},{"key_as_string":"2017-02-07T00:04:00.000Z","key":1486425840000,"doc_count":11},{"key_as_string":"2017-02-07T00:05:00.000Z","key":1486425900000,"doc_count":11},{"key_as_string":"2017-02-07T00:06:00.000Z","key":1486425960000,"doc_count":11},{"key_as_string":"2017-02-07T00:07:00.000Z","key":1486426020000,"doc_count":11},{"key_as_string":"2017-02-07T00:08:00.000Z","key":1486426080000,"doc_count":8},{"key_as_string":"2017-02-07T00:09:00.000Z","key":1486426140000,"doc_count":11},{"key_as_string":"2017-02-07T00:10:00.000Z","key":1486426200000,"doc_count":10},{"key_as_string":"2017-02-07T00:11:00.000Z","key":1486426260000,"doc_count":9},{"key_as_string":"2017-02-07T00:12:00.000Z","key":1486426320000,"doc_count":12},{"key_as_string":"2017-02-07T00:13:00.000Z","key":1486426380000,"doc_count":9},{"key_as_string":"2017-02-07T00:14:00.000Z","key":1486426440000,"doc_count":8},{"key_as_string":"2017-02-07T00:15:00.000Z","key":1486426500000,"doc_count":14},{"key_as_string":"2017-02-07T00:16:00.000Z","key":1486426560000,"doc_count":9},{"key_as_string":"2017-02-07T00:17:00.000Z","key":1486426620000,"doc_count":8},{"key_as_string":"2017-02-07T00:18:00.000Z","key":1486426680000,"doc_count":13},{"key_as_string":"2017-02-07T00:19:00.000Z","key":1486426740000,"doc_count":9},{"key_as_string":"2017-02-07T00:20:00.000Z","key":1486426800000,"doc_count":11},{"key_as_string":"2017-02-07T00:21:00.000Z","key":1486426860000,"doc_count":9},{"key_as_string":"2017-02-07T00:22:00.000Z","key":1486426920000,"doc_count":9},{"key_as_string":"2017-02-07T00:23:00.000Z","key":1486426980000,"doc_count":12},{"key_as_string":"2017-02-07T00:24:00.000Z","key":1486427040000,"doc_count":11},{"key_as_string":"2017-02-07T00:25:00.000Z","key":1486427100000,"doc_count":11},{"key_as_string":"2017-02-07T00:26:00.000Z","key":1486427160000,"doc_count":8},{"key_as_string":"2017-02-07T00:27:00.000Z","key":1486427220000,"doc_count":12},{"key_as_string":"2017-02-07T00:28:00.000Z","key":1486427280000,"doc_count":7},{"key_as_string":"2017-02-07T00:29:00.000Z","key":1486427340000,"doc_count":12},{"key_as_string":"2017-02-07T00:30:00.000Z","key":1486427400000,"doc_count":13},{"key_as_string":"2017-02-07T00:31:00.000Z","key":1486427460000,"doc_count":10},{"key_as_string":"2017-02-07T00:32:00.000Z","key":1486427520000,"doc_count":13},{"key_as_string":"2017-02-07T00:33:00.000Z","key":1486427580000,"doc_count":13},{"key_as_string":"2017-02-07T00:34:00.000Z","key":1486427640000,"doc_count":11},{"key_as_string":"2017-02-07T00:35:00.000Z","key":1486427700000,"doc_count":9},{"key_as_string":"2017-02-07T00:36:00.000Z","key":1486427760000,"doc_count":11},{"key_as_string":"2017-02-07T00:37:00.000Z","key":1486427820000,"doc_count":15},{"key_as_string":"2017-02-07T00:38:00.000Z","key":1486427880000,"doc_count":7},{"key_as_string":"2017-02-07T00:39:00.000Z","key":1486427940000,"doc_count":13},{"key_as_string":"2017-02-07T00:40:00.000Z","key":1486428000000,"doc_count":9},{"key_as_string":"2017-02-07T00:41:00.000Z","key":1486428060000,"doc_count":12},{"key_as_string":"2017-02-07T00:42:00.000Z","key":1486428120000,"doc_count":16},{"key_as_string":"2017-02-07T00:43:00.000Z","key":1486428180000,"doc_count":7},{"key_as_string":"2017-02-07T00:44:00.000Z","key":1486428240000,"doc_count":6},{"key_as_string":"2017-02-07T00:45:00.000Z","key":1486428300000,"doc_count":12},{"key_as_string":"2017-02-07T00:46:00.000Z","key":1486428360000,"doc_count":11},{"key_as_string":"2017-02-07T00:47:00.000Z","key":1486428420000,"doc_count":12},{"key_as_string":"2017-02-07T00:48:00.000Z","key":1486428480000,"doc_count":13},{"key_as_string":"2017-02-07T00:49:00.000Z","key":1486428540000,"doc_count":10},{"key_as_string":"2017-02-07T00:50:00.000Z","key":1486428600000,"doc_count":13},{"key_as_string":"2017-02-07T00:51:00.000Z","key":1486428660000,"doc_count":7},{"key_as_string":"2017-02-07T00:52:00.000Z","key":1486428720000,"doc_count":11},{"key_as_string":"2017-02-07T00:53:00.000Z","key":1486428780000,"doc_count":12},{"key_as_string":"2017-02-07T00:54:00.000Z","key":1486428840000,"doc_count":16},{"key_as_string":"2017-02-07T00:55:00.000Z","key":1486428900000,"doc_count":8},{"key_as_string":"2017-02-07T00:56:00.000Z","key":1486428960000,"doc_count":12},{"key_as_string":"2017-02-07T00:57:00.000Z","key":1486429020000,"doc_count":12},{"key_as_string":"2017-02-07T00:58:00.000Z","key":1486429080000,"doc_count":8},{"key_as_string":"2017-02-07T00:59:00.000Z","key":1486429140000,"doc_count":11},{"key_as_string":"2017-02-07T01:00:00.000Z","key":1486429200000,"doc_count":8},{"key_as_string":"2017-02-07T01:01:00.000Z","key":1486429260000,"doc_count":7},{"key_as_string":"2017-02-07T01:02:00.000Z","key":1486429320000,"doc_count":6},{"key_as_string":"2017-02-07T01:03:00.000Z","key":1486429380000,"doc_count":15},{"key_as_string":"2017-02-07T01:04:00.000Z","key":1486429440000,"doc_count":12},{"key_as_string":"2017-02-07T01:05:00.000Z","key":1486429500000,"doc_count":5},{"key_as_string":"2017-02-07T01:06:00.000Z","key":1486429560000,"doc_count":11},{"key_as_string":"2017-02-07T01:07:00.000Z","key":1486429620000,"doc_count":15},{"key_as_string":"2017-02-07T01:08:00.000Z","key":1486429680000,"doc_count":8},{"key_as_string":"2017-02-07T01:09:00.000Z","key":1486429740000,"doc_count":14},{"key_as_string":"2017-02-07T01:10:00.000Z","key":1486429800000,"doc_count":8},{"key_as_string":"2017-02-07T01:11:00.000Z","key":1486429860000,"doc_count":6},{"key_as_string":"2017-02-07T01:12:00.000Z","key":1486429920000,"doc_count":16},{"key_as_string":"2017-02-07T01:13:00.000Z","key":1486429980000,"doc_count":10},{"key_as_string":"2017-02-07T01:14:00.000Z","key":1486430040000,"doc_count":8},{"key_as_string":"2017-02-07T01:15:00.000Z","key":1486430100000,"doc_count":12},{"key_as_string":"2017-02-07T01:16:00.000Z","key":1486430160000,"doc_count":13},{"key_as_string":"2017-02-07T01:17:00.000Z","key":1486430220000,"doc_count":10},{"key_as_string":"2017-02-07T01:18:00.000Z","key":1486430280000,"doc_count":11},{"key_as_string":"2017-02-07T01:19:00.000Z","key":1486430340000,"doc_count":12},{"key_as_string":"2017-02-07T01:20:00.000Z","key":1486430400000,"doc_count":12},{"key_as_string":"2017-02-07T01:21:00.000Z","key":1486430460000,"doc_count":12},{"key_as_string":"2017-02-07T01:22:00.000Z","key":1486430520000,"doc_count":7},{"key_as_string":"2017-02-07T01:23:00.000Z","key":1486430580000,"doc_count":13},{"key_as_string":"2017-02-07T01:24:00.000Z","key":1486430640000,"doc_count":5},{"key_as_string":"2017-02-07T01:25:00.000Z","key":1486430700000,"doc_count":11},{"key_as_string":"2017-02-07T01:26:00.000Z","key":1486430760000,"doc_count":11},{"key_as_string":"2017-02-07T01:27:00.000Z","key":1486430820000,"doc_count":9},{"key_as_string":"2017-02-07T01:28:00.000Z","key":1486430880000,"doc_count":11},{"key_as_string":"2017-02-07T01:29:00.000Z","key":1486430940000,"doc_count":10},{"key_as_string":"2017-02-07T01:30:00.000Z","key":1486431000000,"doc_count":10},{"key_as_string":"2017-02-07T01:31:00.000Z","key":1486431060000,"doc_count":12},{"key_as_string":"2017-02-07T01:32:00.000Z","key":1486431120000,"doc_count":12},{"key_as_string":"2017-02-07T01:33:00.000Z","key":1486431180000,"doc_count":10},{"key_as_string":"2017-02-07T01:34:00.000Z","key":1486431240000,"doc_count":10},{"key_as_string":"2017-02-07T01:35:00.000Z","key":1486431300000,"doc_count":10},{"key_as_string":"2017-02-07T01:36:00.000Z","key":1486431360000,"doc_count":14},{"key_as_string":"2017-02-07T01:37:00.000Z","key":1486431420000,"doc_count":9},{"key_as_string":"2017-02-07T01:38:00.000Z","key":1486431480000,"doc_count":8},{"key_as_string":"2017-02-07T01:39:00.000Z","key":1486431540000,"doc_count":16},{"key_as_string":"2017-02-07T01:40:00.000Z","key":1486431600000,"doc_count":8},{"key_as_string":"2017-02-07T01:41:00.000Z","key":1486431660000,"doc_count":11},{"key_as_string":"2017-02-07T01:42:00.000Z","key":1486431720000,"doc_count":12},{"key_as_string":"2017-02-07T01:43:00.000Z","key":1486431780000,"doc_count":10},{"key_as_string":"2017-02-07T01:44:00.000Z","key":1486431840000,"doc_count":16},{"key_as_string":"2017-02-07T01:45:00.000Z","key":1486431900000,"doc_count":10},{"key_as_string":"2017-02-07T01:46:00.000Z","key":1486431960000,"doc_count":7},{"key_as_string":"2017-02-07T01:47:00.000Z","key":1486432020000,"doc_count":10},{"key_as_string":"2017-02-07T01:48:00.000Z","key":1486432080000,"doc_count":5},{"key_as_string":"2017-02-07T01:49:00.000Z","key":1486432140000,"doc_count":11},{"key_as_string":"2017-02-07T01:50:00.000Z","key":1486432200000,"doc_count":7},{"key_as_string":"2017-02-07T01:51:00.000Z","key":1486432260000,"doc_count":16},{"key_as_string":"2017-02-07T01:52:00.000Z","key":1486432320000,"doc_count":8},{"key_as_string":"2017-02-07T01:53:00.000Z","key":1486432380000,"doc_count":14},{"key_as_string":"2017-02-07T01:54:00.000Z","key":1486432440000,"doc_count":10},{"key_as_string":"2017-02-07T01:55:00.000Z","key":1486432500000,"doc_count":8},{"key_as_string":"2017-02-07T01:56:00.000Z","key":1486432560000,"doc_count":12},{"key_as_string":"2017-02-07T01:57:00.000Z","key":1486432620000,"doc_count":14},{"key_as_string":"2017-02-07T01:58:00.000Z","key":1486432680000,"doc_count":9},{"key_as_string":"2017-02-07T01:59:00.000Z","key":1486432740000,"doc_count":10},{"key_as_string":"2017-02-07T02:00:00.000Z","key":1486432800000,"doc_count":9},{"key_as_string":"2017-02-07T02:01:00.000Z","key":1486432860000,"doc_count":14},{"key_as_string":"2017-02-07T02:02:00.000Z","key":1486432920000,"doc_count":10},{"key_as_string":"2017-02-07T02:03:00.000Z","key":1486432980000,"doc_count":7},{"key_as_string":"2017-02-07T02:04:00.000Z","key":1486433040000,"doc_count":8},{"key_as_string":"2017-02-07T02:05:00.000Z","key":1486433100000,"doc_count":8},{"key_as_string":"2017-02-07T02:06:00.000Z","key":1486433160000,"doc_count":14},{"key_as_string":"2017-02-07T02:07:00.000Z","key":1486433220000,"doc_count":10},{"key_as_string":"2017-02-07T02:08:00.000Z","key":1486433280000,"doc_count":7},{"key_as_string":"2017-02-07T02:09:00.000Z","key":1486433340000,"doc_count":11},{"key_as_string":"2017-02-07T02:10:00.000Z","key":1486433400000,"doc_count":6},{"key_as_string":"2017-02-07T02:11:00.000Z","key":1486433460000,"doc_count":8},{"key_as_string":"2017-02-07T02:12:00.000Z","key":1486433520000,"doc_count":7},{"key_as_string":"2017-02-07T02:13:00.000Z","key":1486433580000,"doc_count":15},{"key_as_string":"2017-02-07T02:14:00.000Z","key":1486433640000,"doc_count":8},{"key_as_string":"2017-02-07T02:15:00.000Z","key":1486433700000,"doc_count":14},{"key_as_string":"2017-02-07T02:16:00.000Z","key":1486433760000,"doc_count":5},{"key_as_string":"2017-02-07T02:17:00.000Z","key":1486433820000,"doc_count":9},{"key_as_string":"2017-02-07T02:18:00.000Z","key":1486433880000,"doc_count":7},{"key_as_string":"2017-02-07T02:19:00.000Z","key":1486433940000,"doc_count":8},{"key_as_string":"2017-02-07T02:20:00.000Z","key":1486434000000,"doc_count":13},{"key_as_string":"2017-02-07T02:21:00.000Z","key":1486434060000,"doc_count":13},{"key_as_string":"2017-02-07T02:22:00.000Z","key":1486434120000,"doc_count":13},{"key_as_string":"2017-02-07T02:23:00.000Z","key":1486434180000,"doc_count":8},{"key_as_string":"2017-02-07T02:24:00.000Z","key":1486434240000,"doc_count":11},{"key_as_string":"2017-02-07T02:25:00.000Z","key":1486434300000,"doc_count":9},{"key_as_string":"2017-02-07T02:26:00.000Z","key":1486434360000,"doc_count":11},{"key_as_string":"2017-02-07T02:27:00.000Z","key":1486434420000,"doc_count":14},{"key_as_string":"2017-02-07T02:28:00.000Z","key":1486434480000,"doc_count":9},{"key_as_string":"2017-02-07T02:29:00.000Z","key":1486434540000,"doc_count":15},{"key_as_string":"2017-02-07T02:30:00.000Z","key":1486434600000,"doc_count":13},{"key_as_string":"2017-02-07T02:31:00.000Z","key":1486434660000,"doc_count":6},{"key_as_string":"2017-02-07T02:32:00.000Z","key":1486434720000,"doc_count":16},{"key_as_string":"2017-02-07T02:33:00.000Z","key":1486434780000,"doc_count":11},{"key_as_string":"2017-02-07T02:34:00.000Z","key":1486434840000,"doc_count":9},{"key_as_string":"2017-02-07T02:35:00.000Z","key":1486434900000,"doc_count":11},{"key_as_string":"2017-02-07T02:36:00.000Z","key":1486434960000,"doc_count":15},{"key_as_string":"2017-02-07T02:37:00.000Z","key":1486435020000,"doc_count":6},{"key_as_string":"2017-02-07T02:38:00.000Z","key":1486435080000,"doc_count":11},{"key_as_string":"2017-02-07T02:39:00.000Z","key":1486435140000,"doc_count":5},{"key_as_string":"2017-02-07T02:40:00.000Z","key":1486435200000,"doc_count":14},{"key_as_string":"2017-02-07T02:41:00.000Z","key":1486435260000,"doc_count":9},{"key_as_string":"2017-02-07T02:42:00.000Z","key":1486435320000,"doc_count":9},{"key_as_string":"2017-02-07T02:43:00.000Z","key":1486435380000,"doc_count":9},{"key_as_string":"2017-02-07T02:44:00.000Z","key":1486435440000,"doc_count":16},{"key_as_string":"2017-02-07T02:45:00.000Z","key":1486435500000,"doc_count":10},{"key_as_string":"2017-02-07T02:46:00.000Z","key":1486435560000,"doc_count":8},{"key_as_string":"2017-02-07T02:47:00.000Z","key":1486435620000,"doc_count":10},{"key_as_string":"2017-02-07T02:48:00.000Z","key":1486435680000,"doc_count":11},{"key_as_string":"2017-02-07T02:49:00.000Z","key":1486435740000,"doc_count":13},{"key_as_string":"2017-02-07T02:50:00.000Z","key":1486435800000,"doc_count":9},{"key_as_string":"2017-02-07T02:51:00.000Z","key":1486435860000,"doc_count":14},{"key_as_string":"2017-02-07T02:52:00.000Z","key":1486435920000,"doc_count":17},{"key_as_string":"2017-02-07T02:53:00.000Z","key":1486435980000,"doc_count":10},{"key_as_string":"2017-02-07T02:54:00.000Z","key":1486436040000,"doc_count":8},{"key_as_string":"2017-02-07T02:55:00.000Z","key":1486436100000,"doc_count":10},{"key_as_string":"2017-02-07T02:56:00.000Z","key":1486436160000,"doc_count":9},{"key_as_string":"2017-02-07T02:57:00.000Z","key":1486436220000,"doc_count":9},{"key_as_string":"2017-02-07T02:58:00.000Z","key":1486436280000,"doc_count":12},{"key_as_string":"2017-02-07T02:59:00.000Z","key":1486436340000,"doc_count":8},{"key_as_string":"2017-02-07T03:00:00.000Z","key":1486436400000,"doc_count":11},{"key_as_string":"2017-02-07T03:01:00.000Z","key":1486436460000,"doc_count":12},{"key_as_string":"2017-02-07T03:02:00.000Z","key":1486436520000,"doc_count":7},{"key_as_string":"2017-02-07T03:03:00.000Z","key":1486436580000,"doc_count":16},{"key_as_string":"2017-02-07T03:04:00.000Z","key":1486436640000,"doc_count":13},{"key_as_string":"2017-02-07T03:05:00.000Z","key":1486436700000,"doc_count":10},{"key_as_string":"2017-02-07T03:06:00.000Z","key":1486436760000,"doc_count":11},{"key_as_string":"2017-02-07T03:07:00.000Z","key":1486436820000,"doc_count":6},{"key_as_string":"2017-02-07T03:08:00.000Z","key":1486436880000,"doc_count":9},{"key_as_string":"2017-02-07T03:09:00.000Z","key":1486436940000,"doc_count":7},{"key_as_string":"2017-02-07T03:10:00.000Z","key":1486437000000,"doc_count":14},{"key_as_string":"2017-02-07T03:11:00.000Z","key":1486437060000,"doc_count":9},{"key_as_string":"2017-02-07T03:12:00.000Z","key":1486437120000,"doc_count":12},{"key_as_string":"2017-02-07T03:13:00.000Z","key":1486437180000,"doc_count":10},{"key_as_string":"2017-02-07T03:14:00.000Z","key":1486437240000,"doc_count":9},{"key_as_string":"2017-02-07T03:15:00.000Z","key":1486437300000,"doc_count":12},{"key_as_string":"2017-02-07T03:16:00.000Z","key":1486437360000,"doc_count":12},{"key_as_string":"2017-02-07T03:17:00.000Z","key":1486437420000,"doc_count":8},{"key_as_string":"2017-02-07T03:18:00.000Z","key":1486437480000,"doc_count":13},{"key_as_string":"2017-02-07T03:19:00.000Z","key":1486437540000,"doc_count":12},{"key_as_string":"2017-02-07T03:20:00.000Z","key":1486437600000,"doc_count":8},{"key_as_string":"2017-02-07T03:21:00.000Z","key":1486437660000,"doc_count":20},{"key_as_string":"2017-02-07T03:22:00.000Z","key":1486437720000,"doc_count":8},{"key_as_string":"2017-02-07T03:23:00.000Z","key":1486437780000,"doc_count":9},{"key_as_string":"2017-02-07T03:24:00.000Z","key":1486437840000,"doc_count":12},{"key_as_string":"2017-02-07T03:25:00.000Z","key":1486437900000,"doc_count":9},{"key_as_string":"2017-02-07T03:26:00.000Z","key":1486437960000,"doc_count":9},{"key_as_string":"2017-02-07T03:27:00.000Z","key":1486438020000,"doc_count":12},{"key_as_string":"2017-02-07T03:28:00.000Z","key":1486438080000,"doc_count":12},{"key_as_string":"2017-02-07T03:29:00.000Z","key":1486438140000,"doc_count":7},{"key_as_string":"2017-02-07T03:30:00.000Z","key":1486438200000,"doc_count":12},{"key_as_string":"2017-02-07T03:31:00.000Z","key":1486438260000,"doc_count":15},{"key_as_string":"2017-02-07T03:32:00.000Z","key":1486438320000,"doc_count":12},{"key_as_string":"2017-02-07T03:33:00.000Z","key":1486438380000,"doc_count":9},{"key_as_string":"2017-02-07T03:34:00.000Z","key":1486438440000,"doc_count":9},{"key_as_string":"2017-02-07T03:35:00.000Z","key":1486438500000,"doc_count":12},{"key_as_string":"2017-02-07T03:36:00.000Z","key":1486438560000,"doc_count":8},{"key_as_string":"2017-02-07T03:37:00.000Z","key":1486438620000,"doc_count":12},{"key_as_string":"2017-02-07T03:38:00.000Z","key":1486438680000,"doc_count":13},{"key_as_string":"2017-02-07T03:39:00.000Z","key":1486438740000,"doc_count":10},{"key_as_string":"2017-02-07T03:40:00.000Z","key":1486438800000,"doc_count":8},{"key_as_string":"2017-02-07T03:41:00.000Z","key":1486438860000,"doc_count":10},{"key_as_string":"2017-02-07T03:42:00.000Z","key":1486438920000,"doc_count":12},{"key_as_string":"2017-02-07T03:43:00.000Z","key":1486438980000,"doc_count":9},{"key_as_string":"2017-02-07T03:44:00.000Z","key":1486439040000,"doc_count":13},{"key_as_string":"2017-02-07T03:45:00.000Z","key":1486439100000,"doc_count":11},{"key_as_string":"2017-02-07T03:46:00.000Z","key":1486439160000,"doc_count":16},{"key_as_string":"2017-02-07T03:47:00.000Z","key":1486439220000,"doc_count":10},{"key_as_string":"2017-02-07T03:48:00.000Z","key":1486439280000,"doc_count":10},{"key_as_string":"2017-02-07T03:49:00.000Z","key":1486439340000,"doc_count":10},{"key_as_string":"2017-02-07T03:50:00.000Z","key":1486439400000,"doc_count":10},{"key_as_string":"2017-02-07T03:51:00.000Z","key":1486439460000,"doc_count":8},{"key_as_string":"2017-02-07T03:52:00.000Z","key":1486439520000,"doc_count":8},{"key_as_string":"2017-02-07T03:53:00.000Z","key":1486439580000,"doc_count":14},{"key_as_string":"2017-02-07T03:54:00.000Z","key":1486439640000,"doc_count":12},{"key_as_string":"2017-02-07T03:55:00.000Z","key":1486439700000,"doc_count":9},{"key_as_string":"2017-02-07T03:56:00.000Z","key":1486439760000,"doc_count":10},{"key_as_string":"2017-02-07T03:57:00.000Z","key":1486439820000,"doc_count":15},{"key_as_string":"2017-02-07T03:58:00.000Z","key":1486439880000,"doc_count":8},{"key_as_string":"2017-02-07T03:59:00.000Z","key":1486439940000,"doc_count":13},{"key_as_string":"2017-02-07T04:00:00.000Z","key":1486440000000,"doc_count":9},{"key_as_string":"2017-02-07T04:01:00.000Z","key":1486440060000,"doc_count":13},{"key_as_string":"2017-02-07T04:02:00.000Z","key":1486440120000,"doc_count":7},{"key_as_string":"2017-02-07T04:03:00.000Z","key":1486440180000,"doc_count":10},{"key_as_string":"2017-02-07T04:04:00.000Z","key":1486440240000,"doc_count":15},{"key_as_string":"2017-02-07T04:05:00.000Z","key":1486440300000,"doc_count":12},{"key_as_string":"2017-02-07T04:06:00.000Z","key":1486440360000,"doc_count":10},{"key_as_string":"2017-02-07T04:07:00.000Z","key":1486440420000,"doc_count":9},{"key_as_string":"2017-02-07T04:08:00.000Z","key":1486440480000,"doc_count":8},{"key_as_string":"2017-02-07T04:09:00.000Z","key":1486440540000,"doc_count":12},{"key_as_string":"2017-02-07T04:10:00.000Z","key":1486440600000,"doc_count":10},{"key_as_string":"2017-02-07T04:11:00.000Z","key":1486440660000,"doc_count":11},{"key_as_string":"2017-02-07T04:12:00.000Z","key":1486440720000,"doc_count":9},{"key_as_string":"2017-02-07T04:13:00.000Z","key":1486440780000,"doc_count":14},{"key_as_string":"2017-02-07T04:14:00.000Z","key":1486440840000,"doc_count":16},{"key_as_string":"2017-02-07T04:15:00.000Z","key":1486440900000,"doc_count":12},{"key_as_string":"2017-02-07T04:16:00.000Z","key":1486440960000,"doc_count":12},{"key_as_string":"2017-02-07T04:17:00.000Z","key":1486441020000,"doc_count":12},{"key_as_string":"2017-02-07T04:18:00.000Z","key":1486441080000,"doc_count":10},{"key_as_string":"2017-02-07T04:19:00.000Z","key":1486441140000,"doc_count":14},{"key_as_string":"2017-02-07T04:20:00.000Z","key":1486441200000,"doc_count":10},{"key_as_string":"2017-02-07T04:21:00.000Z","key":1486441260000,"doc_count":13},{"key_as_string":"2017-02-07T04:22:00.000Z","key":1486441320000,"doc_count":13},{"key_as_string":"2017-02-07T04:23:00.000Z","key":1486441380000,"doc_count":8},{"key_as_string":"2017-02-07T04:24:00.000Z","key":1486441440000,"doc_count":10},{"key_as_string":"2017-02-07T04:25:00.000Z","key":1486441500000,"doc_count":14},{"key_as_string":"2017-02-07T04:26:00.000Z","key":1486441560000,"doc_count":6},{"key_as_string":"2017-02-07T04:27:00.000Z","key":1486441620000,"doc_count":15},{"key_as_string":"2017-02-07T04:28:00.000Z","key":1486441680000,"doc_count":12},{"key_as_string":"2017-02-07T04:29:00.000Z","key":1486441740000,"doc_count":11},{"key_as_string":"2017-02-07T04:30:00.000Z","key":1486441800000,"doc_count":8},{"key_as_string":"2017-02-07T04:31:00.000Z","key":1486441860000,"doc_count":8},{"key_as_string":"2017-02-07T04:32:00.000Z","key":1486441920000,"doc_count":14},{"key_as_string":"2017-02-07T04:33:00.000Z","key":1486441980000,"doc_count":10},{"key_as_string":"2017-02-07T04:34:00.000Z","key":1486442040000,"doc_count":12},{"key_as_string":"2017-02-07T04:35:00.000Z","key":1486442100000,"doc_count":13},{"key_as_string":"2017-02-07T04:36:00.000Z","key":1486442160000,"doc_count":7},{"key_as_string":"2017-02-07T04:37:00.000Z","key":1486442220000,"doc_count":11},{"key_as_string":"2017-02-07T04:38:00.000Z","key":1486442280000,"doc_count":10},{"key_as_string":"2017-02-07T04:39:00.000Z","key":1486442340000,"doc_count":13},{"key_as_string":"2017-02-07T04:40:00.000Z","key":1486442400000,"doc_count":10},{"key_as_string":"2017-02-07T04:41:00.000Z","key":1486442460000,"doc_count":17},{"key_as_string":"2017-02-07T04:42:00.000Z","key":1486442520000,"doc_count":9},{"key_as_string":"2017-02-07T04:43:00.000Z","key":1486442580000,"doc_count":14},{"key_as_string":"2017-02-07T04:44:00.000Z","key":1486442640000,"doc_count":11},{"key_as_string":"2017-02-07T04:45:00.000Z","key":1486442700000,"doc_count":12},{"key_as_string":"2017-02-07T04:46:00.000Z","key":1486442760000,"doc_count":10},{"key_as_string":"2017-02-07T04:47:00.000Z","key":1486442820000,"doc_count":17},{"key_as_string":"2017-02-07T04:48:00.000Z","key":1486442880000,"doc_count":7},{"key_as_string":"2017-02-07T04:49:00.000Z","key":1486442940000,"doc_count":12},{"key_as_string":"2017-02-07T04:50:00.000Z","key":1486443000000,"doc_count":7},{"key_as_string":"2017-02-07T04:51:00.000Z","key":1486443060000,"doc_count":12},{"key_as_string":"2017-02-07T04:52:00.000Z","key":1486443120000,"doc_count":14},{"key_as_string":"2017-02-07T04:53:00.000Z","key":1486443180000,"doc_count":6},{"key_as_string":"2017-02-07T04:54:00.000Z","key":1486443240000,"doc_count":10},{"key_as_string":"2017-02-07T04:55:00.000Z","key":1486443300000,"doc_count":16},{"key_as_string":"2017-02-07T04:56:00.000Z","key":1486443360000,"doc_count":7},{"key_as_string":"2017-02-07T04:57:00.000Z","key":1486443420000,"doc_count":6},{"key_as_string":"2017-02-07T04:58:00.000Z","key":1486443480000,"doc_count":11},{"key_as_string":"2017-02-07T04:59:00.000Z","key":1486443540000,"doc_count":14},{"key_as_string":"2017-02-07T05:00:00.000Z","key":1486443600000,"doc_count":10},{"key_as_string":"2017-02-07T05:01:00.000Z","key":1486443660000,"doc_count":9},{"key_as_string":"2017-02-07T05:02:00.000Z","key":1486443720000,"doc_count":6},{"key_as_string":"2017-02-07T05:03:00.000Z","key":1486443780000,"doc_count":11},{"key_as_string":"2017-02-07T05:04:00.000Z","key":1486443840000,"doc_count":14},{"key_as_string":"2017-02-07T05:05:00.000Z","key":1486443900000,"doc_count":12},{"key_as_string":"2017-02-07T05:06:00.000Z","key":1486443960000,"doc_count":7},{"key_as_string":"2017-02-07T05:07:00.000Z","key":1486444020000,"doc_count":15},{"key_as_string":"2017-02-07T05:08:00.000Z","key":1486444080000,"doc_count":12},{"key_as_string":"2017-02-07T05:09:00.000Z","key":1486444140000,"doc_count":7},{"key_as_string":"2017-02-07T05:10:00.000Z","key":1486444200000,"doc_count":14},{"key_as_string":"2017-02-07T05:11:00.000Z","key":1486444260000,"doc_count":10},{"key_as_string":"2017-02-07T05:12:00.000Z","key":1486444320000,"doc_count":12},{"key_as_string":"2017-02-07T05:13:00.000Z","key":1486444380000,"doc_count":12},{"key_as_string":"2017-02-07T05:14:00.000Z","key":1486444440000,"doc_count":12},{"key_as_string":"2017-02-07T05:15:00.000Z","key":1486444500000,"doc_count":11},{"key_as_string":"2017-02-07T05:16:00.000Z","key":1486444560000,"doc_count":10},{"key_as_string":"2017-02-07T05:17:00.000Z","key":1486444620000,"doc_count":12},{"key_as_string":"2017-02-07T05:18:00.000Z","key":1486444680000,"doc_count":9},{"key_as_string":"2017-02-07T05:19:00.000Z","key":1486444740000,"doc_count":14},{"key_as_string":"2017-02-07T05:20:00.000Z","key":1486444800000,"doc_count":7},{"key_as_string":"2017-02-07T05:21:00.000Z","key":1486444860000,"doc_count":12},{"key_as_string":"2017-02-07T05:22:00.000Z","key":1486444920000,"doc_count":10},{"key_as_string":"2017-02-07T05:23:00.000Z","key":1486444980000,"doc_count":14},{"key_as_string":"2017-02-07T05:24:00.000Z","key":1486445040000,"doc_count":14},{"key_as_string":"2017-02-07T05:25:00.000Z","key":1486445100000,"doc_count":5},{"key_as_string":"2017-02-07T05:26:00.000Z","key":1486445160000,"doc_count":10},{"key_as_string":"2017-02-07T05:27:00.000Z","key":1486445220000,"doc_count":10},{"key_as_string":"2017-02-07T05:28:00.000Z","key":1486445280000,"doc_count":13},{"key_as_string":"2017-02-07T05:29:00.000Z","key":1486445340000,"doc_count":12},{"key_as_string":"2017-02-07T05:30:00.000Z","key":1486445400000,"doc_count":13},{"key_as_string":"2017-02-07T05:31:00.000Z","key":1486445460000,"doc_count":13},{"key_as_string":"2017-02-07T05:32:00.000Z","key":1486445520000,"doc_count":13},{"key_as_string":"2017-02-07T05:33:00.000Z","key":1486445580000,"doc_count":16},{"key_as_string":"2017-02-07T05:34:00.000Z","key":1486445640000,"doc_count":10},{"key_as_string":"2017-02-07T05:35:00.000Z","key":1486445700000,"doc_count":16},{"key_as_string":"2017-02-07T05:36:00.000Z","key":1486445760000,"doc_count":14},{"key_as_string":"2017-02-07T05:37:00.000Z","key":1486445820000,"doc_count":10},{"key_as_string":"2017-02-07T05:38:00.000Z","key":1486445880000,"doc_count":11},{"key_as_string":"2017-02-07T05:39:00.000Z","key":1486445940000,"doc_count":13},{"key_as_string":"2017-02-07T05:40:00.000Z","key":1486446000000,"doc_count":16},{"key_as_string":"2017-02-07T05:41:00.000Z","key":1486446060000,"doc_count":9},{"key_as_string":"2017-02-07T05:42:00.000Z","key":1486446120000,"doc_count":10},{"key_as_string":"2017-02-07T05:43:00.000Z","key":1486446180000,"doc_count":16},{"key_as_string":"2017-02-07T05:44:00.000Z","key":1486446240000,"doc_count":18},{"key_as_string":"2017-02-07T05:45:00.000Z","key":1486446300000,"doc_count":10},{"key_as_string":"2017-02-07T05:46:00.000Z","key":1486446360000,"doc_count":9},{"key_as_string":"2017-02-07T05:47:00.000Z","key":1486446420000,"doc_count":7},{"key_as_string":"2017-02-07T05:48:00.000Z","key":1486446480000,"doc_count":10},{"key_as_string":"2017-02-07T05:49:00.000Z","key":1486446540000,"doc_count":17},{"key_as_string":"2017-02-07T05:50:00.000Z","key":1486446600000,"doc_count":8},{"key_as_string":"2017-02-07T05:51:00.000Z","key":1486446660000,"doc_count":10},{"key_as_string":"2017-02-07T05:52:00.000Z","key":1486446720000,"doc_count":10},{"key_as_string":"2017-02-07T05:53:00.000Z","key":1486446780000,"doc_count":13},{"key_as_string":"2017-02-07T05:54:00.000Z","key":1486446840000,"doc_count":9},{"key_as_string":"2017-02-07T05:55:00.000Z","key":1486446900000,"doc_count":11},{"key_as_string":"2017-02-07T05:56:00.000Z","key":1486446960000,"doc_count":14},{"key_as_string":"2017-02-07T05:57:00.000Z","key":1486447020000,"doc_count":15},{"key_as_string":"2017-02-07T05:58:00.000Z","key":1486447080000,"doc_count":14},{"key_as_string":"2017-02-07T05:59:00.000Z","key":1486447140000,"doc_count":6},{"key_as_string":"2017-02-07T06:00:00.000Z","key":1486447200000,"doc_count":14},{"key_as_string":"2017-02-07T06:01:00.000Z","key":1486447260000,"doc_count":18},{"key_as_string":"2017-02-07T06:02:00.000Z","key":1486447320000,"doc_count":9},{"key_as_string":"2017-02-07T06:03:00.000Z","key":1486447380000,"doc_count":14},{"key_as_string":"2017-02-07T06:04:00.000Z","key":1486447440000,"doc_count":12},{"key_as_string":"2017-02-07T06:05:00.000Z","key":1486447500000,"doc_count":14},{"key_as_string":"2017-02-07T06:06:00.000Z","key":1486447560000,"doc_count":11},{"key_as_string":"2017-02-07T06:07:00.000Z","key":1486447620000,"doc_count":12},{"key_as_string":"2017-02-07T06:08:00.000Z","key":1486447680000,"doc_count":20},{"key_as_string":"2017-02-07T06:09:00.000Z","key":1486447740000,"doc_count":9},{"key_as_string":"2017-02-07T06:10:00.000Z","key":1486447800000,"doc_count":10},{"key_as_string":"2017-02-07T06:11:00.000Z","key":1486447860000,"doc_count":13},{"key_as_string":"2017-02-07T06:12:00.000Z","key":1486447920000,"doc_count":10},{"key_as_string":"2017-02-07T06:13:00.000Z","key":1486447980000,"doc_count":13},{"key_as_string":"2017-02-07T06:14:00.000Z","key":1486448040000,"doc_count":10},{"key_as_string":"2017-02-07T06:15:00.000Z","key":1486448100000,"doc_count":17},{"key_as_string":"2017-02-07T06:16:00.000Z","key":1486448160000,"doc_count":7},{"key_as_string":"2017-02-07T06:17:00.000Z","key":1486448220000,"doc_count":9},{"key_as_string":"2017-02-07T06:18:00.000Z","key":1486448280000,"doc_count":12},{"key_as_string":"2017-02-07T06:19:00.000Z","key":1486448340000,"doc_count":8},{"key_as_string":"2017-02-07T06:20:00.000Z","key":1486448400000,"doc_count":21},{"key_as_string":"2017-02-07T06:21:00.000Z","key":1486448460000,"doc_count":10},{"key_as_string":"2017-02-07T06:22:00.000Z","key":1486448520000,"doc_count":13},{"key_as_string":"2017-02-07T06:23:00.000Z","key":1486448580000,"doc_count":12},{"key_as_string":"2017-02-07T06:24:00.000Z","key":1486448640000,"doc_count":13},{"key_as_string":"2017-02-07T06:25:00.000Z","key":1486448700000,"doc_count":11},{"key_as_string":"2017-02-07T06:26:00.000Z","key":1486448760000,"doc_count":16},{"key_as_string":"2017-02-07T06:27:00.000Z","key":1486448820000,"doc_count":5},{"key_as_string":"2017-02-07T06:28:00.000Z","key":1486448880000,"doc_count":11},{"key_as_string":"2017-02-07T06:29:00.000Z","key":1486448940000,"doc_count":14},{"key_as_string":"2017-02-07T06:30:00.000Z","key":1486449000000,"doc_count":8},{"key_as_string":"2017-02-07T06:31:00.000Z","key":1486449060000,"doc_count":13},{"key_as_string":"2017-02-07T06:32:00.000Z","key":1486449120000,"doc_count":17},{"key_as_string":"2017-02-07T06:33:00.000Z","key":1486449180000,"doc_count":15},{"key_as_string":"2017-02-07T06:34:00.000Z","key":1486449240000,"doc_count":6},{"key_as_string":"2017-02-07T06:35:00.000Z","key":1486449300000,"doc_count":13},{"key_as_string":"2017-02-07T06:36:00.000Z","key":1486449360000,"doc_count":8},{"key_as_string":"2017-02-07T06:37:00.000Z","key":1486449420000,"doc_count":12},{"key_as_string":"2017-02-07T06:38:00.000Z","key":1486449480000,"doc_count":13},{"key_as_string":"2017-02-07T06:39:00.000Z","key":1486449540000,"doc_count":12},{"key_as_string":"2017-02-07T06:40:00.000Z","key":1486449600000,"doc_count":11},{"key_as_string":"2017-02-07T06:41:00.000Z","key":1486449660000,"doc_count":14},{"key_as_string":"2017-02-07T06:42:00.000Z","key":1486449720000,"doc_count":6},{"key_as_string":"2017-02-07T06:43:00.000Z","key":1486449780000,"doc_count":13},{"key_as_string":"2017-02-07T06:44:00.000Z","key":1486449840000,"doc_count":11},{"key_as_string":"2017-02-07T06:45:00.000Z","key":1486449900000,"doc_count":11},{"key_as_string":"2017-02-07T06:46:00.000Z","key":1486449960000,"doc_count":21},{"key_as_string":"2017-02-07T06:47:00.000Z","key":1486450020000,"doc_count":9},{"key_as_string":"2017-02-07T06:48:00.000Z","key":1486450080000,"doc_count":11},{"key_as_string":"2017-02-07T06:49:00.000Z","key":1486450140000,"doc_count":10},{"key_as_string":"2017-02-07T06:50:00.000Z","key":1486450200000,"doc_count":11},{"key_as_string":"2017-02-07T06:51:00.000Z","key":1486450260000,"doc_count":11},{"key_as_string":"2017-02-07T06:52:00.000Z","key":1486450320000,"doc_count":12},{"key_as_string":"2017-02-07T06:53:00.000Z","key":1486450380000,"doc_count":8},{"key_as_string":"2017-02-07T06:54:00.000Z","key":1486450440000,"doc_count":13},{"key_as_string":"2017-02-07T06:55:00.000Z","key":1486450500000,"doc_count":7},{"key_as_string":"2017-02-07T06:56:00.000Z","key":1486450560000,"doc_count":9},{"key_as_string":"2017-02-07T06:57:00.000Z","key":1486450620000,"doc_count":7},{"key_as_string":"2017-02-07T06:58:00.000Z","key":1486450680000,"doc_count":10},{"key_as_string":"2017-02-07T06:59:00.000Z","key":1486450740000,"doc_count":19},{"key_as_string":"2017-02-07T07:00:00.000Z","key":1486450800000,"doc_count":11},{"key_as_string":"2017-02-07T07:01:00.000Z","key":1486450860000,"doc_count":9},{"key_as_string":"2017-02-07T07:02:00.000Z","key":1486450920000,"doc_count":14},{"key_as_string":"2017-02-07T07:03:00.000Z","key":1486450980000,"doc_count":13},{"key_as_string":"2017-02-07T07:04:00.000Z","key":1486451040000,"doc_count":14},{"key_as_string":"2017-02-07T07:05:00.000Z","key":1486451100000,"doc_count":10},{"key_as_string":"2017-02-07T07:06:00.000Z","key":1486451160000,"doc_count":14},{"key_as_string":"2017-02-07T07:07:00.000Z","key":1486451220000,"doc_count":8},{"key_as_string":"2017-02-07T07:08:00.000Z","key":1486451280000,"doc_count":10},{"key_as_string":"2017-02-07T07:09:00.000Z","key":1486451340000,"doc_count":15},{"key_as_string":"2017-02-07T07:10:00.000Z","key":1486451400000,"doc_count":17},{"key_as_string":"2017-02-07T07:11:00.000Z","key":1486451460000,"doc_count":10},{"key_as_string":"2017-02-07T07:12:00.000Z","key":1486451520000,"doc_count":10},{"key_as_string":"2017-02-07T07:13:00.000Z","key":1486451580000,"doc_count":10},{"key_as_string":"2017-02-07T07:14:00.000Z","key":1486451640000,"doc_count":10},{"key_as_string":"2017-02-07T07:15:00.000Z","key":1486451700000,"doc_count":12},{"key_as_string":"2017-02-07T07:16:00.000Z","key":1486451760000,"doc_count":12},{"key_as_string":"2017-02-07T07:17:00.000Z","key":1486451820000,"doc_count":17},{"key_as_string":"2017-02-07T07:18:00.000Z","key":1486451880000,"doc_count":10},{"key_as_string":"2017-02-07T07:19:00.000Z","key":1486451940000,"doc_count":13},{"key_as_string":"2017-02-07T07:20:00.000Z","key":1486452000000,"doc_count":12},{"key_as_string":"2017-02-07T07:21:00.000Z","key":1486452060000,"doc_count":13},{"key_as_string":"2017-02-07T07:22:00.000Z","key":1486452120000,"doc_count":15},{"key_as_string":"2017-02-07T07:23:00.000Z","key":1486452180000,"doc_count":13},{"key_as_string":"2017-02-07T07:24:00.000Z","key":1486452240000,"doc_count":12},{"key_as_string":"2017-02-07T07:25:00.000Z","key":1486452300000,"doc_count":12},{"key_as_string":"2017-02-07T07:26:00.000Z","key":1486452360000,"doc_count":11},{"key_as_string":"2017-02-07T07:27:00.000Z","key":1486452420000,"doc_count":11},{"key_as_string":"2017-02-07T07:28:00.000Z","key":1486452480000,"doc_count":14},{"key_as_string":"2017-02-07T07:29:00.000Z","key":1486452540000,"doc_count":16},{"key_as_string":"2017-02-07T07:30:00.000Z","key":1486452600000,"doc_count":9},{"key_as_string":"2017-02-07T07:31:00.000Z","key":1486452660000,"doc_count":12},{"key_as_string":"2017-02-07T07:32:00.000Z","key":1486452720000,"doc_count":17},{"key_as_string":"2017-02-07T07:33:00.000Z","key":1486452780000,"doc_count":9},{"key_as_string":"2017-02-07T07:34:00.000Z","key":1486452840000,"doc_count":15},{"key_as_string":"2017-02-07T07:35:00.000Z","key":1486452900000,"doc_count":13},{"key_as_string":"2017-02-07T07:36:00.000Z","key":1486452960000,"doc_count":14},{"key_as_string":"2017-02-07T07:37:00.000Z","key":1486453020000,"doc_count":14},{"key_as_string":"2017-02-07T07:38:00.000Z","key":1486453080000,"doc_count":9},{"key_as_string":"2017-02-07T07:39:00.000Z","key":1486453140000,"doc_count":10},{"key_as_string":"2017-02-07T07:40:00.000Z","key":1486453200000,"doc_count":12},{"key_as_string":"2017-02-07T07:41:00.000Z","key":1486453260000,"doc_count":11},{"key_as_string":"2017-02-07T07:42:00.000Z","key":1486453320000,"doc_count":18},{"key_as_string":"2017-02-07T07:43:00.000Z","key":1486453380000,"doc_count":7},{"key_as_string":"2017-02-07T07:44:00.000Z","key":1486453440000,"doc_count":13},{"key_as_string":"2017-02-07T07:45:00.000Z","key":1486453500000,"doc_count":14},{"key_as_string":"2017-02-07T07:46:00.000Z","key":1486453560000,"doc_count":13},{"key_as_string":"2017-02-07T07:47:00.000Z","key":1486453620000,"doc_count":15},{"key_as_string":"2017-02-07T07:48:00.000Z","key":1486453680000,"doc_count":13},{"key_as_string":"2017-02-07T07:49:00.000Z","key":1486453740000,"doc_count":15},{"key_as_string":"2017-02-07T07:50:00.000Z","key":1486453800000,"doc_count":12},{"key_as_string":"2017-02-07T07:51:00.000Z","key":1486453860000,"doc_count":15},{"key_as_string":"2017-02-07T07:52:00.000Z","key":1486453920000,"doc_count":12},{"key_as_string":"2017-02-07T07:53:00.000Z","key":1486453980000,"doc_count":9},{"key_as_string":"2017-02-07T07:54:00.000Z","key":1486454040000,"doc_count":12},{"key_as_string":"2017-02-07T07:55:00.000Z","key":1486454100000,"doc_count":13},{"key_as_string":"2017-02-07T07:56:00.000Z","key":1486454160000,"doc_count":12},{"key_as_string":"2017-02-07T07:57:00.000Z","key":1486454220000,"doc_count":9},{"key_as_string":"2017-02-07T07:58:00.000Z","key":1486454280000,"doc_count":12},{"key_as_string":"2017-02-07T07:59:00.000Z","key":1486454340000,"doc_count":12},{"key_as_string":"2017-02-07T08:00:00.000Z","key":1486454400000,"doc_count":14},{"key_as_string":"2017-02-07T08:01:00.000Z","key":1486454460000,"doc_count":13},{"key_as_string":"2017-02-07T08:02:00.000Z","key":1486454520000,"doc_count":11},{"key_as_string":"2017-02-07T08:03:00.000Z","key":1486454580000,"doc_count":14},{"key_as_string":"2017-02-07T08:04:00.000Z","key":1486454640000,"doc_count":6},{"key_as_string":"2017-02-07T08:05:00.000Z","key":1486454700000,"doc_count":13},{"key_as_string":"2017-02-07T08:06:00.000Z","key":1486454760000,"doc_count":11},{"key_as_string":"2017-02-07T08:07:00.000Z","key":1486454820000,"doc_count":14},{"key_as_string":"2017-02-07T08:08:00.000Z","key":1486454880000,"doc_count":14},{"key_as_string":"2017-02-07T08:09:00.000Z","key":1486454940000,"doc_count":9},{"key_as_string":"2017-02-07T08:10:00.000Z","key":1486455000000,"doc_count":11},{"key_as_string":"2017-02-07T08:11:00.000Z","key":1486455060000,"doc_count":8},{"key_as_string":"2017-02-07T08:12:00.000Z","key":1486455120000,"doc_count":14},{"key_as_string":"2017-02-07T08:13:00.000Z","key":1486455180000,"doc_count":9},{"key_as_string":"2017-02-07T08:14:00.000Z","key":1486455240000,"doc_count":10},{"key_as_string":"2017-02-07T08:15:00.000Z","key":1486455300000,"doc_count":14},{"key_as_string":"2017-02-07T08:16:00.000Z","key":1486455360000,"doc_count":12},{"key_as_string":"2017-02-07T08:17:00.000Z","key":1486455420000,"doc_count":11},{"key_as_string":"2017-02-07T08:18:00.000Z","key":1486455480000,"doc_count":18},{"key_as_string":"2017-02-07T08:19:00.000Z","key":1486455540000,"doc_count":13},{"key_as_string":"2017-02-07T08:20:00.000Z","key":1486455600000,"doc_count":13},{"key_as_string":"2017-02-07T08:21:00.000Z","key":1486455660000,"doc_count":10},{"key_as_string":"2017-02-07T08:22:00.000Z","key":1486455720000,"doc_count":10},{"key_as_string":"2017-02-07T08:23:00.000Z","key":1486455780000,"doc_count":13},{"key_as_string":"2017-02-07T08:24:00.000Z","key":1486455840000,"doc_count":11},{"key_as_string":"2017-02-07T08:25:00.000Z","key":1486455900000,"doc_count":12},{"key_as_string":"2017-02-07T08:26:00.000Z","key":1486455960000,"doc_count":20},{"key_as_string":"2017-02-07T08:27:00.000Z","key":1486456020000,"doc_count":15},{"key_as_string":"2017-02-07T08:28:00.000Z","key":1486456080000,"doc_count":12},{"key_as_string":"2017-02-07T08:29:00.000Z","key":1486456140000,"doc_count":14},{"key_as_string":"2017-02-07T08:30:00.000Z","key":1486456200000,"doc_count":9},{"key_as_string":"2017-02-07T08:31:00.000Z","key":1486456260000,"doc_count":17},{"key_as_string":"2017-02-07T08:32:00.000Z","key":1486456320000,"doc_count":11},{"key_as_string":"2017-02-07T08:33:00.000Z","key":1486456380000,"doc_count":15},{"key_as_string":"2017-02-07T08:34:00.000Z","key":1486456440000,"doc_count":11},{"key_as_string":"2017-02-07T08:35:00.000Z","key":1486456500000,"doc_count":15},{"key_as_string":"2017-02-07T08:36:00.000Z","key":1486456560000,"doc_count":17},{"key_as_string":"2017-02-07T08:37:00.000Z","key":1486456620000,"doc_count":8},{"key_as_string":"2017-02-07T08:38:00.000Z","key":1486456680000,"doc_count":13},{"key_as_string":"2017-02-07T08:39:00.000Z","key":1486456740000,"doc_count":10},{"key_as_string":"2017-02-07T08:40:00.000Z","key":1486456800000,"doc_count":11},{"key_as_string":"2017-02-07T08:41:00.000Z","key":1486456860000,"doc_count":12},{"key_as_string":"2017-02-07T08:42:00.000Z","key":1486456920000,"doc_count":15},{"key_as_string":"2017-02-07T08:43:00.000Z","key":1486456980000,"doc_count":12},{"key_as_string":"2017-02-07T08:44:00.000Z","key":1486457040000,"doc_count":14},{"key_as_string":"2017-02-07T08:45:00.000Z","key":1486457100000,"doc_count":14},{"key_as_string":"2017-02-07T08:46:00.000Z","key":1486457160000,"doc_count":15},{"key_as_string":"2017-02-07T08:47:00.000Z","key":1486457220000,"doc_count":13},{"key_as_string":"2017-02-07T08:48:00.000Z","key":1486457280000,"doc_count":10},{"key_as_string":"2017-02-07T08:49:00.000Z","key":1486457340000,"doc_count":12},{"key_as_string":"2017-02-07T08:50:00.000Z","key":1486457400000,"doc_count":14},{"key_as_string":"2017-02-07T08:51:00.000Z","key":1486457460000,"doc_count":13},{"key_as_string":"2017-02-07T08:52:00.000Z","key":1486457520000,"doc_count":11},{"key_as_string":"2017-02-07T08:53:00.000Z","key":1486457580000,"doc_count":14},{"key_as_string":"2017-02-07T08:54:00.000Z","key":1486457640000,"doc_count":14},{"key_as_string":"2017-02-07T08:55:00.000Z","key":1486457700000,"doc_count":9},{"key_as_string":"2017-02-07T08:56:00.000Z","key":1486457760000,"doc_count":13},{"key_as_string":"2017-02-07T08:57:00.000Z","key":1486457820000,"doc_count":16},{"key_as_string":"2017-02-07T08:58:00.000Z","key":1486457880000,"doc_count":14},{"key_as_string":"2017-02-07T08:59:00.000Z","key":1486457940000,"doc_count":9},{"key_as_string":"2017-02-07T09:00:00.000Z","key":1486458000000,"doc_count":15},{"key_as_string":"2017-02-07T09:01:00.000Z","key":1486458060000,"doc_count":13},{"key_as_string":"2017-02-07T09:02:00.000Z","key":1486458120000,"doc_count":11},{"key_as_string":"2017-02-07T09:03:00.000Z","key":1486458180000,"doc_count":23},{"key_as_string":"2017-02-07T09:04:00.000Z","key":1486458240000,"doc_count":14},{"key_as_string":"2017-02-07T09:05:00.000Z","key":1486458300000,"doc_count":6},{"key_as_string":"2017-02-07T09:06:00.000Z","key":1486458360000,"doc_count":14},{"key_as_string":"2017-02-07T09:07:00.000Z","key":1486458420000,"doc_count":15},{"key_as_string":"2017-02-07T09:08:00.000Z","key":1486458480000,"doc_count":15},{"key_as_string":"2017-02-07T09:09:00.000Z","key":1486458540000,"doc_count":11},{"key_as_string":"2017-02-07T09:10:00.000Z","key":1486458600000,"doc_count":17},{"key_as_string":"2017-02-07T09:11:00.000Z","key":1486458660000,"doc_count":16},{"key_as_string":"2017-02-07T09:12:00.000Z","key":1486458720000,"doc_count":15},{"key_as_string":"2017-02-07T09:13:00.000Z","key":1486458780000,"doc_count":15},{"key_as_string":"2017-02-07T09:14:00.000Z","key":1486458840000,"doc_count":18},{"key_as_string":"2017-02-07T09:15:00.000Z","key":1486458900000,"doc_count":13},{"key_as_string":"2017-02-07T09:16:00.000Z","key":1486458960000,"doc_count":9},{"key_as_string":"2017-02-07T09:17:00.000Z","key":1486459020000,"doc_count":19},{"key_as_string":"2017-02-07T09:18:00.000Z","key":1486459080000,"doc_count":11},{"key_as_string":"2017-02-07T09:19:00.000Z","key":1486459140000,"doc_count":8},{"key_as_string":"2017-02-07T09:20:00.000Z","key":1486459200000,"doc_count":18},{"key_as_string":"2017-02-07T09:21:00.000Z","key":1486459260000,"doc_count":16},{"key_as_string":"2017-02-07T09:22:00.000Z","key":1486459320000,"doc_count":13},{"key_as_string":"2017-02-07T09:23:00.000Z","key":1486459380000,"doc_count":14},{"key_as_string":"2017-02-07T09:24:00.000Z","key":1486459440000,"doc_count":16},{"key_as_string":"2017-02-07T09:25:00.000Z","key":1486459500000,"doc_count":13},{"key_as_string":"2017-02-07T09:26:00.000Z","key":1486459560000,"doc_count":13},{"key_as_string":"2017-02-07T09:27:00.000Z","key":1486459620000,"doc_count":16},{"key_as_string":"2017-02-07T09:28:00.000Z","key":1486459680000,"doc_count":13},{"key_as_string":"2017-02-07T09:29:00.000Z","key":1486459740000,"doc_count":14},{"key_as_string":"2017-02-07T09:30:00.000Z","key":1486459800000,"doc_count":13},{"key_as_string":"2017-02-07T09:31:00.000Z","key":1486459860000,"doc_count":16},{"key_as_string":"2017-02-07T09:32:00.000Z","key":1486459920000,"doc_count":18},{"key_as_string":"2017-02-07T09:33:00.000Z","key":1486459980000,"doc_count":7},{"key_as_string":"2017-02-07T09:34:00.000Z","key":1486460040000,"doc_count":14},{"key_as_string":"2017-02-07T09:35:00.000Z","key":1486460100000,"doc_count":15},{"key_as_string":"2017-02-07T09:36:00.000Z","key":1486460160000,"doc_count":13},{"key_as_string":"2017-02-07T09:37:00.000Z","key":1486460220000,"doc_count":16},{"key_as_string":"2017-02-07T09:38:00.000Z","key":1486460280000,"doc_count":15},{"key_as_string":"2017-02-07T09:39:00.000Z","key":1486460340000,"doc_count":12},{"key_as_string":"2017-02-07T09:40:00.000Z","key":1486460400000,"doc_count":15},{"key_as_string":"2017-02-07T09:41:00.000Z","key":1486460460000,"doc_count":13},{"key_as_string":"2017-02-07T09:42:00.000Z","key":1486460520000,"doc_count":10},{"key_as_string":"2017-02-07T09:43:00.000Z","key":1486460580000,"doc_count":17},{"key_as_string":"2017-02-07T09:44:00.000Z","key":1486460640000,"doc_count":16},{"key_as_string":"2017-02-07T09:45:00.000Z","key":1486460700000,"doc_count":16},{"key_as_string":"2017-02-07T09:46:00.000Z","key":1486460760000,"doc_count":12},{"key_as_string":"2017-02-07T09:47:00.000Z","key":1486460820000,"doc_count":14},{"key_as_string":"2017-02-07T09:48:00.000Z","key":1486460880000,"doc_count":14},{"key_as_string":"2017-02-07T09:49:00.000Z","key":1486460940000,"doc_count":10},{"key_as_string":"2017-02-07T09:50:00.000Z","key":1486461000000,"doc_count":14},{"key_as_string":"2017-02-07T09:51:00.000Z","key":1486461060000,"doc_count":16},{"key_as_string":"2017-02-07T09:52:00.000Z","key":1486461120000,"doc_count":14},{"key_as_string":"2017-02-07T09:53:00.000Z","key":1486461180000,"doc_count":11},{"key_as_string":"2017-02-07T09:54:00.000Z","key":1486461240000,"doc_count":11},{"key_as_string":"2017-02-07T09:55:00.000Z","key":1486461300000,"doc_count":14},{"key_as_string":"2017-02-07T09:56:00.000Z","key":1486461360000,"doc_count":14},{"key_as_string":"2017-02-07T09:57:00.000Z","key":1486461420000,"doc_count":13},{"key_as_string":"2017-02-07T09:58:00.000Z","key":1486461480000,"doc_count":14},{"key_as_string":"2017-02-07T09:59:00.000Z","key":1486461540000,"doc_count":13},{"key_as_string":"2017-02-07T10:00:00.000Z","key":1486461600000,"doc_count":17},{"key_as_string":"2017-02-07T10:01:00.000Z","key":1486461660000,"doc_count":17},{"key_as_string":"2017-02-07T10:02:00.000Z","key":1486461720000,"doc_count":6},{"key_as_string":"2017-02-07T10:03:00.000Z","key":1486461780000,"doc_count":13},{"key_as_string":"2017-02-07T10:04:00.000Z","key":1486461840000,"doc_count":15},{"key_as_string":"2017-02-07T10:05:00.000Z","key":1486461900000,"doc_count":10},{"key_as_string":"2017-02-07T10:06:00.000Z","key":1486461960000,"doc_count":11},{"key_as_string":"2017-02-07T10:07:00.000Z","key":1486462020000,"doc_count":15},{"key_as_string":"2017-02-07T10:08:00.000Z","key":1486462080000,"doc_count":14},{"key_as_string":"2017-02-07T10:09:00.000Z","key":1486462140000,"doc_count":11},{"key_as_string":"2017-02-07T10:10:00.000Z","key":1486462200000,"doc_count":19},{"key_as_string":"2017-02-07T10:11:00.000Z","key":1486462260000,"doc_count":10},{"key_as_string":"2017-02-07T10:12:00.000Z","key":1486462320000,"doc_count":12},{"key_as_string":"2017-02-07T10:13:00.000Z","key":1486462380000,"doc_count":12},{"key_as_string":"2017-02-07T10:14:00.000Z","key":1486462440000,"doc_count":20},{"key_as_string":"2017-02-07T10:15:00.000Z","key":1486462500000,"doc_count":15},{"key_as_string":"2017-02-07T10:16:00.000Z","key":1486462560000,"doc_count":13},{"key_as_string":"2017-02-07T10:17:00.000Z","key":1486462620000,"doc_count":16},{"key_as_string":"2017-02-07T10:18:00.000Z","key":1486462680000,"doc_count":14},{"key_as_string":"2017-02-07T10:19:00.000Z","key":1486462740000,"doc_count":11},{"key_as_string":"2017-02-07T10:20:00.000Z","key":1486462800000,"doc_count":15},{"key_as_string":"2017-02-07T10:21:00.000Z","key":1486462860000,"doc_count":14},{"key_as_string":"2017-02-07T10:22:00.000Z","key":1486462920000,"doc_count":11},{"key_as_string":"2017-02-07T10:23:00.000Z","key":1486462980000,"doc_count":12},{"key_as_string":"2017-02-07T10:24:00.000Z","key":1486463040000,"doc_count":16},{"key_as_string":"2017-02-07T10:25:00.000Z","key":1486463100000,"doc_count":14},{"key_as_string":"2017-02-07T10:26:00.000Z","key":1486463160000,"doc_count":18},{"key_as_string":"2017-02-07T10:27:00.000Z","key":1486463220000,"doc_count":13},{"key_as_string":"2017-02-07T10:28:00.000Z","key":1486463280000,"doc_count":18},{"key_as_string":"2017-02-07T10:29:00.000Z","key":1486463340000,"doc_count":14},{"key_as_string":"2017-02-07T10:30:00.000Z","key":1486463400000,"doc_count":15},{"key_as_string":"2017-02-07T10:31:00.000Z","key":1486463460000,"doc_count":16},{"key_as_string":"2017-02-07T10:32:00.000Z","key":1486463520000,"doc_count":16},{"key_as_string":"2017-02-07T10:33:00.000Z","key":1486463580000,"doc_count":16},{"key_as_string":"2017-02-07T10:34:00.000Z","key":1486463640000,"doc_count":9},{"key_as_string":"2017-02-07T10:35:00.000Z","key":1486463700000,"doc_count":13},{"key_as_string":"2017-02-07T10:36:00.000Z","key":1486463760000,"doc_count":16},{"key_as_string":"2017-02-07T10:37:00.000Z","key":1486463820000,"doc_count":14},{"key_as_string":"2017-02-07T10:38:00.000Z","key":1486463880000,"doc_count":14},{"key_as_string":"2017-02-07T10:39:00.000Z","key":1486463940000,"doc_count":12},{"key_as_string":"2017-02-07T10:40:00.000Z","key":1486464000000,"doc_count":12},{"key_as_string":"2017-02-07T10:41:00.000Z","key":1486464060000,"doc_count":15},{"key_as_string":"2017-02-07T10:42:00.000Z","key":1486464120000,"doc_count":10},{"key_as_string":"2017-02-07T10:43:00.000Z","key":1486464180000,"doc_count":11},{"key_as_string":"2017-02-07T10:44:00.000Z","key":1486464240000,"doc_count":14},{"key_as_string":"2017-02-07T10:45:00.000Z","key":1486464300000,"doc_count":11},{"key_as_string":"2017-02-07T10:46:00.000Z","key":1486464360000,"doc_count":16},{"key_as_string":"2017-02-07T10:47:00.000Z","key":1486464420000,"doc_count":17},{"key_as_string":"2017-02-07T10:48:00.000Z","key":1486464480000,"doc_count":14},{"key_as_string":"2017-02-07T10:49:00.000Z","key":1486464540000,"doc_count":17},{"key_as_string":"2017-02-07T10:50:00.000Z","key":1486464600000,"doc_count":14},{"key_as_string":"2017-02-07T10:51:00.000Z","key":1486464660000,"doc_count":14},{"key_as_string":"2017-02-07T10:52:00.000Z","key":1486464720000,"doc_count":13},{"key_as_string":"2017-02-07T10:53:00.000Z","key":1486464780000,"doc_count":14},{"key_as_string":"2017-02-07T10:54:00.000Z","key":1486464840000,"doc_count":13},{"key_as_string":"2017-02-07T10:55:00.000Z","key":1486464900000,"doc_count":10},{"key_as_string":"2017-02-07T10:56:00.000Z","key":1486464960000,"doc_count":14},{"key_as_string":"2017-02-07T10:57:00.000Z","key":1486465020000,"doc_count":9},{"key_as_string":"2017-02-07T10:58:00.000Z","key":1486465080000,"doc_count":19},{"key_as_string":"2017-02-07T10:59:00.000Z","key":1486465140000,"doc_count":12},{"key_as_string":"2017-02-07T11:00:00.000Z","key":1486465200000,"doc_count":20},{"key_as_string":"2017-02-07T11:01:00.000Z","key":1486465260000,"doc_count":11},{"key_as_string":"2017-02-07T11:02:00.000Z","key":1486465320000,"doc_count":16},{"key_as_string":"2017-02-07T11:03:00.000Z","key":1486465380000,"doc_count":14},{"key_as_string":"2017-02-07T11:04:00.000Z","key":1486465440000,"doc_count":14},{"key_as_string":"2017-02-07T11:05:00.000Z","key":1486465500000,"doc_count":11},{"key_as_string":"2017-02-07T11:06:00.000Z","key":1486465560000,"doc_count":11},{"key_as_string":"2017-02-07T11:07:00.000Z","key":1486465620000,"doc_count":18},{"key_as_string":"2017-02-07T11:08:00.000Z","key":1486465680000,"doc_count":14},{"key_as_string":"2017-02-07T11:09:00.000Z","key":1486465740000,"doc_count":14},{"key_as_string":"2017-02-07T11:10:00.000Z","key":1486465800000,"doc_count":9},{"key_as_string":"2017-02-07T11:11:00.000Z","key":1486465860000,"doc_count":18},{"key_as_string":"2017-02-07T11:12:00.000Z","key":1486465920000,"doc_count":18},{"key_as_string":"2017-02-07T11:13:00.000Z","key":1486465980000,"doc_count":10},{"key_as_string":"2017-02-07T11:14:00.000Z","key":1486466040000,"doc_count":10},{"key_as_string":"2017-02-07T11:15:00.000Z","key":1486466100000,"doc_count":17},{"key_as_string":"2017-02-07T11:16:00.000Z","key":1486466160000,"doc_count":16},{"key_as_string":"2017-02-07T11:17:00.000Z","key":1486466220000,"doc_count":15},{"key_as_string":"2017-02-07T11:18:00.000Z","key":1486466280000,"doc_count":17},{"key_as_string":"2017-02-07T11:19:00.000Z","key":1486466340000,"doc_count":18},{"key_as_string":"2017-02-07T11:20:00.000Z","key":1486466400000,"doc_count":11},{"key_as_string":"2017-02-07T11:21:00.000Z","key":1486466460000,"doc_count":18},{"key_as_string":"2017-02-07T11:22:00.000Z","key":1486466520000,"doc_count":13},{"key_as_string":"2017-02-07T11:23:00.000Z","key":1486466580000,"doc_count":10},{"key_as_string":"2017-02-07T11:24:00.000Z","key":1486466640000,"doc_count":12},{"key_as_string":"2017-02-07T11:25:00.000Z","key":1486466700000,"doc_count":13},{"key_as_string":"2017-02-07T11:26:00.000Z","key":1486466760000,"doc_count":16},{"key_as_string":"2017-02-07T11:27:00.000Z","key":1486466820000,"doc_count":12},{"key_as_string":"2017-02-07T11:28:00.000Z","key":1486466880000,"doc_count":12},{"key_as_string":"2017-02-07T11:29:00.000Z","key":1486466940000,"doc_count":18},{"key_as_string":"2017-02-07T11:30:00.000Z","key":1486467000000,"doc_count":11},{"key_as_string":"2017-02-07T11:31:00.000Z","key":1486467060000,"doc_count":13},{"key_as_string":"2017-02-07T11:32:00.000Z","key":1486467120000,"doc_count":13},{"key_as_string":"2017-02-07T11:33:00.000Z","key":1486467180000,"doc_count":24},{"key_as_string":"2017-02-07T11:34:00.000Z","key":1486467240000,"doc_count":12},{"key_as_string":"2017-02-07T11:35:00.000Z","key":1486467300000,"doc_count":13},{"key_as_string":"2017-02-07T11:36:00.000Z","key":1486467360000,"doc_count":16},{"key_as_string":"2017-02-07T11:37:00.000Z","key":1486467420000,"doc_count":16},{"key_as_string":"2017-02-07T11:38:00.000Z","key":1486467480000,"doc_count":14},{"key_as_string":"2017-02-07T11:39:00.000Z","key":1486467540000,"doc_count":12},{"key_as_string":"2017-02-07T11:40:00.000Z","key":1486467600000,"doc_count":14},{"key_as_string":"2017-02-07T11:41:00.000Z","key":1486467660000,"doc_count":14},{"key_as_string":"2017-02-07T11:42:00.000Z","key":1486467720000,"doc_count":16},{"key_as_string":"2017-02-07T11:43:00.000Z","key":1486467780000,"doc_count":19},{"key_as_string":"2017-02-07T11:44:00.000Z","key":1486467840000,"doc_count":9},{"key_as_string":"2017-02-07T11:45:00.000Z","key":1486467900000,"doc_count":14},{"key_as_string":"2017-02-07T11:46:00.000Z","key":1486467960000,"doc_count":8},{"key_as_string":"2017-02-07T11:47:00.000Z","key":1486468020000,"doc_count":14},{"key_as_string":"2017-02-07T11:48:00.000Z","key":1486468080000,"doc_count":11},{"key_as_string":"2017-02-07T11:49:00.000Z","key":1486468140000,"doc_count":10},{"key_as_string":"2017-02-07T11:50:00.000Z","key":1486468200000,"doc_count":13},{"key_as_string":"2017-02-07T11:51:00.000Z","key":1486468260000,"doc_count":14},{"key_as_string":"2017-02-07T11:52:00.000Z","key":1486468320000,"doc_count":13},{"key_as_string":"2017-02-07T11:53:00.000Z","key":1486468380000,"doc_count":15},{"key_as_string":"2017-02-07T11:54:00.000Z","key":1486468440000,"doc_count":11},{"key_as_string":"2017-02-07T11:55:00.000Z","key":1486468500000,"doc_count":11},{"key_as_string":"2017-02-07T11:56:00.000Z","key":1486468560000,"doc_count":13},{"key_as_string":"2017-02-07T11:57:00.000Z","key":1486468620000,"doc_count":17},{"key_as_string":"2017-02-07T11:58:00.000Z","key":1486468680000,"doc_count":19},{"key_as_string":"2017-02-07T11:59:00.000Z","key":1486468740000,"doc_count":15},{"key_as_string":"2017-02-07T12:00:00.000Z","key":1486468800000,"doc_count":14},{"key_as_string":"2017-02-07T12:01:00.000Z","key":1486468860000,"doc_count":14},{"key_as_string":"2017-02-07T12:02:00.000Z","key":1486468920000,"doc_count":19},{"key_as_string":"2017-02-07T12:03:00.000Z","key":1486468980000,"doc_count":16},{"key_as_string":"2017-02-07T12:04:00.000Z","key":1486469040000,"doc_count":13},{"key_as_string":"2017-02-07T12:05:00.000Z","key":1486469100000,"doc_count":20},{"key_as_string":"2017-02-07T12:06:00.000Z","key":1486469160000,"doc_count":9},{"key_as_string":"2017-02-07T12:07:00.000Z","key":1486469220000,"doc_count":12},{"key_as_string":"2017-02-07T12:08:00.000Z","key":1486469280000,"doc_count":14},{"key_as_string":"2017-02-07T12:09:00.000Z","key":1486469340000,"doc_count":18},{"key_as_string":"2017-02-07T12:10:00.000Z","key":1486469400000,"doc_count":10},{"key_as_string":"2017-02-07T12:11:00.000Z","key":1486469460000,"doc_count":11},{"key_as_string":"2017-02-07T12:12:00.000Z","key":1486469520000,"doc_count":15},{"key_as_string":"2017-02-07T12:13:00.000Z","key":1486469580000,"doc_count":10},{"key_as_string":"2017-02-07T12:14:00.000Z","key":1486469640000,"doc_count":14},{"key_as_string":"2017-02-07T12:15:00.000Z","key":1486469700000,"doc_count":18},{"key_as_string":"2017-02-07T12:16:00.000Z","key":1486469760000,"doc_count":15},{"key_as_string":"2017-02-07T12:17:00.000Z","key":1486469820000,"doc_count":12},{"key_as_string":"2017-02-07T12:18:00.000Z","key":1486469880000,"doc_count":7},{"key_as_string":"2017-02-07T12:19:00.000Z","key":1486469940000,"doc_count":17},{"key_as_string":"2017-02-07T12:20:00.000Z","key":1486470000000,"doc_count":14},{"key_as_string":"2017-02-07T12:21:00.000Z","key":1486470060000,"doc_count":15},{"key_as_string":"2017-02-07T12:22:00.000Z","key":1486470120000,"doc_count":17},{"key_as_string":"2017-02-07T12:23:00.000Z","key":1486470180000,"doc_count":17},{"key_as_string":"2017-02-07T12:24:00.000Z","key":1486470240000,"doc_count":12},{"key_as_string":"2017-02-07T12:25:00.000Z","key":1486470300000,"doc_count":15},{"key_as_string":"2017-02-07T12:26:00.000Z","key":1486470360000,"doc_count":8},{"key_as_string":"2017-02-07T12:27:00.000Z","key":1486470420000,"doc_count":19},{"key_as_string":"2017-02-07T12:28:00.000Z","key":1486470480000,"doc_count":10},{"key_as_string":"2017-02-07T12:29:00.000Z","key":1486470540000,"doc_count":13},{"key_as_string":"2017-02-07T12:30:00.000Z","key":1486470600000,"doc_count":14},{"key_as_string":"2017-02-07T12:31:00.000Z","key":1486470660000,"doc_count":17},{"key_as_string":"2017-02-07T12:32:00.000Z","key":1486470720000,"doc_count":12},{"key_as_string":"2017-02-07T12:33:00.000Z","key":1486470780000,"doc_count":11},{"key_as_string":"2017-02-07T12:34:00.000Z","key":1486470840000,"doc_count":18},{"key_as_string":"2017-02-07T12:35:00.000Z","key":1486470900000,"doc_count":16},{"key_as_string":"2017-02-07T12:36:00.000Z","key":1486470960000,"doc_count":17},{"key_as_string":"2017-02-07T12:37:00.000Z","key":1486471020000,"doc_count":18},{"key_as_string":"2017-02-07T12:38:00.000Z","key":1486471080000,"doc_count":16},{"key_as_string":"2017-02-07T12:39:00.000Z","key":1486471140000,"doc_count":15},{"key_as_string":"2017-02-07T12:40:00.000Z","key":1486471200000,"doc_count":16},{"key_as_string":"2017-02-07T12:41:00.000Z","key":1486471260000,"doc_count":22},{"key_as_string":"2017-02-07T12:42:00.000Z","key":1486471320000,"doc_count":14},{"key_as_string":"2017-02-07T12:43:00.000Z","key":1486471380000,"doc_count":13},{"key_as_string":"2017-02-07T12:44:00.000Z","key":1486471440000,"doc_count":10},{"key_as_string":"2017-02-07T12:45:00.000Z","key":1486471500000,"doc_count":13},{"key_as_string":"2017-02-07T12:46:00.000Z","key":1486471560000,"doc_count":19},{"key_as_string":"2017-02-07T12:47:00.000Z","key":1486471620000,"doc_count":12},{"key_as_string":"2017-02-07T12:48:00.000Z","key":1486471680000,"doc_count":12},{"key_as_string":"2017-02-07T12:49:00.000Z","key":1486471740000,"doc_count":14},{"key_as_string":"2017-02-07T12:50:00.000Z","key":1486471800000,"doc_count":17},{"key_as_string":"2017-02-07T12:51:00.000Z","key":1486471860000,"doc_count":14},{"key_as_string":"2017-02-07T12:52:00.000Z","key":1486471920000,"doc_count":9},{"key_as_string":"2017-02-07T12:53:00.000Z","key":1486471980000,"doc_count":21},{"key_as_string":"2017-02-07T12:54:00.000Z","key":1486472040000,"doc_count":14},{"key_as_string":"2017-02-07T12:55:00.000Z","key":1486472100000,"doc_count":11},{"key_as_string":"2017-02-07T12:56:00.000Z","key":1486472160000,"doc_count":13},{"key_as_string":"2017-02-07T12:57:00.000Z","key":1486472220000,"doc_count":13},{"key_as_string":"2017-02-07T12:58:00.000Z","key":1486472280000,"doc_count":12},{"key_as_string":"2017-02-07T12:59:00.000Z","key":1486472340000,"doc_count":19},{"key_as_string":"2017-02-07T13:00:00.000Z","key":1486472400000,"doc_count":10},{"key_as_string":"2017-02-07T13:01:00.000Z","key":1486472460000,"doc_count":13},{"key_as_string":"2017-02-07T13:02:00.000Z","key":1486472520000,"doc_count":12},{"key_as_string":"2017-02-07T13:03:00.000Z","key":1486472580000,"doc_count":9},{"key_as_string":"2017-02-07T13:04:00.000Z","key":1486472640000,"doc_count":17},{"key_as_string":"2017-02-07T13:05:00.000Z","key":1486472700000,"doc_count":14},{"key_as_string":"2017-02-07T13:06:00.000Z","key":1486472760000,"doc_count":13},{"key_as_string":"2017-02-07T13:07:00.000Z","key":1486472820000,"doc_count":18},{"key_as_string":"2017-02-07T13:08:00.000Z","key":1486472880000,"doc_count":16},{"key_as_string":"2017-02-07T13:09:00.000Z","key":1486472940000,"doc_count":11},{"key_as_string":"2017-02-07T13:10:00.000Z","key":1486473000000,"doc_count":9},{"key_as_string":"2017-02-07T13:11:00.000Z","key":1486473060000,"doc_count":14},{"key_as_string":"2017-02-07T13:12:00.000Z","key":1486473120000,"doc_count":11},{"key_as_string":"2017-02-07T13:13:00.000Z","key":1486473180000,"doc_count":17},{"key_as_string":"2017-02-07T13:14:00.000Z","key":1486473240000,"doc_count":14},{"key_as_string":"2017-02-07T13:15:00.000Z","key":1486473300000,"doc_count":17},{"key_as_string":"2017-02-07T13:16:00.000Z","key":1486473360000,"doc_count":14},{"key_as_string":"2017-02-07T13:17:00.000Z","key":1486473420000,"doc_count":18},{"key_as_string":"2017-02-07T13:18:00.000Z","key":1486473480000,"doc_count":13},{"key_as_string":"2017-02-07T13:19:00.000Z","key":1486473540000,"doc_count":24},{"key_as_string":"2017-02-07T13:20:00.000Z","key":1486473600000,"doc_count":6},{"key_as_string":"2017-02-07T13:21:00.000Z","key":1486473660000,"doc_count":16},{"key_as_string":"2017-02-07T13:22:00.000Z","key":1486473720000,"doc_count":19},{"key_as_string":"2017-02-07T13:23:00.000Z","key":1486473780000,"doc_count":12},{"key_as_string":"2017-02-07T13:24:00.000Z","key":1486473840000,"doc_count":18},{"key_as_string":"2017-02-07T13:25:00.000Z","key":1486473900000,"doc_count":11},{"key_as_string":"2017-02-07T13:26:00.000Z","key":1486473960000,"doc_count":14},{"key_as_string":"2017-02-07T13:27:00.000Z","key":1486474020000,"doc_count":13},{"key_as_string":"2017-02-07T13:28:00.000Z","key":1486474080000,"doc_count":13},{"key_as_string":"2017-02-07T13:29:00.000Z","key":1486474140000,"doc_count":12},{"key_as_string":"2017-02-07T13:30:00.000Z","key":1486474200000,"doc_count":12},{"key_as_string":"2017-02-07T13:31:00.000Z","key":1486474260000,"doc_count":10},{"key_as_string":"2017-02-07T13:32:00.000Z","key":1486474320000,"doc_count":15},{"key_as_string":"2017-02-07T13:33:00.000Z","key":1486474380000,"doc_count":11},{"key_as_string":"2017-02-07T13:34:00.000Z","key":1486474440000,"doc_count":15},{"key_as_string":"2017-02-07T13:35:00.000Z","key":1486474500000,"doc_count":6},{"key_as_string":"2017-02-07T13:36:00.000Z","key":1486474560000,"doc_count":20},{"key_as_string":"2017-02-07T13:37:00.000Z","key":1486474620000,"doc_count":9},{"key_as_string":"2017-02-07T13:38:00.000Z","key":1486474680000,"doc_count":15},{"key_as_string":"2017-02-07T13:39:00.000Z","key":1486474740000,"doc_count":18},{"key_as_string":"2017-02-07T13:40:00.000Z","key":1486474800000,"doc_count":14},{"key_as_string":"2017-02-07T13:41:00.000Z","key":1486474860000,"doc_count":11},{"key_as_string":"2017-02-07T13:42:00.000Z","key":1486474920000,"doc_count":15},{"key_as_string":"2017-02-07T13:43:00.000Z","key":1486474980000,"doc_count":10},{"key_as_string":"2017-02-07T13:44:00.000Z","key":1486475040000,"doc_count":14},{"key_as_string":"2017-02-07T13:45:00.000Z","key":1486475100000,"doc_count":13},{"key_as_string":"2017-02-07T13:46:00.000Z","key":1486475160000,"doc_count":16},{"key_as_string":"2017-02-07T13:47:00.000Z","key":1486475220000,"doc_count":10},{"key_as_string":"2017-02-07T13:48:00.000Z","key":1486475280000,"doc_count":13},{"key_as_string":"2017-02-07T13:49:00.000Z","key":1486475340000,"doc_count":14},{"key_as_string":"2017-02-07T13:50:00.000Z","key":1486475400000,"doc_count":12},{"key_as_string":"2017-02-07T13:51:00.000Z","key":1486475460000,"doc_count":12},{"key_as_string":"2017-02-07T13:52:00.000Z","key":1486475520000,"doc_count":11},{"key_as_string":"2017-02-07T13:53:00.000Z","key":1486475580000,"doc_count":15},{"key_as_string":"2017-02-07T13:54:00.000Z","key":1486475640000,"doc_count":13},{"key_as_string":"2017-02-07T13:55:00.000Z","key":1486475700000,"doc_count":14},{"key_as_string":"2017-02-07T13:56:00.000Z","key":1486475760000,"doc_count":18},{"key_as_string":"2017-02-07T13:57:00.000Z","key":1486475820000,"doc_count":6},{"key_as_string":"2017-02-07T13:58:00.000Z","key":1486475880000,"doc_count":19},{"key_as_string":"2017-02-07T13:59:00.000Z","key":1486475940000,"doc_count":13},{"key_as_string":"2017-02-07T14:00:00.000Z","key":1486476000000,"doc_count":9},{"key_as_string":"2017-02-07T14:01:00.000Z","key":1486476060000,"doc_count":10},{"key_as_string":"2017-02-07T14:02:00.000Z","key":1486476120000,"doc_count":14},{"key_as_string":"2017-02-07T14:03:00.000Z","key":1486476180000,"doc_count":13},{"key_as_string":"2017-02-07T14:04:00.000Z","key":1486476240000,"doc_count":19},{"key_as_string":"2017-02-07T14:05:00.000Z","key":1486476300000,"doc_count":15},{"key_as_string":"2017-02-07T14:06:00.000Z","key":1486476360000,"doc_count":12},{"key_as_string":"2017-02-07T14:07:00.000Z","key":1486476420000,"doc_count":11},{"key_as_string":"2017-02-07T14:08:00.000Z","key":1486476480000,"doc_count":15},{"key_as_string":"2017-02-07T14:09:00.000Z","key":1486476540000,"doc_count":10},{"key_as_string":"2017-02-07T14:10:00.000Z","key":1486476600000,"doc_count":13},{"key_as_string":"2017-02-07T14:11:00.000Z","key":1486476660000,"doc_count":18},{"key_as_string":"2017-02-07T14:12:00.000Z","key":1486476720000,"doc_count":10},{"key_as_string":"2017-02-07T14:13:00.000Z","key":1486476780000,"doc_count":13},{"key_as_string":"2017-02-07T14:14:00.000Z","key":1486476840000,"doc_count":15},{"key_as_string":"2017-02-07T14:15:00.000Z","key":1486476900000,"doc_count":18},{"key_as_string":"2017-02-07T14:16:00.000Z","key":1486476960000,"doc_count":14},{"key_as_string":"2017-02-07T14:17:00.000Z","key":1486477020000,"doc_count":13},{"key_as_string":"2017-02-07T14:18:00.000Z","key":1486477080000,"doc_count":12},{"key_as_string":"2017-02-07T14:19:00.000Z","key":1486477140000,"doc_count":18},{"key_as_string":"2017-02-07T14:20:00.000Z","key":1486477200000,"doc_count":13},{"key_as_string":"2017-02-07T14:21:00.000Z","key":1486477260000,"doc_count":14},{"key_as_string":"2017-02-07T14:22:00.000Z","key":1486477320000,"doc_count":8},{"key_as_string":"2017-02-07T14:23:00.000Z","key":1486477380000,"doc_count":16},{"key_as_string":"2017-02-07T14:24:00.000Z","key":1486477440000,"doc_count":12},{"key_as_string":"2017-02-07T14:25:00.000Z","key":1486477500000,"doc_count":17},{"key_as_string":"2017-02-07T14:26:00.000Z","key":1486477560000,"doc_count":11},{"key_as_string":"2017-02-07T14:27:00.000Z","key":1486477620000,"doc_count":17},{"key_as_string":"2017-02-07T14:28:00.000Z","key":1486477680000,"doc_count":17},{"key_as_string":"2017-02-07T14:29:00.000Z","key":1486477740000,"doc_count":14},{"key_as_string":"2017-02-07T14:30:00.000Z","key":1486477800000,"doc_count":15},{"key_as_string":"2017-02-07T14:31:00.000Z","key":1486477860000,"doc_count":13},{"key_as_string":"2017-02-07T14:32:00.000Z","key":1486477920000,"doc_count":9},{"key_as_string":"2017-02-07T14:33:00.000Z","key":1486477980000,"doc_count":17},{"key_as_string":"2017-02-07T14:34:00.000Z","key":1486478040000,"doc_count":12},{"key_as_string":"2017-02-07T14:35:00.000Z","key":1486478100000,"doc_count":14},{"key_as_string":"2017-02-07T14:36:00.000Z","key":1486478160000,"doc_count":14},{"key_as_string":"2017-02-07T14:37:00.000Z","key":1486478220000,"doc_count":13},{"key_as_string":"2017-02-07T14:38:00.000Z","key":1486478280000,"doc_count":13},{"key_as_string":"2017-02-07T14:39:00.000Z","key":1486478340000,"doc_count":11},{"key_as_string":"2017-02-07T14:40:00.000Z","key":1486478400000,"doc_count":20},{"key_as_string":"2017-02-07T14:41:00.000Z","key":1486478460000,"doc_count":12},{"key_as_string":"2017-02-07T14:42:00.000Z","key":1486478520000,"doc_count":14},{"key_as_string":"2017-02-07T14:43:00.000Z","key":1486478580000,"doc_count":14},{"key_as_string":"2017-02-07T14:44:00.000Z","key":1486478640000,"doc_count":13},{"key_as_string":"2017-02-07T14:45:00.000Z","key":1486478700000,"doc_count":16},{"key_as_string":"2017-02-07T14:46:00.000Z","key":1486478760000,"doc_count":16},{"key_as_string":"2017-02-07T14:47:00.000Z","key":1486478820000,"doc_count":12},{"key_as_string":"2017-02-07T14:48:00.000Z","key":1486478880000,"doc_count":11},{"key_as_string":"2017-02-07T14:49:00.000Z","key":1486478940000,"doc_count":16},{"key_as_string":"2017-02-07T14:50:00.000Z","key":1486479000000,"doc_count":12},{"key_as_string":"2017-02-07T14:51:00.000Z","key":1486479060000,"doc_count":9},{"key_as_string":"2017-02-07T14:52:00.000Z","key":1486479120000,"doc_count":13},{"key_as_string":"2017-02-07T14:53:00.000Z","key":1486479180000,"doc_count":12},{"key_as_string":"2017-02-07T14:54:00.000Z","key":1486479240000,"doc_count":19},{"key_as_string":"2017-02-07T14:55:00.000Z","key":1486479300000,"doc_count":10},{"key_as_string":"2017-02-07T14:56:00.000Z","key":1486479360000,"doc_count":15},{"key_as_string":"2017-02-07T14:57:00.000Z","key":1486479420000,"doc_count":16},{"key_as_string":"2017-02-07T14:58:00.000Z","key":1486479480000,"doc_count":14},{"key_as_string":"2017-02-07T14:59:00.000Z","key":1486479540000,"doc_count":12},{"key_as_string":"2017-02-07T15:00:00.000Z","key":1486479600000,"doc_count":14},{"key_as_string":"2017-02-07T15:01:00.000Z","key":1486479660000,"doc_count":12},{"key_as_string":"2017-02-07T15:02:00.000Z","key":1486479720000,"doc_count":11},{"key_as_string":"2017-02-07T15:03:00.000Z","key":1486479780000,"doc_count":13},{"key_as_string":"2017-02-07T15:04:00.000Z","key":1486479840000,"doc_count":14},{"key_as_string":"2017-02-07T15:05:00.000Z","key":1486479900000,"doc_count":10},{"key_as_string":"2017-02-07T15:06:00.000Z","key":1486479960000,"doc_count":9},{"key_as_string":"2017-02-07T15:07:00.000Z","key":1486480020000,"doc_count":10},{"key_as_string":"2017-02-07T15:08:00.000Z","key":1486480080000,"doc_count":15},{"key_as_string":"2017-02-07T15:09:00.000Z","key":1486480140000,"doc_count":13},{"key_as_string":"2017-02-07T15:10:00.000Z","key":1486480200000,"doc_count":12},{"key_as_string":"2017-02-07T15:11:00.000Z","key":1486480260000,"doc_count":13},{"key_as_string":"2017-02-07T15:12:00.000Z","key":1486480320000,"doc_count":12},{"key_as_string":"2017-02-07T15:13:00.000Z","key":1486480380000,"doc_count":13},{"key_as_string":"2017-02-07T15:14:00.000Z","key":1486480440000,"doc_count":15},{"key_as_string":"2017-02-07T15:15:00.000Z","key":1486480500000,"doc_count":15},{"key_as_string":"2017-02-07T15:16:00.000Z","key":1486480560000,"doc_count":14},{"key_as_string":"2017-02-07T15:17:00.000Z","key":1486480620000,"doc_count":7},{"key_as_string":"2017-02-07T15:18:00.000Z","key":1486480680000,"doc_count":16},{"key_as_string":"2017-02-07T15:19:00.000Z","key":1486480740000,"doc_count":13},{"key_as_string":"2017-02-07T15:20:00.000Z","key":1486480800000,"doc_count":14},{"key_as_string":"2017-02-07T15:21:00.000Z","key":1486480860000,"doc_count":13},{"key_as_string":"2017-02-07T15:22:00.000Z","key":1486480920000,"doc_count":12},{"key_as_string":"2017-02-07T15:23:00.000Z","key":1486480980000,"doc_count":16},{"key_as_string":"2017-02-07T15:24:00.000Z","key":1486481040000,"doc_count":15},{"key_as_string":"2017-02-07T15:25:00.000Z","key":1486481100000,"doc_count":8},{"key_as_string":"2017-02-07T15:26:00.000Z","key":1486481160000,"doc_count":15},{"key_as_string":"2017-02-07T15:27:00.000Z","key":1486481220000,"doc_count":11},{"key_as_string":"2017-02-07T15:28:00.000Z","key":1486481280000,"doc_count":14},{"key_as_string":"2017-02-07T15:29:00.000Z","key":1486481340000,"doc_count":14},{"key_as_string":"2017-02-07T15:30:00.000Z","key":1486481400000,"doc_count":12},{"key_as_string":"2017-02-07T15:31:00.000Z","key":1486481460000,"doc_count":19},{"key_as_string":"2017-02-07T15:32:00.000Z","key":1486481520000,"doc_count":15},{"key_as_string":"2017-02-07T15:33:00.000Z","key":1486481580000,"doc_count":15},{"key_as_string":"2017-02-07T15:34:00.000Z","key":1486481640000,"doc_count":11},{"key_as_string":"2017-02-07T15:35:00.000Z","key":1486481700000,"doc_count":14},{"key_as_string":"2017-02-07T15:36:00.000Z","key":1486481760000,"doc_count":13},{"key_as_string":"2017-02-07T15:37:00.000Z","key":1486481820000,"doc_count":18},{"key_as_string":"2017-02-07T15:38:00.000Z","key":1486481880000,"doc_count":8},{"key_as_string":"2017-02-07T15:39:00.000Z","key":1486481940000,"doc_count":14},{"key_as_string":"2017-02-07T15:40:00.000Z","key":1486482000000,"doc_count":11},{"key_as_string":"2017-02-07T15:41:00.000Z","key":1486482060000,"doc_count":10},{"key_as_string":"2017-02-07T15:42:00.000Z","key":1486482120000,"doc_count":10},{"key_as_string":"2017-02-07T15:43:00.000Z","key":1486482180000,"doc_count":14},{"key_as_string":"2017-02-07T15:44:00.000Z","key":1486482240000,"doc_count":10},{"key_as_string":"2017-02-07T15:45:00.000Z","key":1486482300000,"doc_count":13},{"key_as_string":"2017-02-07T15:46:00.000Z","key":1486482360000,"doc_count":15},{"key_as_string":"2017-02-07T15:47:00.000Z","key":1486482420000,"doc_count":11},{"key_as_string":"2017-02-07T15:48:00.000Z","key":1486482480000,"doc_count":13},{"key_as_string":"2017-02-07T15:49:00.000Z","key":1486482540000,"doc_count":10},{"key_as_string":"2017-02-07T15:50:00.000Z","key":1486482600000,"doc_count":17},{"key_as_string":"2017-02-07T15:51:00.000Z","key":1486482660000,"doc_count":11},{"key_as_string":"2017-02-07T15:52:00.000Z","key":1486482720000,"doc_count":15},{"key_as_string":"2017-02-07T15:53:00.000Z","key":1486482780000,"doc_count":10},{"key_as_string":"2017-02-07T15:54:00.000Z","key":1486482840000,"doc_count":11},{"key_as_string":"2017-02-07T15:55:00.000Z","key":1486482900000,"doc_count":14},{"key_as_string":"2017-02-07T15:56:00.000Z","key":1486482960000,"doc_count":16},{"key_as_string":"2017-02-07T15:57:00.000Z","key":1486483020000,"doc_count":15},{"key_as_string":"2017-02-07T15:58:00.000Z","key":1486483080000,"doc_count":13},{"key_as_string":"2017-02-07T15:59:00.000Z","key":1486483140000,"doc_count":10},{"key_as_string":"2017-02-07T16:00:00.000Z","key":1486483200000,"doc_count":18},{"key_as_string":"2017-02-07T16:01:00.000Z","key":1486483260000,"doc_count":11},{"key_as_string":"2017-02-07T16:02:00.000Z","key":1486483320000,"doc_count":14},{"key_as_string":"2017-02-07T16:03:00.000Z","key":1486483380000,"doc_count":16},{"key_as_string":"2017-02-07T16:04:00.000Z","key":1486483440000,"doc_count":10},{"key_as_string":"2017-02-07T16:05:00.000Z","key":1486483500000,"doc_count":12},{"key_as_string":"2017-02-07T16:06:00.000Z","key":1486483560000,"doc_count":11},{"key_as_string":"2017-02-07T16:07:00.000Z","key":1486483620000,"doc_count":14},{"key_as_string":"2017-02-07T16:08:00.000Z","key":1486483680000,"doc_count":12},{"key_as_string":"2017-02-07T16:09:00.000Z","key":1486483740000,"doc_count":8},{"key_as_string":"2017-02-07T16:10:00.000Z","key":1486483800000,"doc_count":13},{"key_as_string":"2017-02-07T16:11:00.000Z","key":1486483860000,"doc_count":15},{"key_as_string":"2017-02-07T16:12:00.000Z","key":1486483920000,"doc_count":9},{"key_as_string":"2017-02-07T16:13:00.000Z","key":1486483980000,"doc_count":14},{"key_as_string":"2017-02-07T16:14:00.000Z","key":1486484040000,"doc_count":11},{"key_as_string":"2017-02-07T16:15:00.000Z","key":1486484100000,"doc_count":10},{"key_as_string":"2017-02-07T16:16:00.000Z","key":1486484160000,"doc_count":14},{"key_as_string":"2017-02-07T16:17:00.000Z","key":1486484220000,"doc_count":10},{"key_as_string":"2017-02-07T16:18:00.000Z","key":1486484280000,"doc_count":10},{"key_as_string":"2017-02-07T16:19:00.000Z","key":1486484340000,"doc_count":17},{"key_as_string":"2017-02-07T16:20:00.000Z","key":1486484400000,"doc_count":11},{"key_as_string":"2017-02-07T16:21:00.000Z","key":1486484460000,"doc_count":14},{"key_as_string":"2017-02-07T16:22:00.000Z","key":1486484520000,"doc_count":15},{"key_as_string":"2017-02-07T16:23:00.000Z","key":1486484580000,"doc_count":8},{"key_as_string":"2017-02-07T16:24:00.000Z","key":1486484640000,"doc_count":14},{"key_as_string":"2017-02-07T16:25:00.000Z","key":1486484700000,"doc_count":10},{"key_as_string":"2017-02-07T16:26:00.000Z","key":1486484760000,"doc_count":10},{"key_as_string":"2017-02-07T16:27:00.000Z","key":1486484820000,"doc_count":14},{"key_as_string":"2017-02-07T16:28:00.000Z","key":1486484880000,"doc_count":19},{"key_as_string":"2017-02-07T16:29:00.000Z","key":1486484940000,"doc_count":9},{"key_as_string":"2017-02-07T16:30:00.000Z","key":1486485000000,"doc_count":15},{"key_as_string":"2017-02-07T16:31:00.000Z","key":1486485060000,"doc_count":13},{"key_as_string":"2017-02-07T16:32:00.000Z","key":1486485120000,"doc_count":10},{"key_as_string":"2017-02-07T16:33:00.000Z","key":1486485180000,"doc_count":12},{"key_as_string":"2017-02-07T16:34:00.000Z","key":1486485240000,"doc_count":12},{"key_as_string":"2017-02-07T16:35:00.000Z","key":1486485300000,"doc_count":17},{"key_as_string":"2017-02-07T16:36:00.000Z","key":1486485360000,"doc_count":8},{"key_as_string":"2017-02-07T16:37:00.000Z","key":1486485420000,"doc_count":17},{"key_as_string":"2017-02-07T16:38:00.000Z","key":1486485480000,"doc_count":13},{"key_as_string":"2017-02-07T16:39:00.000Z","key":1486485540000,"doc_count":7},{"key_as_string":"2017-02-07T16:40:00.000Z","key":1486485600000,"doc_count":11},{"key_as_string":"2017-02-07T16:41:00.000Z","key":1486485660000,"doc_count":14},{"key_as_string":"2017-02-07T16:42:00.000Z","key":1486485720000,"doc_count":16},{"key_as_string":"2017-02-07T16:43:00.000Z","key":1486485780000,"doc_count":11},{"key_as_string":"2017-02-07T16:44:00.000Z","key":1486485840000,"doc_count":15},{"key_as_string":"2017-02-07T16:45:00.000Z","key":1486485900000,"doc_count":15},{"key_as_string":"2017-02-07T16:46:00.000Z","key":1486485960000,"doc_count":11},{"key_as_string":"2017-02-07T16:47:00.000Z","key":1486486020000,"doc_count":11},{"key_as_string":"2017-02-07T16:48:00.000Z","key":1486486080000,"doc_count":12},{"key_as_string":"2017-02-07T16:49:00.000Z","key":1486486140000,"doc_count":12},{"key_as_string":"2017-02-07T16:50:00.000Z","key":1486486200000,"doc_count":11},{"key_as_string":"2017-02-07T16:51:00.000Z","key":1486486260000,"doc_count":12},{"key_as_string":"2017-02-07T16:52:00.000Z","key":1486486320000,"doc_count":12},{"key_as_string":"2017-02-07T16:53:00.000Z","key":1486486380000,"doc_count":14},{"key_as_string":"2017-02-07T16:54:00.000Z","key":1486486440000,"doc_count":10},{"key_as_string":"2017-02-07T16:55:00.000Z","key":1486486500000,"doc_count":12},{"key_as_string":"2017-02-07T16:56:00.000Z","key":1486486560000,"doc_count":15},{"key_as_string":"2017-02-07T16:57:00.000Z","key":1486486620000,"doc_count":14},{"key_as_string":"2017-02-07T16:58:00.000Z","key":1486486680000,"doc_count":9},{"key_as_string":"2017-02-07T16:59:00.000Z","key":1486486740000,"doc_count":15},{"key_as_string":"2017-02-07T17:00:00.000Z","key":1486486800000,"doc_count":10},{"key_as_string":"2017-02-07T17:01:00.000Z","key":1486486860000,"doc_count":9},{"key_as_string":"2017-02-07T17:02:00.000Z","key":1486486920000,"doc_count":15},{"key_as_string":"2017-02-07T17:03:00.000Z","key":1486486980000,"doc_count":11},{"key_as_string":"2017-02-07T17:04:00.000Z","key":1486487040000,"doc_count":11},{"key_as_string":"2017-02-07T17:05:00.000Z","key":1486487100000,"doc_count":6},{"key_as_string":"2017-02-07T17:06:00.000Z","key":1486487160000,"doc_count":20},{"key_as_string":"2017-02-07T17:07:00.000Z","key":1486487220000,"doc_count":12},{"key_as_string":"2017-02-07T17:08:00.000Z","key":1486487280000,"doc_count":10},{"key_as_string":"2017-02-07T17:09:00.000Z","key":1486487340000,"doc_count":15},{"key_as_string":"2017-02-07T17:10:00.000Z","key":1486487400000,"doc_count":15},{"key_as_string":"2017-02-07T17:11:00.000Z","key":1486487460000,"doc_count":12},{"key_as_string":"2017-02-07T17:12:00.000Z","key":1486487520000,"doc_count":10},{"key_as_string":"2017-02-07T17:13:00.000Z","key":1486487580000,"doc_count":14},{"key_as_string":"2017-02-07T17:14:00.000Z","key":1486487640000,"doc_count":11},{"key_as_string":"2017-02-07T17:15:00.000Z","key":1486487700000,"doc_count":15},{"key_as_string":"2017-02-07T17:16:00.000Z","key":1486487760000,"doc_count":9},{"key_as_string":"2017-02-07T17:17:00.000Z","key":1486487820000,"doc_count":15},{"key_as_string":"2017-02-07T17:18:00.000Z","key":1486487880000,"doc_count":10},{"key_as_string":"2017-02-07T17:19:00.000Z","key":1486487940000,"doc_count":15},{"key_as_string":"2017-02-07T17:20:00.000Z","key":1486488000000,"doc_count":8},{"key_as_string":"2017-02-07T17:21:00.000Z","key":1486488060000,"doc_count":9},{"key_as_string":"2017-02-07T17:22:00.000Z","key":1486488120000,"doc_count":13},{"key_as_string":"2017-02-07T17:23:00.000Z","key":1486488180000,"doc_count":12},{"key_as_string":"2017-02-07T17:24:00.000Z","key":1486488240000,"doc_count":12},{"key_as_string":"2017-02-07T17:25:00.000Z","key":1486488300000,"doc_count":13},{"key_as_string":"2017-02-07T17:26:00.000Z","key":1486488360000,"doc_count":12},{"key_as_string":"2017-02-07T17:27:00.000Z","key":1486488420000,"doc_count":13},{"key_as_string":"2017-02-07T17:28:00.000Z","key":1486488480000,"doc_count":13},{"key_as_string":"2017-02-07T17:29:00.000Z","key":1486488540000,"doc_count":13},{"key_as_string":"2017-02-07T17:30:00.000Z","key":1486488600000,"doc_count":15},{"key_as_string":"2017-02-07T17:31:00.000Z","key":1486488660000,"doc_count":11},{"key_as_string":"2017-02-07T17:32:00.000Z","key":1486488720000,"doc_count":10},{"key_as_string":"2017-02-07T17:33:00.000Z","key":1486488780000,"doc_count":10},{"key_as_string":"2017-02-07T17:34:00.000Z","key":1486488840000,"doc_count":10},{"key_as_string":"2017-02-07T17:35:00.000Z","key":1486488900000,"doc_count":12},{"key_as_string":"2017-02-07T17:36:00.000Z","key":1486488960000,"doc_count":15},{"key_as_string":"2017-02-07T17:37:00.000Z","key":1486489020000,"doc_count":8},{"key_as_string":"2017-02-07T17:38:00.000Z","key":1486489080000,"doc_count":15},{"key_as_string":"2017-02-07T17:39:00.000Z","key":1486489140000,"doc_count":11},{"key_as_string":"2017-02-07T17:40:00.000Z","key":1486489200000,"doc_count":8},{"key_as_string":"2017-02-07T17:41:00.000Z","key":1486489260000,"doc_count":17},{"key_as_string":"2017-02-07T17:42:00.000Z","key":1486489320000,"doc_count":16},{"key_as_string":"2017-02-07T17:43:00.000Z","key":1486489380000,"doc_count":12},{"key_as_string":"2017-02-07T17:44:00.000Z","key":1486489440000,"doc_count":8},{"key_as_string":"2017-02-07T17:45:00.000Z","key":1486489500000,"doc_count":12},{"key_as_string":"2017-02-07T17:46:00.000Z","key":1486489560000,"doc_count":13},{"key_as_string":"2017-02-07T17:47:00.000Z","key":1486489620000,"doc_count":8},{"key_as_string":"2017-02-07T17:48:00.000Z","key":1486489680000,"doc_count":7},{"key_as_string":"2017-02-07T17:49:00.000Z","key":1486489740000,"doc_count":16},{"key_as_string":"2017-02-07T17:50:00.000Z","key":1486489800000,"doc_count":13},{"key_as_string":"2017-02-07T17:51:00.000Z","key":1486489860000,"doc_count":11},{"key_as_string":"2017-02-07T17:52:00.000Z","key":1486489920000,"doc_count":14},{"key_as_string":"2017-02-07T17:53:00.000Z","key":1486489980000,"doc_count":8},{"key_as_string":"2017-02-07T17:54:00.000Z","key":1486490040000,"doc_count":10},{"key_as_string":"2017-02-07T17:55:00.000Z","key":1486490100000,"doc_count":12},{"key_as_string":"2017-02-07T17:56:00.000Z","key":1486490160000,"doc_count":11},{"key_as_string":"2017-02-07T17:57:00.000Z","key":1486490220000,"doc_count":9},{"key_as_string":"2017-02-07T17:58:00.000Z","key":1486490280000,"doc_count":10},{"key_as_string":"2017-02-07T17:59:00.000Z","key":1486490340000,"doc_count":11},{"key_as_string":"2017-02-07T18:00:00.000Z","key":1486490400000,"doc_count":8},{"key_as_string":"2017-02-07T18:01:00.000Z","key":1486490460000,"doc_count":13},{"key_as_string":"2017-02-07T18:02:00.000Z","key":1486490520000,"doc_count":19},{"key_as_string":"2017-02-07T18:03:00.000Z","key":1486490580000,"doc_count":7},{"key_as_string":"2017-02-07T18:04:00.000Z","key":1486490640000,"doc_count":11},{"key_as_string":"2017-02-07T18:05:00.000Z","key":1486490700000,"doc_count":11},{"key_as_string":"2017-02-07T18:06:00.000Z","key":1486490760000,"doc_count":9},{"key_as_string":"2017-02-07T18:07:00.000Z","key":1486490820000,"doc_count":16},{"key_as_string":"2017-02-07T18:08:00.000Z","key":1486490880000,"doc_count":13},{"key_as_string":"2017-02-07T18:09:00.000Z","key":1486490940000,"doc_count":9},{"key_as_string":"2017-02-07T18:10:00.000Z","key":1486491000000,"doc_count":14},{"key_as_string":"2017-02-07T18:11:00.000Z","key":1486491060000,"doc_count":9},{"key_as_string":"2017-02-07T18:12:00.000Z","key":1486491120000,"doc_count":12},{"key_as_string":"2017-02-07T18:13:00.000Z","key":1486491180000,"doc_count":12},{"key_as_string":"2017-02-07T18:14:00.000Z","key":1486491240000,"doc_count":10},{"key_as_string":"2017-02-07T18:15:00.000Z","key":1486491300000,"doc_count":13},{"key_as_string":"2017-02-07T18:16:00.000Z","key":1486491360000,"doc_count":11},{"key_as_string":"2017-02-07T18:17:00.000Z","key":1486491420000,"doc_count":13},{"key_as_string":"2017-02-07T18:18:00.000Z","key":1486491480000,"doc_count":10},{"key_as_string":"2017-02-07T18:19:00.000Z","key":1486491540000,"doc_count":10},{"key_as_string":"2017-02-07T18:20:00.000Z","key":1486491600000,"doc_count":8},{"key_as_string":"2017-02-07T18:21:00.000Z","key":1486491660000,"doc_count":15},{"key_as_string":"2017-02-07T18:22:00.000Z","key":1486491720000,"doc_count":16},{"key_as_string":"2017-02-07T18:23:00.000Z","key":1486491780000,"doc_count":14},{"key_as_string":"2017-02-07T18:24:00.000Z","key":1486491840000,"doc_count":17},{"key_as_string":"2017-02-07T18:25:00.000Z","key":1486491900000,"doc_count":11},{"key_as_string":"2017-02-07T18:26:00.000Z","key":1486491960000,"doc_count":13},{"key_as_string":"2017-02-07T18:27:00.000Z","key":1486492020000,"doc_count":15},{"key_as_string":"2017-02-07T18:28:00.000Z","key":1486492080000,"doc_count":13},{"key_as_string":"2017-02-07T18:29:00.000Z","key":1486492140000,"doc_count":13},{"key_as_string":"2017-02-07T18:30:00.000Z","key":1486492200000,"doc_count":12},{"key_as_string":"2017-02-07T18:31:00.000Z","key":1486492260000,"doc_count":14},{"key_as_string":"2017-02-07T18:32:00.000Z","key":1486492320000,"doc_count":13},{"key_as_string":"2017-02-07T18:33:00.000Z","key":1486492380000,"doc_count":6},{"key_as_string":"2017-02-07T18:34:00.000Z","key":1486492440000,"doc_count":14},{"key_as_string":"2017-02-07T18:35:00.000Z","key":1486492500000,"doc_count":14},{"key_as_string":"2017-02-07T18:36:00.000Z","key":1486492560000,"doc_count":9},{"key_as_string":"2017-02-07T18:37:00.000Z","key":1486492620000,"doc_count":12},{"key_as_string":"2017-02-07T18:38:00.000Z","key":1486492680000,"doc_count":8},{"key_as_string":"2017-02-07T18:39:00.000Z","key":1486492740000,"doc_count":13},{"key_as_string":"2017-02-07T18:40:00.000Z","key":1486492800000,"doc_count":6},{"key_as_string":"2017-02-07T18:41:00.000Z","key":1486492860000,"doc_count":16},{"key_as_string":"2017-02-07T18:42:00.000Z","key":1486492920000,"doc_count":12},{"key_as_string":"2017-02-07T18:43:00.000Z","key":1486492980000,"doc_count":9},{"key_as_string":"2017-02-07T18:44:00.000Z","key":1486493040000,"doc_count":14},{"key_as_string":"2017-02-07T18:45:00.000Z","key":1486493100000,"doc_count":14},{"key_as_string":"2017-02-07T18:46:00.000Z","key":1486493160000,"doc_count":8},{"key_as_string":"2017-02-07T18:47:00.000Z","key":1486493220000,"doc_count":12},{"key_as_string":"2017-02-07T18:48:00.000Z","key":1486493280000,"doc_count":11},{"key_as_string":"2017-02-07T18:49:00.000Z","key":1486493340000,"doc_count":14},{"key_as_string":"2017-02-07T18:50:00.000Z","key":1486493400000,"doc_count":10},{"key_as_string":"2017-02-07T18:51:00.000Z","key":1486493460000,"doc_count":11},{"key_as_string":"2017-02-07T18:52:00.000Z","key":1486493520000,"doc_count":17},{"key_as_string":"2017-02-07T18:53:00.000Z","key":1486493580000,"doc_count":11},{"key_as_string":"2017-02-07T18:54:00.000Z","key":1486493640000,"doc_count":12},{"key_as_string":"2017-02-07T18:55:00.000Z","key":1486493700000,"doc_count":10},{"key_as_string":"2017-02-07T18:56:00.000Z","key":1486493760000,"doc_count":10},{"key_as_string":"2017-02-07T18:57:00.000Z","key":1486493820000,"doc_count":11},{"key_as_string":"2017-02-07T18:58:00.000Z","key":1486493880000,"doc_count":11},{"key_as_string":"2017-02-07T18:59:00.000Z","key":1486493940000,"doc_count":13},{"key_as_string":"2017-02-07T19:00:00.000Z","key":1486494000000,"doc_count":9},{"key_as_string":"2017-02-07T19:01:00.000Z","key":1486494060000,"doc_count":15},{"key_as_string":"2017-02-07T19:02:00.000Z","key":1486494120000,"doc_count":10},{"key_as_string":"2017-02-07T19:03:00.000Z","key":1486494180000,"doc_count":11},{"key_as_string":"2017-02-07T19:04:00.000Z","key":1486494240000,"doc_count":10},{"key_as_string":"2017-02-07T19:05:00.000Z","key":1486494300000,"doc_count":10},{"key_as_string":"2017-02-07T19:06:00.000Z","key":1486494360000,"doc_count":15},{"key_as_string":"2017-02-07T19:07:00.000Z","key":1486494420000,"doc_count":14},{"key_as_string":"2017-02-07T19:08:00.000Z","key":1486494480000,"doc_count":7},{"key_as_string":"2017-02-07T19:09:00.000Z","key":1486494540000,"doc_count":14},{"key_as_string":"2017-02-07T19:10:00.000Z","key":1486494600000,"doc_count":8},{"key_as_string":"2017-02-07T19:11:00.000Z","key":1486494660000,"doc_count":11},{"key_as_string":"2017-02-07T19:12:00.000Z","key":1486494720000,"doc_count":10},{"key_as_string":"2017-02-07T19:13:00.000Z","key":1486494780000,"doc_count":11},{"key_as_string":"2017-02-07T19:14:00.000Z","key":1486494840000,"doc_count":13},{"key_as_string":"2017-02-07T19:15:00.000Z","key":1486494900000,"doc_count":16},{"key_as_string":"2017-02-07T19:16:00.000Z","key":1486494960000,"doc_count":5},{"key_as_string":"2017-02-07T19:17:00.000Z","key":1486495020000,"doc_count":14},{"key_as_string":"2017-02-07T19:18:00.000Z","key":1486495080000,"doc_count":13},{"key_as_string":"2017-02-07T19:19:00.000Z","key":1486495140000,"doc_count":10},{"key_as_string":"2017-02-07T19:20:00.000Z","key":1486495200000,"doc_count":13},{"key_as_string":"2017-02-07T19:21:00.000Z","key":1486495260000,"doc_count":10},{"key_as_string":"2017-02-07T19:22:00.000Z","key":1486495320000,"doc_count":12},{"key_as_string":"2017-02-07T19:23:00.000Z","key":1486495380000,"doc_count":13},{"key_as_string":"2017-02-07T19:24:00.000Z","key":1486495440000,"doc_count":10},{"key_as_string":"2017-02-07T19:25:00.000Z","key":1486495500000,"doc_count":10},{"key_as_string":"2017-02-07T19:26:00.000Z","key":1486495560000,"doc_count":11},{"key_as_string":"2017-02-07T19:27:00.000Z","key":1486495620000,"doc_count":9},{"key_as_string":"2017-02-07T19:28:00.000Z","key":1486495680000,"doc_count":10},{"key_as_string":"2017-02-07T19:29:00.000Z","key":1486495740000,"doc_count":14},{"key_as_string":"2017-02-07T19:30:00.000Z","key":1486495800000,"doc_count":14},{"key_as_string":"2017-02-07T19:31:00.000Z","key":1486495860000,"doc_count":12},{"key_as_string":"2017-02-07T19:32:00.000Z","key":1486495920000,"doc_count":6},{"key_as_string":"2017-02-07T19:33:00.000Z","key":1486495980000,"doc_count":11},{"key_as_string":"2017-02-07T19:34:00.000Z","key":1486496040000,"doc_count":12},{"key_as_string":"2017-02-07T19:35:00.000Z","key":1486496100000,"doc_count":9},{"key_as_string":"2017-02-07T19:36:00.000Z","key":1486496160000,"doc_count":11},{"key_as_string":"2017-02-07T19:37:00.000Z","key":1486496220000,"doc_count":14},{"key_as_string":"2017-02-07T19:38:00.000Z","key":1486496280000,"doc_count":9},{"key_as_string":"2017-02-07T19:39:00.000Z","key":1486496340000,"doc_count":10},{"key_as_string":"2017-02-07T19:40:00.000Z","key":1486496400000,"doc_count":10},{"key_as_string":"2017-02-07T19:41:00.000Z","key":1486496460000,"doc_count":9},{"key_as_string":"2017-02-07T19:42:00.000Z","key":1486496520000,"doc_count":12},{"key_as_string":"2017-02-07T19:43:00.000Z","key":1486496580000,"doc_count":10},{"key_as_string":"2017-02-07T19:44:00.000Z","key":1486496640000,"doc_count":10},{"key_as_string":"2017-02-07T19:45:00.000Z","key":1486496700000,"doc_count":14},{"key_as_string":"2017-02-07T19:46:00.000Z","key":1486496760000,"doc_count":7},{"key_as_string":"2017-02-07T19:47:00.000Z","key":1486496820000,"doc_count":14},{"key_as_string":"2017-02-07T19:48:00.000Z","key":1486496880000,"doc_count":12},{"key_as_string":"2017-02-07T19:49:00.000Z","key":1486496940000,"doc_count":8},{"key_as_string":"2017-02-07T19:50:00.000Z","key":1486497000000,"doc_count":14},{"key_as_string":"2017-02-07T19:51:00.000Z","key":1486497060000,"doc_count":7},{"key_as_string":"2017-02-07T19:52:00.000Z","key":1486497120000,"doc_count":14},{"key_as_string":"2017-02-07T19:53:00.000Z","key":1486497180000,"doc_count":10},{"key_as_string":"2017-02-07T19:54:00.000Z","key":1486497240000,"doc_count":9},{"key_as_string":"2017-02-07T19:55:00.000Z","key":1486497300000,"doc_count":13},{"key_as_string":"2017-02-07T19:56:00.000Z","key":1486497360000,"doc_count":6},{"key_as_string":"2017-02-07T19:57:00.000Z","key":1486497420000,"doc_count":7},{"key_as_string":"2017-02-07T19:58:00.000Z","key":1486497480000,"doc_count":13},{"key_as_string":"2017-02-07T19:59:00.000Z","key":1486497540000,"doc_count":14},{"key_as_string":"2017-02-07T20:00:00.000Z","key":1486497600000,"doc_count":9},{"key_as_string":"2017-02-07T20:01:00.000Z","key":1486497660000,"doc_count":12},{"key_as_string":"2017-02-07T20:02:00.000Z","key":1486497720000,"doc_count":9},{"key_as_string":"2017-02-07T20:03:00.000Z","key":1486497780000,"doc_count":11},{"key_as_string":"2017-02-07T20:04:00.000Z","key":1486497840000,"doc_count":12},{"key_as_string":"2017-02-07T20:05:00.000Z","key":1486497900000,"doc_count":15},{"key_as_string":"2017-02-07T20:06:00.000Z","key":1486497960000,"doc_count":8},{"key_as_string":"2017-02-07T20:07:00.000Z","key":1486498020000,"doc_count":3},{"key_as_string":"2017-02-07T20:08:00.000Z","key":1486498080000,"doc_count":12},{"key_as_string":"2017-02-07T20:09:00.000Z","key":1486498140000,"doc_count":12},{"key_as_string":"2017-02-07T20:10:00.000Z","key":1486498200000,"doc_count":10},{"key_as_string":"2017-02-07T20:11:00.000Z","key":1486498260000,"doc_count":16},{"key_as_string":"2017-02-07T20:12:00.000Z","key":1486498320000,"doc_count":12},{"key_as_string":"2017-02-07T20:13:00.000Z","key":1486498380000,"doc_count":6},{"key_as_string":"2017-02-07T20:14:00.000Z","key":1486498440000,"doc_count":12},{"key_as_string":"2017-02-07T20:15:00.000Z","key":1486498500000,"doc_count":7},{"key_as_string":"2017-02-07T20:16:00.000Z","key":1486498560000,"doc_count":12},{"key_as_string":"2017-02-07T20:17:00.000Z","key":1486498620000,"doc_count":9},{"key_as_string":"2017-02-07T20:18:00.000Z","key":1486498680000,"doc_count":13},{"key_as_string":"2017-02-07T20:19:00.000Z","key":1486498740000,"doc_count":11},{"key_as_string":"2017-02-07T20:20:00.000Z","key":1486498800000,"doc_count":8},{"key_as_string":"2017-02-07T20:21:00.000Z","key":1486498860000,"doc_count":11},{"key_as_string":"2017-02-07T20:22:00.000Z","key":1486498920000,"doc_count":7},{"key_as_string":"2017-02-07T20:23:00.000Z","key":1486498980000,"doc_count":11},{"key_as_string":"2017-02-07T20:24:00.000Z","key":1486499040000,"doc_count":11},{"key_as_string":"2017-02-07T20:25:00.000Z","key":1486499100000,"doc_count":7},{"key_as_string":"2017-02-07T20:26:00.000Z","key":1486499160000,"doc_count":11},{"key_as_string":"2017-02-07T20:27:00.000Z","key":1486499220000,"doc_count":12},{"key_as_string":"2017-02-07T20:28:00.000Z","key":1486499280000,"doc_count":13},{"key_as_string":"2017-02-07T20:29:00.000Z","key":1486499340000,"doc_count":11},{"key_as_string":"2017-02-07T20:30:00.000Z","key":1486499400000,"doc_count":6},{"key_as_string":"2017-02-07T20:31:00.000Z","key":1486499460000,"doc_count":12},{"key_as_string":"2017-02-07T20:32:00.000Z","key":1486499520000,"doc_count":9},{"key_as_string":"2017-02-07T20:33:00.000Z","key":1486499580000,"doc_count":13},{"key_as_string":"2017-02-07T20:34:00.000Z","key":1486499640000,"doc_count":11},{"key_as_string":"2017-02-07T20:35:00.000Z","key":1486499700000,"doc_count":16},{"key_as_string":"2017-02-07T20:36:00.000Z","key":1486499760000,"doc_count":9},{"key_as_string":"2017-02-07T20:37:00.000Z","key":1486499820000,"doc_count":7},{"key_as_string":"2017-02-07T20:38:00.000Z","key":1486499880000,"doc_count":14},{"key_as_string":"2017-02-07T20:39:00.000Z","key":1486499940000,"doc_count":10},{"key_as_string":"2017-02-07T20:40:00.000Z","key":1486500000000,"doc_count":9},{"key_as_string":"2017-02-07T20:41:00.000Z","key":1486500060000,"doc_count":11},{"key_as_string":"2017-02-07T20:42:00.000Z","key":1486500120000,"doc_count":15},{"key_as_string":"2017-02-07T20:43:00.000Z","key":1486500180000,"doc_count":8},{"key_as_string":"2017-02-07T20:44:00.000Z","key":1486500240000,"doc_count":16},{"key_as_string":"2017-02-07T20:45:00.000Z","key":1486500300000,"doc_count":8},{"key_as_string":"2017-02-07T20:46:00.000Z","key":1486500360000,"doc_count":12},{"key_as_string":"2017-02-07T20:47:00.000Z","key":1486500420000,"doc_count":11},{"key_as_string":"2017-02-07T20:48:00.000Z","key":1486500480000,"doc_count":6},{"key_as_string":"2017-02-07T20:49:00.000Z","key":1486500540000,"doc_count":10},{"key_as_string":"2017-02-07T20:50:00.000Z","key":1486500600000,"doc_count":8},{"key_as_string":"2017-02-07T20:51:00.000Z","key":1486500660000,"doc_count":15},{"key_as_string":"2017-02-07T20:52:00.000Z","key":1486500720000,"doc_count":9},{"key_as_string":"2017-02-07T20:53:00.000Z","key":1486500780000,"doc_count":11},{"key_as_string":"2017-02-07T20:54:00.000Z","key":1486500840000,"doc_count":13},{"key_as_string":"2017-02-07T20:55:00.000Z","key":1486500900000,"doc_count":9},{"key_as_string":"2017-02-07T20:56:00.000Z","key":1486500960000,"doc_count":5},{"key_as_string":"2017-02-07T20:57:00.000Z","key":1486501020000,"doc_count":10},{"key_as_string":"2017-02-07T20:58:00.000Z","key":1486501080000,"doc_count":13},{"key_as_string":"2017-02-07T20:59:00.000Z","key":1486501140000,"doc_count":14},{"key_as_string":"2017-02-07T21:00:00.000Z","key":1486501200000,"doc_count":13},{"key_as_string":"2017-02-07T21:01:00.000Z","key":1486501260000,"doc_count":8},{"key_as_string":"2017-02-07T21:02:00.000Z","key":1486501320000,"doc_count":11},{"key_as_string":"2017-02-07T21:03:00.000Z","key":1486501380000,"doc_count":9},{"key_as_string":"2017-02-07T21:04:00.000Z","key":1486501440000,"doc_count":16},{"key_as_string":"2017-02-07T21:05:00.000Z","key":1486501500000,"doc_count":10},{"key_as_string":"2017-02-07T21:06:00.000Z","key":1486501560000,"doc_count":8},{"key_as_string":"2017-02-07T21:07:00.000Z","key":1486501620000,"doc_count":16},{"key_as_string":"2017-02-07T21:08:00.000Z","key":1486501680000,"doc_count":13},{"key_as_string":"2017-02-07T21:09:00.000Z","key":1486501740000,"doc_count":10},{"key_as_string":"2017-02-07T21:10:00.000Z","key":1486501800000,"doc_count":11},{"key_as_string":"2017-02-07T21:11:00.000Z","key":1486501860000,"doc_count":8},{"key_as_string":"2017-02-07T21:12:00.000Z","key":1486501920000,"doc_count":12},{"key_as_string":"2017-02-07T21:13:00.000Z","key":1486501980000,"doc_count":17},{"key_as_string":"2017-02-07T21:14:00.000Z","key":1486502040000,"doc_count":6},{"key_as_string":"2017-02-07T21:15:00.000Z","key":1486502100000,"doc_count":12},{"key_as_string":"2017-02-07T21:16:00.000Z","key":1486502160000,"doc_count":9},{"key_as_string":"2017-02-07T21:17:00.000Z","key":1486502220000,"doc_count":8},{"key_as_string":"2017-02-07T21:18:00.000Z","key":1486502280000,"doc_count":8},{"key_as_string":"2017-02-07T21:19:00.000Z","key":1486502340000,"doc_count":11},{"key_as_string":"2017-02-07T21:20:00.000Z","key":1486502400000,"doc_count":11},{"key_as_string":"2017-02-07T21:21:00.000Z","key":1486502460000,"doc_count":10},{"key_as_string":"2017-02-07T21:22:00.000Z","key":1486502520000,"doc_count":10},{"key_as_string":"2017-02-07T21:23:00.000Z","key":1486502580000,"doc_count":13},{"key_as_string":"2017-02-07T21:24:00.000Z","key":1486502640000,"doc_count":10},{"key_as_string":"2017-02-07T21:25:00.000Z","key":1486502700000,"doc_count":11},{"key_as_string":"2017-02-07T21:26:00.000Z","key":1486502760000,"doc_count":7},{"key_as_string":"2017-02-07T21:27:00.000Z","key":1486502820000,"doc_count":15},{"key_as_string":"2017-02-07T21:28:00.000Z","key":1486502880000,"doc_count":14},{"key_as_string":"2017-02-07T21:29:00.000Z","key":1486502940000,"doc_count":8},{"key_as_string":"2017-02-07T21:30:00.000Z","key":1486503000000,"doc_count":7},{"key_as_string":"2017-02-07T21:31:00.000Z","key":1486503060000,"doc_count":12},{"key_as_string":"2017-02-07T21:32:00.000Z","key":1486503120000,"doc_count":10},{"key_as_string":"2017-02-07T21:33:00.000Z","key":1486503180000,"doc_count":10},{"key_as_string":"2017-02-07T21:34:00.000Z","key":1486503240000,"doc_count":14},{"key_as_string":"2017-02-07T21:35:00.000Z","key":1486503300000,"doc_count":9},{"key_as_string":"2017-02-07T21:36:00.000Z","key":1486503360000,"doc_count":9},{"key_as_string":"2017-02-07T21:37:00.000Z","key":1486503420000,"doc_count":13},{"key_as_string":"2017-02-07T21:38:00.000Z","key":1486503480000,"doc_count":7},{"key_as_string":"2017-02-07T21:39:00.000Z","key":1486503540000,"doc_count":6},{"key_as_string":"2017-02-07T21:40:00.000Z","key":1486503600000,"doc_count":16},{"key_as_string":"2017-02-07T21:41:00.000Z","key":1486503660000,"doc_count":13},{"key_as_string":"2017-02-07T21:42:00.000Z","key":1486503720000,"doc_count":9},{"key_as_string":"2017-02-07T21:43:00.000Z","key":1486503780000,"doc_count":10},{"key_as_string":"2017-02-07T21:44:00.000Z","key":1486503840000,"doc_count":9},{"key_as_string":"2017-02-07T21:45:00.000Z","key":1486503900000,"doc_count":10},{"key_as_string":"2017-02-07T21:46:00.000Z","key":1486503960000,"doc_count":13},{"key_as_string":"2017-02-07T21:47:00.000Z","key":1486504020000,"doc_count":11},{"key_as_string":"2017-02-07T21:48:00.000Z","key":1486504080000,"doc_count":8},{"key_as_string":"2017-02-07T21:49:00.000Z","key":1486504140000,"doc_count":11},{"key_as_string":"2017-02-07T21:50:00.000Z","key":1486504200000,"doc_count":14},{"key_as_string":"2017-02-07T21:51:00.000Z","key":1486504260000,"doc_count":7},{"key_as_string":"2017-02-07T21:52:00.000Z","key":1486504320000,"doc_count":11},{"key_as_string":"2017-02-07T21:53:00.000Z","key":1486504380000,"doc_count":9},{"key_as_string":"2017-02-07T21:54:00.000Z","key":1486504440000,"doc_count":10},{"key_as_string":"2017-02-07T21:55:00.000Z","key":1486504500000,"doc_count":11},{"key_as_string":"2017-02-07T21:56:00.000Z","key":1486504560000,"doc_count":12},{"key_as_string":"2017-02-07T21:57:00.000Z","key":1486504620000,"doc_count":14},{"key_as_string":"2017-02-07T21:58:00.000Z","key":1486504680000,"doc_count":9},{"key_as_string":"2017-02-07T21:59:00.000Z","key":1486504740000,"doc_count":7},{"key_as_string":"2017-02-07T22:00:00.000Z","key":1486504800000,"doc_count":14},{"key_as_string":"2017-02-07T22:01:00.000Z","key":1486504860000,"doc_count":12},{"key_as_string":"2017-02-07T22:02:00.000Z","key":1486504920000,"doc_count":7},{"key_as_string":"2017-02-07T22:03:00.000Z","key":1486504980000,"doc_count":16},{"key_as_string":"2017-02-07T22:04:00.000Z","key":1486505040000,"doc_count":9},{"key_as_string":"2017-02-07T22:05:00.000Z","key":1486505100000,"doc_count":10},{"key_as_string":"2017-02-07T22:06:00.000Z","key":1486505160000,"doc_count":11},{"key_as_string":"2017-02-07T22:07:00.000Z","key":1486505220000,"doc_count":16},{"key_as_string":"2017-02-07T22:08:00.000Z","key":1486505280000,"doc_count":9},{"key_as_string":"2017-02-07T22:09:00.000Z","key":1486505340000,"doc_count":7},{"key_as_string":"2017-02-07T22:10:00.000Z","key":1486505400000,"doc_count":15},{"key_as_string":"2017-02-07T22:11:00.000Z","key":1486505460000,"doc_count":12},{"key_as_string":"2017-02-07T22:12:00.000Z","key":1486505520000,"doc_count":8},{"key_as_string":"2017-02-07T22:13:00.000Z","key":1486505580000,"doc_count":12},{"key_as_string":"2017-02-07T22:14:00.000Z","key":1486505640000,"doc_count":15},{"key_as_string":"2017-02-07T22:15:00.000Z","key":1486505700000,"doc_count":10},{"key_as_string":"2017-02-07T22:16:00.000Z","key":1486505760000,"doc_count":11},{"key_as_string":"2017-02-07T22:17:00.000Z","key":1486505820000,"doc_count":6},{"key_as_string":"2017-02-07T22:18:00.000Z","key":1486505880000,"doc_count":13},{"key_as_string":"2017-02-07T22:19:00.000Z","key":1486505940000,"doc_count":8},{"key_as_string":"2017-02-07T22:20:00.000Z","key":1486506000000,"doc_count":5},{"key_as_string":"2017-02-07T22:21:00.000Z","key":1486506060000,"doc_count":15},{"key_as_string":"2017-02-07T22:22:00.000Z","key":1486506120000,"doc_count":10},{"key_as_string":"2017-02-07T22:23:00.000Z","key":1486506180000,"doc_count":10},{"key_as_string":"2017-02-07T22:24:00.000Z","key":1486506240000,"doc_count":14},{"key_as_string":"2017-02-07T22:25:00.000Z","key":1486506300000,"doc_count":8},{"key_as_string":"2017-02-07T22:26:00.000Z","key":1486506360000,"doc_count":12},{"key_as_string":"2017-02-07T22:27:00.000Z","key":1486506420000,"doc_count":6},{"key_as_string":"2017-02-07T22:28:00.000Z","key":1486506480000,"doc_count":12},{"key_as_string":"2017-02-07T22:29:00.000Z","key":1486506540000,"doc_count":14},{"key_as_string":"2017-02-07T22:30:00.000Z","key":1486506600000,"doc_count":8},{"key_as_string":"2017-02-07T22:31:00.000Z","key":1486506660000,"doc_count":10},{"key_as_string":"2017-02-07T22:32:00.000Z","key":1486506720000,"doc_count":15},{"key_as_string":"2017-02-07T22:33:00.000Z","key":1486506780000,"doc_count":9},{"key_as_string":"2017-02-07T22:34:00.000Z","key":1486506840000,"doc_count":7},{"key_as_string":"2017-02-07T22:35:00.000Z","key":1486506900000,"doc_count":12},{"key_as_string":"2017-02-07T22:36:00.000Z","key":1486506960000,"doc_count":9},{"key_as_string":"2017-02-07T22:37:00.000Z","key":1486507020000,"doc_count":15},{"key_as_string":"2017-02-07T22:38:00.000Z","key":1486507080000,"doc_count":6},{"key_as_string":"2017-02-07T22:39:00.000Z","key":1486507140000,"doc_count":12},{"key_as_string":"2017-02-07T22:40:00.000Z","key":1486507200000,"doc_count":13},{"key_as_string":"2017-02-07T22:41:00.000Z","key":1486507260000,"doc_count":8},{"key_as_string":"2017-02-07T22:42:00.000Z","key":1486507320000,"doc_count":13},{"key_as_string":"2017-02-07T22:43:00.000Z","key":1486507380000,"doc_count":7},{"key_as_string":"2017-02-07T22:44:00.000Z","key":1486507440000,"doc_count":10},{"key_as_string":"2017-02-07T22:45:00.000Z","key":1486507500000,"doc_count":7},{"key_as_string":"2017-02-07T22:46:00.000Z","key":1486507560000,"doc_count":9},{"key_as_string":"2017-02-07T22:47:00.000Z","key":1486507620000,"doc_count":12},{"key_as_string":"2017-02-07T22:48:00.000Z","key":1486507680000,"doc_count":9},{"key_as_string":"2017-02-07T22:49:00.000Z","key":1486507740000,"doc_count":6},{"key_as_string":"2017-02-07T22:50:00.000Z","key":1486507800000,"doc_count":12},{"key_as_string":"2017-02-07T22:51:00.000Z","key":1486507860000,"doc_count":12},{"key_as_string":"2017-02-07T22:52:00.000Z","key":1486507920000,"doc_count":9},{"key_as_string":"2017-02-07T22:53:00.000Z","key":1486507980000,"doc_count":10},{"key_as_string":"2017-02-07T22:54:00.000Z","key":1486508040000,"doc_count":12},{"key_as_string":"2017-02-07T22:55:00.000Z","key":1486508100000,"doc_count":8},{"key_as_string":"2017-02-07T22:56:00.000Z","key":1486508160000,"doc_count":9},{"key_as_string":"2017-02-07T22:57:00.000Z","key":1486508220000,"doc_count":10},{"key_as_string":"2017-02-07T22:58:00.000Z","key":1486508280000,"doc_count":13},{"key_as_string":"2017-02-07T22:59:00.000Z","key":1486508340000,"doc_count":12},{"key_as_string":"2017-02-07T23:00:00.000Z","key":1486508400000,"doc_count":9},{"key_as_string":"2017-02-07T23:01:00.000Z","key":1486508460000,"doc_count":9},{"key_as_string":"2017-02-07T23:02:00.000Z","key":1486508520000,"doc_count":10},{"key_as_string":"2017-02-07T23:03:00.000Z","key":1486508580000,"doc_count":14},{"key_as_string":"2017-02-07T23:04:00.000Z","key":1486508640000,"doc_count":9},{"key_as_string":"2017-02-07T23:05:00.000Z","key":1486508700000,"doc_count":11},{"key_as_string":"2017-02-07T23:06:00.000Z","key":1486508760000,"doc_count":10},{"key_as_string":"2017-02-07T23:07:00.000Z","key":1486508820000,"doc_count":16},{"key_as_string":"2017-02-07T23:08:00.000Z","key":1486508880000,"doc_count":12},{"key_as_string":"2017-02-07T23:09:00.000Z","key":1486508940000,"doc_count":11},{"key_as_string":"2017-02-07T23:10:00.000Z","key":1486509000000,"doc_count":9},{"key_as_string":"2017-02-07T23:11:00.000Z","key":1486509060000,"doc_count":13},{"key_as_string":"2017-02-07T23:12:00.000Z","key":1486509120000,"doc_count":5},{"key_as_string":"2017-02-07T23:13:00.000Z","key":1486509180000,"doc_count":9},{"key_as_string":"2017-02-07T23:14:00.000Z","key":1486509240000,"doc_count":11},{"key_as_string":"2017-02-07T23:15:00.000Z","key":1486509300000,"doc_count":14},{"key_as_string":"2017-02-07T23:16:00.000Z","key":1486509360000,"doc_count":11},{"key_as_string":"2017-02-07T23:17:00.000Z","key":1486509420000,"doc_count":8},{"key_as_string":"2017-02-07T23:18:00.000Z","key":1486509480000,"doc_count":12},{"key_as_string":"2017-02-07T23:19:00.000Z","key":1486509540000,"doc_count":8},{"key_as_string":"2017-02-07T23:20:00.000Z","key":1486509600000,"doc_count":7},{"key_as_string":"2017-02-07T23:21:00.000Z","key":1486509660000,"doc_count":13},{"key_as_string":"2017-02-07T23:22:00.000Z","key":1486509720000,"doc_count":13},{"key_as_string":"2017-02-07T23:23:00.000Z","key":1486509780000,"doc_count":5},{"key_as_string":"2017-02-07T23:24:00.000Z","key":1486509840000,"doc_count":13},{"key_as_string":"2017-02-07T23:25:00.000Z","key":1486509900000,"doc_count":5},{"key_as_string":"2017-02-07T23:26:00.000Z","key":1486509960000,"doc_count":11},{"key_as_string":"2017-02-07T23:27:00.000Z","key":1486510020000,"doc_count":11},{"key_as_string":"2017-02-07T23:28:00.000Z","key":1486510080000,"doc_count":12},{"key_as_string":"2017-02-07T23:29:00.000Z","key":1486510140000,"doc_count":5},{"key_as_string":"2017-02-07T23:30:00.000Z","key":1486510200000,"doc_count":11},{"key_as_string":"2017-02-07T23:31:00.000Z","key":1486510260000,"doc_count":6},{"key_as_string":"2017-02-07T23:32:00.000Z","key":1486510320000,"doc_count":15},{"key_as_string":"2017-02-07T23:33:00.000Z","key":1486510380000,"doc_count":13},{"key_as_string":"2017-02-07T23:34:00.000Z","key":1486510440000,"doc_count":15},{"key_as_string":"2017-02-07T23:35:00.000Z","key":1486510500000,"doc_count":8},{"key_as_string":"2017-02-07T23:36:00.000Z","key":1486510560000,"doc_count":11},{"key_as_string":"2017-02-07T23:37:00.000Z","key":1486510620000,"doc_count":15},{"key_as_string":"2017-02-07T23:38:00.000Z","key":1486510680000,"doc_count":7},{"key_as_string":"2017-02-07T23:39:00.000Z","key":1486510740000,"doc_count":11},{"key_as_string":"2017-02-07T23:40:00.000Z","key":1486510800000,"doc_count":7},{"key_as_string":"2017-02-07T23:41:00.000Z","key":1486510860000,"doc_count":9},{"key_as_string":"2017-02-07T23:42:00.000Z","key":1486510920000,"doc_count":11},{"key_as_string":"2017-02-07T23:43:00.000Z","key":1486510980000,"doc_count":12},{"key_as_string":"2017-02-07T23:44:00.000Z","key":1486511040000,"doc_count":19},{"key_as_string":"2017-02-07T23:45:00.000Z","key":1486511100000,"doc_count":6},{"key_as_string":"2017-02-07T23:46:00.000Z","key":1486511160000,"doc_count":12},{"key_as_string":"2017-02-07T23:47:00.000Z","key":1486511220000,"doc_count":7},{"key_as_string":"2017-02-07T23:48:00.000Z","key":1486511280000,"doc_count":19},{"key_as_string":"2017-02-07T23:49:00.000Z","key":1486511340000,"doc_count":4},{"key_as_string":"2017-02-07T23:50:00.000Z","key":1486511400000,"doc_count":15},{"key_as_string":"2017-02-07T23:51:00.000Z","key":1486511460000,"doc_count":9},{"key_as_string":"2017-02-07T23:52:00.000Z","key":1486511520000,"doc_count":7},{"key_as_string":"2017-02-07T23:53:00.000Z","key":1486511580000,"doc_count":11},{"key_as_string":"2017-02-07T23:54:00.000Z","key":1486511640000,"doc_count":11},{"key_as_string":"2017-02-07T23:55:00.000Z","key":1486511700000,"doc_count":10},{"key_as_string":"2017-02-07T23:56:00.000Z","key":1486511760000,"doc_count":7},{"key_as_string":"2017-02-07T23:57:00.000Z","key":1486511820000,"doc_count":16},{"key_as_string":"2017-02-07T23:58:00.000Z","key":1486511880000,"doc_count":9},{"key_as_string":"2017-02-07T23:59:00.000Z","key":1486511940000,"doc_count":13},{"key_as_string":"2017-02-08T00:00:00.000Z","key":1486512000000,"doc_count":11},{"key_as_string":"2017-02-08T00:01:00.000Z","key":1486512060000,"doc_count":8},{"key_as_string":"2017-02-08T00:02:00.000Z","key":1486512120000,"doc_count":11},{"key_as_string":"2017-02-08T00:03:00.000Z","key":1486512180000,"doc_count":11},{"key_as_string":"2017-02-08T00:04:00.000Z","key":1486512240000,"doc_count":10},{"key_as_string":"2017-02-08T00:05:00.000Z","key":1486512300000,"doc_count":11},{"key_as_string":"2017-02-08T00:06:00.000Z","key":1486512360000,"doc_count":9},{"key_as_string":"2017-02-08T00:07:00.000Z","key":1486512420000,"doc_count":10},{"key_as_string":"2017-02-08T00:08:00.000Z","key":1486512480000,"doc_count":9},{"key_as_string":"2017-02-08T00:09:00.000Z","key":1486512540000,"doc_count":12},{"key_as_string":"2017-02-08T00:10:00.000Z","key":1486512600000,"doc_count":7},{"key_as_string":"2017-02-08T00:11:00.000Z","key":1486512660000,"doc_count":14},{"key_as_string":"2017-02-08T00:12:00.000Z","key":1486512720000,"doc_count":10},{"key_as_string":"2017-02-08T00:13:00.000Z","key":1486512780000,"doc_count":8},{"key_as_string":"2017-02-08T00:14:00.000Z","key":1486512840000,"doc_count":7},{"key_as_string":"2017-02-08T00:15:00.000Z","key":1486512900000,"doc_count":10},{"key_as_string":"2017-02-08T00:16:00.000Z","key":1486512960000,"doc_count":12},{"key_as_string":"2017-02-08T00:17:00.000Z","key":1486513020000,"doc_count":13},{"key_as_string":"2017-02-08T00:18:00.000Z","key":1486513080000,"doc_count":7},{"key_as_string":"2017-02-08T00:19:00.000Z","key":1486513140000,"doc_count":7},{"key_as_string":"2017-02-08T00:20:00.000Z","key":1486513200000,"doc_count":7},{"key_as_string":"2017-02-08T00:21:00.000Z","key":1486513260000,"doc_count":9},{"key_as_string":"2017-02-08T00:22:00.000Z","key":1486513320000,"doc_count":13},{"key_as_string":"2017-02-08T00:23:00.000Z","key":1486513380000,"doc_count":10},{"key_as_string":"2017-02-08T00:24:00.000Z","key":1486513440000,"doc_count":8},{"key_as_string":"2017-02-08T00:25:00.000Z","key":1486513500000,"doc_count":13},{"key_as_string":"2017-02-08T00:26:00.000Z","key":1486513560000,"doc_count":10},{"key_as_string":"2017-02-08T00:27:00.000Z","key":1486513620000,"doc_count":8},{"key_as_string":"2017-02-08T00:28:00.000Z","key":1486513680000,"doc_count":15},{"key_as_string":"2017-02-08T00:29:00.000Z","key":1486513740000,"doc_count":7},{"key_as_string":"2017-02-08T00:30:00.000Z","key":1486513800000,"doc_count":9},{"key_as_string":"2017-02-08T00:31:00.000Z","key":1486513860000,"doc_count":13},{"key_as_string":"2017-02-08T00:32:00.000Z","key":1486513920000,"doc_count":8},{"key_as_string":"2017-02-08T00:33:00.000Z","key":1486513980000,"doc_count":12},{"key_as_string":"2017-02-08T00:34:00.000Z","key":1486514040000,"doc_count":13},{"key_as_string":"2017-02-08T00:35:00.000Z","key":1486514100000,"doc_count":9},{"key_as_string":"2017-02-08T00:36:00.000Z","key":1486514160000,"doc_count":12},{"key_as_string":"2017-02-08T00:37:00.000Z","key":1486514220000,"doc_count":9},{"key_as_string":"2017-02-08T00:38:00.000Z","key":1486514280000,"doc_count":9},{"key_as_string":"2017-02-08T00:39:00.000Z","key":1486514340000,"doc_count":16},{"key_as_string":"2017-02-08T00:40:00.000Z","key":1486514400000,"doc_count":10},{"key_as_string":"2017-02-08T00:41:00.000Z","key":1486514460000,"doc_count":13},{"key_as_string":"2017-02-08T00:42:00.000Z","key":1486514520000,"doc_count":6},{"key_as_string":"2017-02-08T00:43:00.000Z","key":1486514580000,"doc_count":16},{"key_as_string":"2017-02-08T00:44:00.000Z","key":1486514640000,"doc_count":16},{"key_as_string":"2017-02-08T00:45:00.000Z","key":1486514700000,"doc_count":6},{"key_as_string":"2017-02-08T00:46:00.000Z","key":1486514760000,"doc_count":11},{"key_as_string":"2017-02-08T00:47:00.000Z","key":1486514820000,"doc_count":11},{"key_as_string":"2017-02-08T00:48:00.000Z","key":1486514880000,"doc_count":9},{"key_as_string":"2017-02-08T00:49:00.000Z","key":1486514940000,"doc_count":11},{"key_as_string":"2017-02-08T00:50:00.000Z","key":1486515000000,"doc_count":9},{"key_as_string":"2017-02-08T00:51:00.000Z","key":1486515060000,"doc_count":12},{"key_as_string":"2017-02-08T00:52:00.000Z","key":1486515120000,"doc_count":10},{"key_as_string":"2017-02-08T00:53:00.000Z","key":1486515180000,"doc_count":13},{"key_as_string":"2017-02-08T00:54:00.000Z","key":1486515240000,"doc_count":9},{"key_as_string":"2017-02-08T00:55:00.000Z","key":1486515300000,"doc_count":15},{"key_as_string":"2017-02-08T00:56:00.000Z","key":1486515360000,"doc_count":11},{"key_as_string":"2017-02-08T00:57:00.000Z","key":1486515420000,"doc_count":12},{"key_as_string":"2017-02-08T00:58:00.000Z","key":1486515480000,"doc_count":17},{"key_as_string":"2017-02-08T00:59:00.000Z","key":1486515540000,"doc_count":11},{"key_as_string":"2017-02-08T01:00:00.000Z","key":1486515600000,"doc_count":14},{"key_as_string":"2017-02-08T01:01:00.000Z","key":1486515660000,"doc_count":12},{"key_as_string":"2017-02-08T01:02:00.000Z","key":1486515720000,"doc_count":8},{"key_as_string":"2017-02-08T01:03:00.000Z","key":1486515780000,"doc_count":12},{"key_as_string":"2017-02-08T01:04:00.000Z","key":1486515840000,"doc_count":8},{"key_as_string":"2017-02-08T01:05:00.000Z","key":1486515900000,"doc_count":13},{"key_as_string":"2017-02-08T01:06:00.000Z","key":1486515960000,"doc_count":9},{"key_as_string":"2017-02-08T01:07:00.000Z","key":1486516020000,"doc_count":7},{"key_as_string":"2017-02-08T01:08:00.000Z","key":1486516080000,"doc_count":14},{"key_as_string":"2017-02-08T01:09:00.000Z","key":1486516140000,"doc_count":9},{"key_as_string":"2017-02-08T01:10:00.000Z","key":1486516200000,"doc_count":10},{"key_as_string":"2017-02-08T01:11:00.000Z","key":1486516260000,"doc_count":12},{"key_as_string":"2017-02-08T01:12:00.000Z","key":1486516320000,"doc_count":8},{"key_as_string":"2017-02-08T01:13:00.000Z","key":1486516380000,"doc_count":9},{"key_as_string":"2017-02-08T01:14:00.000Z","key":1486516440000,"doc_count":11},{"key_as_string":"2017-02-08T01:15:00.000Z","key":1486516500000,"doc_count":10},{"key_as_string":"2017-02-08T01:16:00.000Z","key":1486516560000,"doc_count":16},{"key_as_string":"2017-02-08T01:17:00.000Z","key":1486516620000,"doc_count":12},{"key_as_string":"2017-02-08T01:18:00.000Z","key":1486516680000,"doc_count":7},{"key_as_string":"2017-02-08T01:19:00.000Z","key":1486516740000,"doc_count":7},{"key_as_string":"2017-02-08T01:20:00.000Z","key":1486516800000,"doc_count":12},{"key_as_string":"2017-02-08T01:21:00.000Z","key":1486516860000,"doc_count":10},{"key_as_string":"2017-02-08T01:22:00.000Z","key":1486516920000,"doc_count":15},{"key_as_string":"2017-02-08T01:23:00.000Z","key":1486516980000,"doc_count":9},{"key_as_string":"2017-02-08T01:24:00.000Z","key":1486517040000,"doc_count":15},{"key_as_string":"2017-02-08T01:25:00.000Z","key":1486517100000,"doc_count":9},{"key_as_string":"2017-02-08T01:26:00.000Z","key":1486517160000,"doc_count":15},{"key_as_string":"2017-02-08T01:27:00.000Z","key":1486517220000,"doc_count":11},{"key_as_string":"2017-02-08T01:28:00.000Z","key":1486517280000,"doc_count":15},{"key_as_string":"2017-02-08T01:29:00.000Z","key":1486517340000,"doc_count":17},{"key_as_string":"2017-02-08T01:30:00.000Z","key":1486517400000,"doc_count":5},{"key_as_string":"2017-02-08T01:31:00.000Z","key":1486517460000,"doc_count":10},{"key_as_string":"2017-02-08T01:32:00.000Z","key":1486517520000,"doc_count":9},{"key_as_string":"2017-02-08T01:33:00.000Z","key":1486517580000,"doc_count":13},{"key_as_string":"2017-02-08T01:34:00.000Z","key":1486517640000,"doc_count":17},{"key_as_string":"2017-02-08T01:35:00.000Z","key":1486517700000,"doc_count":8},{"key_as_string":"2017-02-08T01:36:00.000Z","key":1486517760000,"doc_count":7},{"key_as_string":"2017-02-08T01:37:00.000Z","key":1486517820000,"doc_count":14},{"key_as_string":"2017-02-08T01:38:00.000Z","key":1486517880000,"doc_count":11},{"key_as_string":"2017-02-08T01:39:00.000Z","key":1486517940000,"doc_count":4},{"key_as_string":"2017-02-08T01:40:00.000Z","key":1486518000000,"doc_count":13},{"key_as_string":"2017-02-08T01:41:00.000Z","key":1486518060000,"doc_count":13},{"key_as_string":"2017-02-08T01:42:00.000Z","key":1486518120000,"doc_count":6},{"key_as_string":"2017-02-08T01:43:00.000Z","key":1486518180000,"doc_count":12},{"key_as_string":"2017-02-08T01:44:00.000Z","key":1486518240000,"doc_count":13},{"key_as_string":"2017-02-08T01:45:00.000Z","key":1486518300000,"doc_count":9},{"key_as_string":"2017-02-08T01:46:00.000Z","key":1486518360000,"doc_count":15},{"key_as_string":"2017-02-08T01:47:00.000Z","key":1486518420000,"doc_count":14},{"key_as_string":"2017-02-08T01:48:00.000Z","key":1486518480000,"doc_count":7},{"key_as_string":"2017-02-08T01:49:00.000Z","key":1486518540000,"doc_count":15},{"key_as_string":"2017-02-08T01:50:00.000Z","key":1486518600000,"doc_count":10},{"key_as_string":"2017-02-08T01:51:00.000Z","key":1486518660000,"doc_count":9},{"key_as_string":"2017-02-08T01:52:00.000Z","key":1486518720000,"doc_count":7},{"key_as_string":"2017-02-08T01:53:00.000Z","key":1486518780000,"doc_count":14},{"key_as_string":"2017-02-08T01:54:00.000Z","key":1486518840000,"doc_count":10},{"key_as_string":"2017-02-08T01:55:00.000Z","key":1486518900000,"doc_count":9},{"key_as_string":"2017-02-08T01:56:00.000Z","key":1486518960000,"doc_count":11},{"key_as_string":"2017-02-08T01:57:00.000Z","key":1486519020000,"doc_count":7},{"key_as_string":"2017-02-08T01:58:00.000Z","key":1486519080000,"doc_count":12},{"key_as_string":"2017-02-08T01:59:00.000Z","key":1486519140000,"doc_count":7},{"key_as_string":"2017-02-08T02:00:00.000Z","key":1486519200000,"doc_count":16},{"key_as_string":"2017-02-08T02:01:00.000Z","key":1486519260000,"doc_count":8},{"key_as_string":"2017-02-08T02:02:00.000Z","key":1486519320000,"doc_count":5},{"key_as_string":"2017-02-08T02:03:00.000Z","key":1486519380000,"doc_count":12},{"key_as_string":"2017-02-08T02:04:00.000Z","key":1486519440000,"doc_count":11},{"key_as_string":"2017-02-08T02:05:00.000Z","key":1486519500000,"doc_count":12},{"key_as_string":"2017-02-08T02:06:00.000Z","key":1486519560000,"doc_count":12},{"key_as_string":"2017-02-08T02:07:00.000Z","key":1486519620000,"doc_count":9},{"key_as_string":"2017-02-08T02:08:00.000Z","key":1486519680000,"doc_count":11},{"key_as_string":"2017-02-08T02:09:00.000Z","key":1486519740000,"doc_count":16},{"key_as_string":"2017-02-08T02:10:00.000Z","key":1486519800000,"doc_count":9},{"key_as_string":"2017-02-08T02:11:00.000Z","key":1486519860000,"doc_count":8},{"key_as_string":"2017-02-08T02:12:00.000Z","key":1486519920000,"doc_count":9},{"key_as_string":"2017-02-08T02:13:00.000Z","key":1486519980000,"doc_count":8},{"key_as_string":"2017-02-08T02:14:00.000Z","key":1486520040000,"doc_count":12},{"key_as_string":"2017-02-08T02:15:00.000Z","key":1486520100000,"doc_count":8},{"key_as_string":"2017-02-08T02:16:00.000Z","key":1486520160000,"doc_count":9},{"key_as_string":"2017-02-08T02:17:00.000Z","key":1486520220000,"doc_count":11},{"key_as_string":"2017-02-08T02:18:00.000Z","key":1486520280000,"doc_count":8},{"key_as_string":"2017-02-08T02:19:00.000Z","key":1486520340000,"doc_count":9},{"key_as_string":"2017-02-08T02:20:00.000Z","key":1486520400000,"doc_count":7},{"key_as_string":"2017-02-08T02:21:00.000Z","key":1486520460000,"doc_count":15},{"key_as_string":"2017-02-08T02:22:00.000Z","key":1486520520000,"doc_count":6},{"key_as_string":"2017-02-08T02:23:00.000Z","key":1486520580000,"doc_count":11},{"key_as_string":"2017-02-08T02:24:00.000Z","key":1486520640000,"doc_count":14},{"key_as_string":"2017-02-08T02:25:00.000Z","key":1486520700000,"doc_count":8},{"key_as_string":"2017-02-08T02:26:00.000Z","key":1486520760000,"doc_count":11},{"key_as_string":"2017-02-08T02:27:00.000Z","key":1486520820000,"doc_count":15},{"key_as_string":"2017-02-08T02:28:00.000Z","key":1486520880000,"doc_count":6},{"key_as_string":"2017-02-08T02:29:00.000Z","key":1486520940000,"doc_count":5},{"key_as_string":"2017-02-08T02:30:00.000Z","key":1486521000000,"doc_count":18},{"key_as_string":"2017-02-08T02:31:00.000Z","key":1486521060000,"doc_count":7},{"key_as_string":"2017-02-08T02:32:00.000Z","key":1486521120000,"doc_count":6},{"key_as_string":"2017-02-08T02:33:00.000Z","key":1486521180000,"doc_count":15},{"key_as_string":"2017-02-08T02:34:00.000Z","key":1486521240000,"doc_count":11},{"key_as_string":"2017-02-08T02:35:00.000Z","key":1486521300000,"doc_count":13},{"key_as_string":"2017-02-08T02:36:00.000Z","key":1486521360000,"doc_count":12},{"key_as_string":"2017-02-08T02:37:00.000Z","key":1486521420000,"doc_count":9},{"key_as_string":"2017-02-08T02:38:00.000Z","key":1486521480000,"doc_count":14},{"key_as_string":"2017-02-08T02:39:00.000Z","key":1486521540000,"doc_count":9},{"key_as_string":"2017-02-08T02:40:00.000Z","key":1486521600000,"doc_count":12},{"key_as_string":"2017-02-08T02:41:00.000Z","key":1486521660000,"doc_count":10},{"key_as_string":"2017-02-08T02:42:00.000Z","key":1486521720000,"doc_count":12},{"key_as_string":"2017-02-08T02:43:00.000Z","key":1486521780000,"doc_count":8},{"key_as_string":"2017-02-08T02:44:00.000Z","key":1486521840000,"doc_count":15},{"key_as_string":"2017-02-08T02:45:00.000Z","key":1486521900000,"doc_count":6},{"key_as_string":"2017-02-08T02:46:00.000Z","key":1486521960000,"doc_count":12},{"key_as_string":"2017-02-08T02:47:00.000Z","key":1486522020000,"doc_count":10},{"key_as_string":"2017-02-08T02:48:00.000Z","key":1486522080000,"doc_count":13},{"key_as_string":"2017-02-08T02:49:00.000Z","key":1486522140000,"doc_count":14},{"key_as_string":"2017-02-08T02:50:00.000Z","key":1486522200000,"doc_count":8},{"key_as_string":"2017-02-08T02:51:00.000Z","key":1486522260000,"doc_count":12},{"key_as_string":"2017-02-08T02:52:00.000Z","key":1486522320000,"doc_count":6},{"key_as_string":"2017-02-08T02:53:00.000Z","key":1486522380000,"doc_count":14},{"key_as_string":"2017-02-08T02:54:00.000Z","key":1486522440000,"doc_count":12},{"key_as_string":"2017-02-08T02:55:00.000Z","key":1486522500000,"doc_count":11},{"key_as_string":"2017-02-08T02:56:00.000Z","key":1486522560000,"doc_count":14},{"key_as_string":"2017-02-08T02:57:00.000Z","key":1486522620000,"doc_count":13},{"key_as_string":"2017-02-08T02:58:00.000Z","key":1486522680000,"doc_count":7},{"key_as_string":"2017-02-08T02:59:00.000Z","key":1486522740000,"doc_count":9},{"key_as_string":"2017-02-08T03:00:00.000Z","key":1486522800000,"doc_count":9},{"key_as_string":"2017-02-08T03:01:00.000Z","key":1486522860000,"doc_count":13},{"key_as_string":"2017-02-08T03:02:00.000Z","key":1486522920000,"doc_count":10},{"key_as_string":"2017-02-08T03:03:00.000Z","key":1486522980000,"doc_count":12},{"key_as_string":"2017-02-08T03:04:00.000Z","key":1486523040000,"doc_count":4},{"key_as_string":"2017-02-08T03:05:00.000Z","key":1486523100000,"doc_count":12},{"key_as_string":"2017-02-08T03:06:00.000Z","key":1486523160000,"doc_count":10},{"key_as_string":"2017-02-08T03:07:00.000Z","key":1486523220000,"doc_count":8},{"key_as_string":"2017-02-08T03:08:00.000Z","key":1486523280000,"doc_count":12},{"key_as_string":"2017-02-08T03:09:00.000Z","key":1486523340000,"doc_count":12},{"key_as_string":"2017-02-08T03:10:00.000Z","key":1486523400000,"doc_count":9},{"key_as_string":"2017-02-08T03:11:00.000Z","key":1486523460000,"doc_count":12},{"key_as_string":"2017-02-08T03:12:00.000Z","key":1486523520000,"doc_count":6},{"key_as_string":"2017-02-08T03:13:00.000Z","key":1486523580000,"doc_count":11},{"key_as_string":"2017-02-08T03:14:00.000Z","key":1486523640000,"doc_count":8},{"key_as_string":"2017-02-08T03:15:00.000Z","key":1486523700000,"doc_count":11},{"key_as_string":"2017-02-08T03:16:00.000Z","key":1486523760000,"doc_count":9},{"key_as_string":"2017-02-08T03:17:00.000Z","key":1486523820000,"doc_count":9},{"key_as_string":"2017-02-08T03:18:00.000Z","key":1486523880000,"doc_count":16},{"key_as_string":"2017-02-08T03:19:00.000Z","key":1486523940000,"doc_count":6},{"key_as_string":"2017-02-08T03:20:00.000Z","key":1486524000000,"doc_count":10},{"key_as_string":"2017-02-08T03:21:00.000Z","key":1486524060000,"doc_count":14},{"key_as_string":"2017-02-08T03:22:00.000Z","key":1486524120000,"doc_count":3},{"key_as_string":"2017-02-08T03:23:00.000Z","key":1486524180000,"doc_count":11},{"key_as_string":"2017-02-08T03:24:00.000Z","key":1486524240000,"doc_count":13},{"key_as_string":"2017-02-08T03:25:00.000Z","key":1486524300000,"doc_count":11},{"key_as_string":"2017-02-08T03:26:00.000Z","key":1486524360000,"doc_count":18},{"key_as_string":"2017-02-08T03:27:00.000Z","key":1486524420000,"doc_count":12},{"key_as_string":"2017-02-08T03:28:00.000Z","key":1486524480000,"doc_count":11},{"key_as_string":"2017-02-08T03:29:00.000Z","key":1486524540000,"doc_count":8},{"key_as_string":"2017-02-08T03:30:00.000Z","key":1486524600000,"doc_count":18},{"key_as_string":"2017-02-08T03:31:00.000Z","key":1486524660000,"doc_count":12},{"key_as_string":"2017-02-08T03:32:00.000Z","key":1486524720000,"doc_count":13},{"key_as_string":"2017-02-08T03:33:00.000Z","key":1486524780000,"doc_count":12},{"key_as_string":"2017-02-08T03:34:00.000Z","key":1486524840000,"doc_count":10},{"key_as_string":"2017-02-08T03:35:00.000Z","key":1486524900000,"doc_count":10},{"key_as_string":"2017-02-08T03:36:00.000Z","key":1486524960000,"doc_count":10},{"key_as_string":"2017-02-08T03:37:00.000Z","key":1486525020000,"doc_count":9},{"key_as_string":"2017-02-08T03:38:00.000Z","key":1486525080000,"doc_count":20},{"key_as_string":"2017-02-08T03:39:00.000Z","key":1486525140000,"doc_count":8},{"key_as_string":"2017-02-08T03:40:00.000Z","key":1486525200000,"doc_count":7},{"key_as_string":"2017-02-08T03:41:00.000Z","key":1486525260000,"doc_count":10},{"key_as_string":"2017-02-08T03:42:00.000Z","key":1486525320000,"doc_count":12},{"key_as_string":"2017-02-08T03:43:00.000Z","key":1486525380000,"doc_count":9},{"key_as_string":"2017-02-08T03:44:00.000Z","key":1486525440000,"doc_count":7},{"key_as_string":"2017-02-08T03:45:00.000Z","key":1486525500000,"doc_count":8},{"key_as_string":"2017-02-08T03:46:00.000Z","key":1486525560000,"doc_count":10},{"key_as_string":"2017-02-08T03:47:00.000Z","key":1486525620000,"doc_count":10},{"key_as_string":"2017-02-08T03:48:00.000Z","key":1486525680000,"doc_count":7},{"key_as_string":"2017-02-08T03:49:00.000Z","key":1486525740000,"doc_count":9},{"key_as_string":"2017-02-08T03:50:00.000Z","key":1486525800000,"doc_count":11},{"key_as_string":"2017-02-08T03:51:00.000Z","key":1486525860000,"doc_count":8},{"key_as_string":"2017-02-08T03:52:00.000Z","key":1486525920000,"doc_count":10},{"key_as_string":"2017-02-08T03:53:00.000Z","key":1486525980000,"doc_count":12},{"key_as_string":"2017-02-08T03:54:00.000Z","key":1486526040000,"doc_count":14},{"key_as_string":"2017-02-08T03:55:00.000Z","key":1486526100000,"doc_count":9},{"key_as_string":"2017-02-08T03:56:00.000Z","key":1486526160000,"doc_count":11},{"key_as_string":"2017-02-08T03:57:00.000Z","key":1486526220000,"doc_count":7},{"key_as_string":"2017-02-08T03:58:00.000Z","key":1486526280000,"doc_count":11},{"key_as_string":"2017-02-08T03:59:00.000Z","key":1486526340000,"doc_count":14},{"key_as_string":"2017-02-08T04:00:00.000Z","key":1486526400000,"doc_count":10},{"key_as_string":"2017-02-08T04:01:00.000Z","key":1486526460000,"doc_count":11},{"key_as_string":"2017-02-08T04:02:00.000Z","key":1486526520000,"doc_count":12},{"key_as_string":"2017-02-08T04:03:00.000Z","key":1486526580000,"doc_count":8},{"key_as_string":"2017-02-08T04:04:00.000Z","key":1486526640000,"doc_count":11},{"key_as_string":"2017-02-08T04:05:00.000Z","key":1486526700000,"doc_count":15},{"key_as_string":"2017-02-08T04:06:00.000Z","key":1486526760000,"doc_count":8},{"key_as_string":"2017-02-08T04:07:00.000Z","key":1486526820000,"doc_count":13},{"key_as_string":"2017-02-08T04:08:00.000Z","key":1486526880000,"doc_count":12},{"key_as_string":"2017-02-08T04:09:00.000Z","key":1486526940000,"doc_count":11},{"key_as_string":"2017-02-08T04:10:00.000Z","key":1486527000000,"doc_count":12},{"key_as_string":"2017-02-08T04:11:00.000Z","key":1486527060000,"doc_count":10},{"key_as_string":"2017-02-08T04:12:00.000Z","key":1486527120000,"doc_count":13},{"key_as_string":"2017-02-08T04:13:00.000Z","key":1486527180000,"doc_count":5},{"key_as_string":"2017-02-08T04:14:00.000Z","key":1486527240000,"doc_count":6},{"key_as_string":"2017-02-08T04:15:00.000Z","key":1486527300000,"doc_count":16},{"key_as_string":"2017-02-08T04:16:00.000Z","key":1486527360000,"doc_count":12},{"key_as_string":"2017-02-08T04:17:00.000Z","key":1486527420000,"doc_count":17},{"key_as_string":"2017-02-08T04:18:00.000Z","key":1486527480000,"doc_count":11},{"key_as_string":"2017-02-08T04:19:00.000Z","key":1486527540000,"doc_count":16},{"key_as_string":"2017-02-08T04:20:00.000Z","key":1486527600000,"doc_count":8},{"key_as_string":"2017-02-08T04:21:00.000Z","key":1486527660000,"doc_count":10},{"key_as_string":"2017-02-08T04:22:00.000Z","key":1486527720000,"doc_count":14},{"key_as_string":"2017-02-08T04:23:00.000Z","key":1486527780000,"doc_count":10},{"key_as_string":"2017-02-08T04:24:00.000Z","key":1486527840000,"doc_count":9},{"key_as_string":"2017-02-08T04:25:00.000Z","key":1486527900000,"doc_count":13},{"key_as_string":"2017-02-08T04:26:00.000Z","key":1486527960000,"doc_count":8},{"key_as_string":"2017-02-08T04:27:00.000Z","key":1486528020000,"doc_count":13},{"key_as_string":"2017-02-08T04:28:00.000Z","key":1486528080000,"doc_count":16},{"key_as_string":"2017-02-08T04:29:00.000Z","key":1486528140000,"doc_count":8},{"key_as_string":"2017-02-08T04:30:00.000Z","key":1486528200000,"doc_count":13},{"key_as_string":"2017-02-08T04:31:00.000Z","key":1486528260000,"doc_count":12},{"key_as_string":"2017-02-08T04:32:00.000Z","key":1486528320000,"doc_count":9},{"key_as_string":"2017-02-08T04:33:00.000Z","key":1486528380000,"doc_count":14},{"key_as_string":"2017-02-08T04:34:00.000Z","key":1486528440000,"doc_count":8},{"key_as_string":"2017-02-08T04:35:00.000Z","key":1486528500000,"doc_count":9},{"key_as_string":"2017-02-08T04:36:00.000Z","key":1486528560000,"doc_count":19},{"key_as_string":"2017-02-08T04:37:00.000Z","key":1486528620000,"doc_count":8},{"key_as_string":"2017-02-08T04:38:00.000Z","key":1486528680000,"doc_count":12},{"key_as_string":"2017-02-08T04:39:00.000Z","key":1486528740000,"doc_count":13},{"key_as_string":"2017-02-08T04:40:00.000Z","key":1486528800000,"doc_count":13},{"key_as_string":"2017-02-08T04:41:00.000Z","key":1486528860000,"doc_count":9},{"key_as_string":"2017-02-08T04:42:00.000Z","key":1486528920000,"doc_count":8},{"key_as_string":"2017-02-08T04:43:00.000Z","key":1486528980000,"doc_count":8},{"key_as_string":"2017-02-08T04:44:00.000Z","key":1486529040000,"doc_count":11},{"key_as_string":"2017-02-08T04:45:00.000Z","key":1486529100000,"doc_count":11},{"key_as_string":"2017-02-08T04:46:00.000Z","key":1486529160000,"doc_count":14},{"key_as_string":"2017-02-08T04:47:00.000Z","key":1486529220000,"doc_count":9},{"key_as_string":"2017-02-08T04:48:00.000Z","key":1486529280000,"doc_count":11},{"key_as_string":"2017-02-08T04:49:00.000Z","key":1486529340000,"doc_count":16},{"key_as_string":"2017-02-08T04:50:00.000Z","key":1486529400000,"doc_count":6},{"key_as_string":"2017-02-08T04:51:00.000Z","key":1486529460000,"doc_count":14},{"key_as_string":"2017-02-08T04:52:00.000Z","key":1486529520000,"doc_count":12},{"key_as_string":"2017-02-08T04:53:00.000Z","key":1486529580000,"doc_count":13},{"key_as_string":"2017-02-08T04:54:00.000Z","key":1486529640000,"doc_count":12},{"key_as_string":"2017-02-08T04:55:00.000Z","key":1486529700000,"doc_count":12},{"key_as_string":"2017-02-08T04:56:00.000Z","key":1486529760000,"doc_count":15},{"key_as_string":"2017-02-08T04:57:00.000Z","key":1486529820000,"doc_count":12},{"key_as_string":"2017-02-08T04:58:00.000Z","key":1486529880000,"doc_count":11},{"key_as_string":"2017-02-08T04:59:00.000Z","key":1486529940000,"doc_count":7},{"key_as_string":"2017-02-08T05:00:00.000Z","key":1486530000000,"doc_count":10},{"key_as_string":"2017-02-08T05:01:00.000Z","key":1486530060000,"doc_count":15},{"key_as_string":"2017-02-08T05:02:00.000Z","key":1486530120000,"doc_count":15},{"key_as_string":"2017-02-08T05:03:00.000Z","key":1486530180000,"doc_count":11},{"key_as_string":"2017-02-08T05:04:00.000Z","key":1486530240000,"doc_count":11},{"key_as_string":"2017-02-08T05:05:00.000Z","key":1486530300000,"doc_count":14},{"key_as_string":"2017-02-08T05:06:00.000Z","key":1486530360000,"doc_count":9},{"key_as_string":"2017-02-08T05:07:00.000Z","key":1486530420000,"doc_count":15},{"key_as_string":"2017-02-08T05:08:00.000Z","key":1486530480000,"doc_count":8},{"key_as_string":"2017-02-08T05:09:00.000Z","key":1486530540000,"doc_count":13},{"key_as_string":"2017-02-08T05:10:00.000Z","key":1486530600000,"doc_count":12},{"key_as_string":"2017-02-08T05:11:00.000Z","key":1486530660000,"doc_count":7},{"key_as_string":"2017-02-08T05:12:00.000Z","key":1486530720000,"doc_count":9},{"key_as_string":"2017-02-08T05:13:00.000Z","key":1486530780000,"doc_count":12},{"key_as_string":"2017-02-08T05:14:00.000Z","key":1486530840000,"doc_count":14},{"key_as_string":"2017-02-08T05:15:00.000Z","key":1486530900000,"doc_count":15},{"key_as_string":"2017-02-08T05:16:00.000Z","key":1486530960000,"doc_count":7},{"key_as_string":"2017-02-08T05:17:00.000Z","key":1486531020000,"doc_count":10},{"key_as_string":"2017-02-08T05:18:00.000Z","key":1486531080000,"doc_count":10},{"key_as_string":"2017-02-08T05:19:00.000Z","key":1486531140000,"doc_count":12},{"key_as_string":"2017-02-08T05:20:00.000Z","key":1486531200000,"doc_count":7},{"key_as_string":"2017-02-08T05:21:00.000Z","key":1486531260000,"doc_count":9},{"key_as_string":"2017-02-08T05:22:00.000Z","key":1486531320000,"doc_count":13},{"key_as_string":"2017-02-08T05:23:00.000Z","key":1486531380000,"doc_count":9},{"key_as_string":"2017-02-08T05:24:00.000Z","key":1486531440000,"doc_count":10},{"key_as_string":"2017-02-08T05:25:00.000Z","key":1486531500000,"doc_count":12},{"key_as_string":"2017-02-08T05:26:00.000Z","key":1486531560000,"doc_count":7},{"key_as_string":"2017-02-08T05:27:00.000Z","key":1486531620000,"doc_count":16},{"key_as_string":"2017-02-08T05:28:00.000Z","key":1486531680000,"doc_count":12},{"key_as_string":"2017-02-08T05:29:00.000Z","key":1486531740000,"doc_count":13},{"key_as_string":"2017-02-08T05:30:00.000Z","key":1486531800000,"doc_count":10},{"key_as_string":"2017-02-08T05:31:00.000Z","key":1486531860000,"doc_count":12},{"key_as_string":"2017-02-08T05:32:00.000Z","key":1486531920000,"doc_count":6},{"key_as_string":"2017-02-08T05:33:00.000Z","key":1486531980000,"doc_count":16},{"key_as_string":"2017-02-08T05:34:00.000Z","key":1486532040000,"doc_count":11},{"key_as_string":"2017-02-08T05:35:00.000Z","key":1486532100000,"doc_count":13},{"key_as_string":"2017-02-08T05:36:00.000Z","key":1486532160000,"doc_count":12},{"key_as_string":"2017-02-08T05:37:00.000Z","key":1486532220000,"doc_count":14},{"key_as_string":"2017-02-08T05:38:00.000Z","key":1486532280000,"doc_count":9},{"key_as_string":"2017-02-08T05:39:00.000Z","key":1486532340000,"doc_count":9},{"key_as_string":"2017-02-08T05:40:00.000Z","key":1486532400000,"doc_count":11},{"key_as_string":"2017-02-08T05:41:00.000Z","key":1486532460000,"doc_count":17},{"key_as_string":"2017-02-08T05:42:00.000Z","key":1486532520000,"doc_count":13},{"key_as_string":"2017-02-08T05:43:00.000Z","key":1486532580000,"doc_count":10},{"key_as_string":"2017-02-08T05:44:00.000Z","key":1486532640000,"doc_count":12},{"key_as_string":"2017-02-08T05:45:00.000Z","key":1486532700000,"doc_count":17},{"key_as_string":"2017-02-08T05:46:00.000Z","key":1486532760000,"doc_count":7},{"key_as_string":"2017-02-08T05:47:00.000Z","key":1486532820000,"doc_count":11},{"key_as_string":"2017-02-08T05:48:00.000Z","key":1486532880000,"doc_count":10},{"key_as_string":"2017-02-08T05:49:00.000Z","key":1486532940000,"doc_count":7},{"key_as_string":"2017-02-08T05:50:00.000Z","key":1486533000000,"doc_count":12},{"key_as_string":"2017-02-08T05:51:00.000Z","key":1486533060000,"doc_count":12},{"key_as_string":"2017-02-08T05:52:00.000Z","key":1486533120000,"doc_count":6},{"key_as_string":"2017-02-08T05:53:00.000Z","key":1486533180000,"doc_count":11},{"key_as_string":"2017-02-08T05:54:00.000Z","key":1486533240000,"doc_count":14},{"key_as_string":"2017-02-08T05:55:00.000Z","key":1486533300000,"doc_count":10},{"key_as_string":"2017-02-08T05:56:00.000Z","key":1486533360000,"doc_count":11},{"key_as_string":"2017-02-08T05:57:00.000Z","key":1486533420000,"doc_count":14},{"key_as_string":"2017-02-08T05:58:00.000Z","key":1486533480000,"doc_count":5},{"key_as_string":"2017-02-08T05:59:00.000Z","key":1486533540000,"doc_count":16},{"key_as_string":"2017-02-08T06:00:00.000Z","key":1486533600000,"doc_count":9},{"key_as_string":"2017-02-08T06:01:00.000Z","key":1486533660000,"doc_count":12},{"key_as_string":"2017-02-08T06:02:00.000Z","key":1486533720000,"doc_count":10},{"key_as_string":"2017-02-08T06:03:00.000Z","key":1486533780000,"doc_count":13},{"key_as_string":"2017-02-08T06:04:00.000Z","key":1486533840000,"doc_count":14},{"key_as_string":"2017-02-08T06:05:00.000Z","key":1486533900000,"doc_count":11},{"key_as_string":"2017-02-08T06:06:00.000Z","key":1486533960000,"doc_count":9},{"key_as_string":"2017-02-08T06:07:00.000Z","key":1486534020000,"doc_count":10},{"key_as_string":"2017-02-08T06:08:00.000Z","key":1486534080000,"doc_count":7},{"key_as_string":"2017-02-08T06:09:00.000Z","key":1486534140000,"doc_count":14},{"key_as_string":"2017-02-08T06:10:00.000Z","key":1486534200000,"doc_count":13},{"key_as_string":"2017-02-08T06:11:00.000Z","key":1486534260000,"doc_count":15},{"key_as_string":"2017-02-08T06:12:00.000Z","key":1486534320000,"doc_count":9},{"key_as_string":"2017-02-08T06:13:00.000Z","key":1486534380000,"doc_count":8},{"key_as_string":"2017-02-08T06:14:00.000Z","key":1486534440000,"doc_count":7},{"key_as_string":"2017-02-08T06:15:00.000Z","key":1486534500000,"doc_count":15},{"key_as_string":"2017-02-08T06:16:00.000Z","key":1486534560000,"doc_count":11},{"key_as_string":"2017-02-08T06:17:00.000Z","key":1486534620000,"doc_count":15},{"key_as_string":"2017-02-08T06:18:00.000Z","key":1486534680000,"doc_count":14},{"key_as_string":"2017-02-08T06:19:00.000Z","key":1486534740000,"doc_count":12},{"key_as_string":"2017-02-08T06:20:00.000Z","key":1486534800000,"doc_count":15},{"key_as_string":"2017-02-08T06:21:00.000Z","key":1486534860000,"doc_count":13},{"key_as_string":"2017-02-08T06:22:00.000Z","key":1486534920000,"doc_count":10},{"key_as_string":"2017-02-08T06:23:00.000Z","key":1486534980000,"doc_count":14},{"key_as_string":"2017-02-08T06:24:00.000Z","key":1486535040000,"doc_count":15},{"key_as_string":"2017-02-08T06:25:00.000Z","key":1486535100000,"doc_count":14},{"key_as_string":"2017-02-08T06:26:00.000Z","key":1486535160000,"doc_count":11},{"key_as_string":"2017-02-08T06:27:00.000Z","key":1486535220000,"doc_count":13},{"key_as_string":"2017-02-08T06:28:00.000Z","key":1486535280000,"doc_count":11},{"key_as_string":"2017-02-08T06:29:00.000Z","key":1486535340000,"doc_count":16},{"key_as_string":"2017-02-08T06:30:00.000Z","key":1486535400000,"doc_count":12},{"key_as_string":"2017-02-08T06:31:00.000Z","key":1486535460000,"doc_count":11},{"key_as_string":"2017-02-08T06:32:00.000Z","key":1486535520000,"doc_count":10},{"key_as_string":"2017-02-08T06:33:00.000Z","key":1486535580000,"doc_count":8},{"key_as_string":"2017-02-08T06:34:00.000Z","key":1486535640000,"doc_count":14},{"key_as_string":"2017-02-08T06:35:00.000Z","key":1486535700000,"doc_count":9},{"key_as_string":"2017-02-08T06:36:00.000Z","key":1486535760000,"doc_count":16},{"key_as_string":"2017-02-08T06:37:00.000Z","key":1486535820000,"doc_count":13},{"key_as_string":"2017-02-08T06:38:00.000Z","key":1486535880000,"doc_count":10},{"key_as_string":"2017-02-08T06:39:00.000Z","key":1486535940000,"doc_count":13},{"key_as_string":"2017-02-08T06:40:00.000Z","key":1486536000000,"doc_count":13},{"key_as_string":"2017-02-08T06:41:00.000Z","key":1486536060000,"doc_count":10},{"key_as_string":"2017-02-08T06:42:00.000Z","key":1486536120000,"doc_count":12},{"key_as_string":"2017-02-08T06:43:00.000Z","key":1486536180000,"doc_count":10},{"key_as_string":"2017-02-08T06:44:00.000Z","key":1486536240000,"doc_count":15},{"key_as_string":"2017-02-08T06:45:00.000Z","key":1486536300000,"doc_count":10},{"key_as_string":"2017-02-08T06:46:00.000Z","key":1486536360000,"doc_count":8},{"key_as_string":"2017-02-08T06:47:00.000Z","key":1486536420000,"doc_count":15},{"key_as_string":"2017-02-08T06:48:00.000Z","key":1486536480000,"doc_count":14},{"key_as_string":"2017-02-08T06:49:00.000Z","key":1486536540000,"doc_count":8},{"key_as_string":"2017-02-08T06:50:00.000Z","key":1486536600000,"doc_count":10},{"key_as_string":"2017-02-08T06:51:00.000Z","key":1486536660000,"doc_count":12},{"key_as_string":"2017-02-08T06:52:00.000Z","key":1486536720000,"doc_count":19},{"key_as_string":"2017-02-08T06:53:00.000Z","key":1486536780000,"doc_count":10},{"key_as_string":"2017-02-08T06:54:00.000Z","key":1486536840000,"doc_count":12},{"key_as_string":"2017-02-08T06:55:00.000Z","key":1486536900000,"doc_count":13},{"key_as_string":"2017-02-08T06:56:00.000Z","key":1486536960000,"doc_count":12},{"key_as_string":"2017-02-08T06:57:00.000Z","key":1486537020000,"doc_count":10},{"key_as_string":"2017-02-08T06:58:00.000Z","key":1486537080000,"doc_count":13},{"key_as_string":"2017-02-08T06:59:00.000Z","key":1486537140000,"doc_count":9},{"key_as_string":"2017-02-08T07:00:00.000Z","key":1486537200000,"doc_count":17},{"key_as_string":"2017-02-08T07:01:00.000Z","key":1486537260000,"doc_count":14},{"key_as_string":"2017-02-08T07:02:00.000Z","key":1486537320000,"doc_count":11},{"key_as_string":"2017-02-08T07:03:00.000Z","key":1486537380000,"doc_count":13},{"key_as_string":"2017-02-08T07:04:00.000Z","key":1486537440000,"doc_count":15},{"key_as_string":"2017-02-08T07:05:00.000Z","key":1486537500000,"doc_count":16},{"key_as_string":"2017-02-08T07:06:00.000Z","key":1486537560000,"doc_count":11},{"key_as_string":"2017-02-08T07:07:00.000Z","key":1486537620000,"doc_count":7},{"key_as_string":"2017-02-08T07:08:00.000Z","key":1486537680000,"doc_count":12},{"key_as_string":"2017-02-08T07:09:00.000Z","key":1486537740000,"doc_count":14},{"key_as_string":"2017-02-08T07:10:00.000Z","key":1486537800000,"doc_count":9},{"key_as_string":"2017-02-08T07:11:00.000Z","key":1486537860000,"doc_count":11},{"key_as_string":"2017-02-08T07:12:00.000Z","key":1486537920000,"doc_count":12},{"key_as_string":"2017-02-08T07:13:00.000Z","key":1486537980000,"doc_count":7},{"key_as_string":"2017-02-08T07:14:00.000Z","key":1486538040000,"doc_count":15},{"key_as_string":"2017-02-08T07:15:00.000Z","key":1486538100000,"doc_count":13},{"key_as_string":"2017-02-08T07:16:00.000Z","key":1486538160000,"doc_count":10},{"key_as_string":"2017-02-08T07:17:00.000Z","key":1486538220000,"doc_count":8},{"key_as_string":"2017-02-08T07:18:00.000Z","key":1486538280000,"doc_count":16},{"key_as_string":"2017-02-08T07:19:00.000Z","key":1486538340000,"doc_count":12},{"key_as_string":"2017-02-08T07:20:00.000Z","key":1486538400000,"doc_count":13},{"key_as_string":"2017-02-08T07:21:00.000Z","key":1486538460000,"doc_count":15},{"key_as_string":"2017-02-08T07:22:00.000Z","key":1486538520000,"doc_count":12},{"key_as_string":"2017-02-08T07:23:00.000Z","key":1486538580000,"doc_count":17},{"key_as_string":"2017-02-08T07:24:00.000Z","key":1486538640000,"doc_count":10},{"key_as_string":"2017-02-08T07:25:00.000Z","key":1486538700000,"doc_count":11},{"key_as_string":"2017-02-08T07:26:00.000Z","key":1486538760000,"doc_count":11},{"key_as_string":"2017-02-08T07:27:00.000Z","key":1486538820000,"doc_count":13},{"key_as_string":"2017-02-08T07:28:00.000Z","key":1486538880000,"doc_count":10},{"key_as_string":"2017-02-08T07:29:00.000Z","key":1486538940000,"doc_count":13},{"key_as_string":"2017-02-08T07:30:00.000Z","key":1486539000000,"doc_count":11},{"key_as_string":"2017-02-08T07:31:00.000Z","key":1486539060000,"doc_count":17},{"key_as_string":"2017-02-08T07:32:00.000Z","key":1486539120000,"doc_count":12},{"key_as_string":"2017-02-08T07:33:00.000Z","key":1486539180000,"doc_count":11},{"key_as_string":"2017-02-08T07:34:00.000Z","key":1486539240000,"doc_count":11},{"key_as_string":"2017-02-08T07:35:00.000Z","key":1486539300000,"doc_count":17},{"key_as_string":"2017-02-08T07:36:00.000Z","key":1486539360000,"doc_count":12},{"key_as_string":"2017-02-08T07:37:00.000Z","key":1486539420000,"doc_count":15},{"key_as_string":"2017-02-08T07:38:00.000Z","key":1486539480000,"doc_count":12},{"key_as_string":"2017-02-08T07:39:00.000Z","key":1486539540000,"doc_count":19},{"key_as_string":"2017-02-08T07:40:00.000Z","key":1486539600000,"doc_count":10},{"key_as_string":"2017-02-08T07:41:00.000Z","key":1486539660000,"doc_count":10},{"key_as_string":"2017-02-08T07:42:00.000Z","key":1486539720000,"doc_count":13},{"key_as_string":"2017-02-08T07:43:00.000Z","key":1486539780000,"doc_count":17},{"key_as_string":"2017-02-08T07:44:00.000Z","key":1486539840000,"doc_count":15},{"key_as_string":"2017-02-08T07:45:00.000Z","key":1486539900000,"doc_count":8},{"key_as_string":"2017-02-08T07:46:00.000Z","key":1486539960000,"doc_count":13},{"key_as_string":"2017-02-08T07:47:00.000Z","key":1486540020000,"doc_count":9},{"key_as_string":"2017-02-08T07:48:00.000Z","key":1486540080000,"doc_count":10},{"key_as_string":"2017-02-08T07:49:00.000Z","key":1486540140000,"doc_count":10},{"key_as_string":"2017-02-08T07:50:00.000Z","key":1486540200000,"doc_count":15},{"key_as_string":"2017-02-08T07:51:00.000Z","key":1486540260000,"doc_count":13},{"key_as_string":"2017-02-08T07:52:00.000Z","key":1486540320000,"doc_count":14},{"key_as_string":"2017-02-08T07:53:00.000Z","key":1486540380000,"doc_count":13},{"key_as_string":"2017-02-08T07:54:00.000Z","key":1486540440000,"doc_count":14},{"key_as_string":"2017-02-08T07:55:00.000Z","key":1486540500000,"doc_count":11},{"key_as_string":"2017-02-08T07:56:00.000Z","key":1486540560000,"doc_count":10},{"key_as_string":"2017-02-08T07:57:00.000Z","key":1486540620000,"doc_count":9},{"key_as_string":"2017-02-08T07:58:00.000Z","key":1486540680000,"doc_count":16},{"key_as_string":"2017-02-08T07:59:00.000Z","key":1486540740000,"doc_count":17},{"key_as_string":"2017-02-08T08:00:00.000Z","key":1486540800000,"doc_count":14},{"key_as_string":"2017-02-08T08:01:00.000Z","key":1486540860000,"doc_count":12},{"key_as_string":"2017-02-08T08:02:00.000Z","key":1486540920000,"doc_count":15},{"key_as_string":"2017-02-08T08:03:00.000Z","key":1486540980000,"doc_count":15},{"key_as_string":"2017-02-08T08:04:00.000Z","key":1486541040000,"doc_count":10},{"key_as_string":"2017-02-08T08:05:00.000Z","key":1486541100000,"doc_count":12},{"key_as_string":"2017-02-08T08:06:00.000Z","key":1486541160000,"doc_count":6},{"key_as_string":"2017-02-08T08:07:00.000Z","key":1486541220000,"doc_count":17},{"key_as_string":"2017-02-08T08:08:00.000Z","key":1486541280000,"doc_count":15},{"key_as_string":"2017-02-08T08:09:00.000Z","key":1486541340000,"doc_count":14},{"key_as_string":"2017-02-08T08:10:00.000Z","key":1486541400000,"doc_count":12},{"key_as_string":"2017-02-08T08:11:00.000Z","key":1486541460000,"doc_count":18},{"key_as_string":"2017-02-08T08:12:00.000Z","key":1486541520000,"doc_count":9},{"key_as_string":"2017-02-08T08:13:00.000Z","key":1486541580000,"doc_count":12},{"key_as_string":"2017-02-08T08:14:00.000Z","key":1486541640000,"doc_count":11},{"key_as_string":"2017-02-08T08:15:00.000Z","key":1486541700000,"doc_count":19},{"key_as_string":"2017-02-08T08:16:00.000Z","key":1486541760000,"doc_count":12},{"key_as_string":"2017-02-08T08:17:00.000Z","key":1486541820000,"doc_count":10},{"key_as_string":"2017-02-08T08:18:00.000Z","key":1486541880000,"doc_count":16},{"key_as_string":"2017-02-08T08:19:00.000Z","key":1486541940000,"doc_count":12},{"key_as_string":"2017-02-08T08:20:00.000Z","key":1486542000000,"doc_count":15},{"key_as_string":"2017-02-08T08:21:00.000Z","key":1486542060000,"doc_count":14},{"key_as_string":"2017-02-08T08:22:00.000Z","key":1486542120000,"doc_count":15},{"key_as_string":"2017-02-08T08:23:00.000Z","key":1486542180000,"doc_count":12},{"key_as_string":"2017-02-08T08:24:00.000Z","key":1486542240000,"doc_count":15},{"key_as_string":"2017-02-08T08:25:00.000Z","key":1486542300000,"doc_count":7},{"key_as_string":"2017-02-08T08:26:00.000Z","key":1486542360000,"doc_count":10},{"key_as_string":"2017-02-08T08:27:00.000Z","key":1486542420000,"doc_count":12},{"key_as_string":"2017-02-08T08:28:00.000Z","key":1486542480000,"doc_count":10},{"key_as_string":"2017-02-08T08:29:00.000Z","key":1486542540000,"doc_count":12},{"key_as_string":"2017-02-08T08:30:00.000Z","key":1486542600000,"doc_count":17},{"key_as_string":"2017-02-08T08:31:00.000Z","key":1486542660000,"doc_count":13},{"key_as_string":"2017-02-08T08:32:00.000Z","key":1486542720000,"doc_count":14},{"key_as_string":"2017-02-08T08:33:00.000Z","key":1486542780000,"doc_count":10},{"key_as_string":"2017-02-08T08:34:00.000Z","key":1486542840000,"doc_count":12},{"key_as_string":"2017-02-08T08:35:00.000Z","key":1486542900000,"doc_count":14},{"key_as_string":"2017-02-08T08:36:00.000Z","key":1486542960000,"doc_count":11},{"key_as_string":"2017-02-08T08:37:00.000Z","key":1486543020000,"doc_count":16},{"key_as_string":"2017-02-08T08:38:00.000Z","key":1486543080000,"doc_count":13},{"key_as_string":"2017-02-08T08:39:00.000Z","key":1486543140000,"doc_count":11},{"key_as_string":"2017-02-08T08:40:00.000Z","key":1486543200000,"doc_count":9},{"key_as_string":"2017-02-08T08:41:00.000Z","key":1486543260000,"doc_count":15},{"key_as_string":"2017-02-08T08:42:00.000Z","key":1486543320000,"doc_count":12},{"key_as_string":"2017-02-08T08:43:00.000Z","key":1486543380000,"doc_count":9},{"key_as_string":"2017-02-08T08:44:00.000Z","key":1486543440000,"doc_count":15},{"key_as_string":"2017-02-08T08:45:00.000Z","key":1486543500000,"doc_count":17},{"key_as_string":"2017-02-08T08:46:00.000Z","key":1486543560000,"doc_count":5},{"key_as_string":"2017-02-08T08:47:00.000Z","key":1486543620000,"doc_count":19},{"key_as_string":"2017-02-08T08:48:00.000Z","key":1486543680000,"doc_count":14},{"key_as_string":"2017-02-08T08:49:00.000Z","key":1486543740000,"doc_count":13},{"key_as_string":"2017-02-08T08:50:00.000Z","key":1486543800000,"doc_count":16},{"key_as_string":"2017-02-08T08:51:00.000Z","key":1486543860000,"doc_count":14},{"key_as_string":"2017-02-08T08:52:00.000Z","key":1486543920000,"doc_count":17},{"key_as_string":"2017-02-08T08:53:00.000Z","key":1486543980000,"doc_count":12},{"key_as_string":"2017-02-08T08:54:00.000Z","key":1486544040000,"doc_count":13},{"key_as_string":"2017-02-08T08:55:00.000Z","key":1486544100000,"doc_count":17},{"key_as_string":"2017-02-08T08:56:00.000Z","key":1486544160000,"doc_count":11},{"key_as_string":"2017-02-08T08:57:00.000Z","key":1486544220000,"doc_count":10},{"key_as_string":"2017-02-08T08:58:00.000Z","key":1486544280000,"doc_count":13},{"key_as_string":"2017-02-08T08:59:00.000Z","key":1486544340000,"doc_count":11},{"key_as_string":"2017-02-08T09:00:00.000Z","key":1486544400000,"doc_count":12},{"key_as_string":"2017-02-08T09:01:00.000Z","key":1486544460000,"doc_count":15},{"key_as_string":"2017-02-08T09:02:00.000Z","key":1486544520000,"doc_count":8},{"key_as_string":"2017-02-08T09:03:00.000Z","key":1486544580000,"doc_count":14},{"key_as_string":"2017-02-08T09:04:00.000Z","key":1486544640000,"doc_count":13},{"key_as_string":"2017-02-08T09:05:00.000Z","key":1486544700000,"doc_count":12},{"key_as_string":"2017-02-08T09:06:00.000Z","key":1486544760000,"doc_count":15},{"key_as_string":"2017-02-08T09:07:00.000Z","key":1486544820000,"doc_count":13},{"key_as_string":"2017-02-08T09:08:00.000Z","key":1486544880000,"doc_count":19},{"key_as_string":"2017-02-08T09:09:00.000Z","key":1486544940000,"doc_count":14},{"key_as_string":"2017-02-08T09:10:00.000Z","key":1486545000000,"doc_count":11},{"key_as_string":"2017-02-08T09:11:00.000Z","key":1486545060000,"doc_count":15},{"key_as_string":"2017-02-08T09:12:00.000Z","key":1486545120000,"doc_count":15},{"key_as_string":"2017-02-08T09:13:00.000Z","key":1486545180000,"doc_count":10},{"key_as_string":"2017-02-08T09:14:00.000Z","key":1486545240000,"doc_count":17},{"key_as_string":"2017-02-08T09:15:00.000Z","key":1486545300000,"doc_count":18},{"key_as_string":"2017-02-08T09:16:00.000Z","key":1486545360000,"doc_count":15},{"key_as_string":"2017-02-08T09:17:00.000Z","key":1486545420000,"doc_count":13},{"key_as_string":"2017-02-08T09:18:00.000Z","key":1486545480000,"doc_count":9},{"key_as_string":"2017-02-08T09:19:00.000Z","key":1486545540000,"doc_count":15},{"key_as_string":"2017-02-08T09:20:00.000Z","key":1486545600000,"doc_count":10},{"key_as_string":"2017-02-08T09:21:00.000Z","key":1486545660000,"doc_count":9},{"key_as_string":"2017-02-08T09:22:00.000Z","key":1486545720000,"doc_count":13},{"key_as_string":"2017-02-08T09:23:00.000Z","key":1486545780000,"doc_count":18},{"key_as_string":"2017-02-08T09:24:00.000Z","key":1486545840000,"doc_count":12},{"key_as_string":"2017-02-08T09:25:00.000Z","key":1486545900000,"doc_count":15},{"key_as_string":"2017-02-08T09:26:00.000Z","key":1486545960000,"doc_count":10},{"key_as_string":"2017-02-08T09:27:00.000Z","key":1486546020000,"doc_count":16},{"key_as_string":"2017-02-08T09:28:00.000Z","key":1486546080000,"doc_count":17},{"key_as_string":"2017-02-08T09:29:00.000Z","key":1486546140000,"doc_count":14},{"key_as_string":"2017-02-08T09:30:00.000Z","key":1486546200000,"doc_count":13},{"key_as_string":"2017-02-08T09:31:00.000Z","key":1486546260000,"doc_count":14},{"key_as_string":"2017-02-08T09:32:00.000Z","key":1486546320000,"doc_count":11},{"key_as_string":"2017-02-08T09:33:00.000Z","key":1486546380000,"doc_count":13},{"key_as_string":"2017-02-08T09:34:00.000Z","key":1486546440000,"doc_count":10},{"key_as_string":"2017-02-08T09:35:00.000Z","key":1486546500000,"doc_count":15},{"key_as_string":"2017-02-08T09:36:00.000Z","key":1486546560000,"doc_count":15},{"key_as_string":"2017-02-08T09:37:00.000Z","key":1486546620000,"doc_count":11},{"key_as_string":"2017-02-08T09:38:00.000Z","key":1486546680000,"doc_count":13},{"key_as_string":"2017-02-08T09:39:00.000Z","key":1486546740000,"doc_count":12},{"key_as_string":"2017-02-08T09:40:00.000Z","key":1486546800000,"doc_count":12},{"key_as_string":"2017-02-08T09:41:00.000Z","key":1486546860000,"doc_count":11},{"key_as_string":"2017-02-08T09:42:00.000Z","key":1486546920000,"doc_count":10},{"key_as_string":"2017-02-08T09:43:00.000Z","key":1486546980000,"doc_count":15},{"key_as_string":"2017-02-08T09:44:00.000Z","key":1486547040000,"doc_count":12},{"key_as_string":"2017-02-08T09:45:00.000Z","key":1486547100000,"doc_count":11},{"key_as_string":"2017-02-08T09:46:00.000Z","key":1486547160000,"doc_count":12},{"key_as_string":"2017-02-08T09:47:00.000Z","key":1486547220000,"doc_count":12},{"key_as_string":"2017-02-08T09:48:00.000Z","key":1486547280000,"doc_count":10},{"key_as_string":"2017-02-08T09:49:00.000Z","key":1486547340000,"doc_count":18},{"key_as_string":"2017-02-08T09:50:00.000Z","key":1486547400000,"doc_count":16},{"key_as_string":"2017-02-08T09:51:00.000Z","key":1486547460000,"doc_count":20},{"key_as_string":"2017-02-08T09:52:00.000Z","key":1486547520000,"doc_count":12},{"key_as_string":"2017-02-08T09:53:00.000Z","key":1486547580000,"doc_count":13},{"key_as_string":"2017-02-08T09:54:00.000Z","key":1486547640000,"doc_count":16},{"key_as_string":"2017-02-08T09:55:00.000Z","key":1486547700000,"doc_count":14},{"key_as_string":"2017-02-08T09:56:00.000Z","key":1486547760000,"doc_count":12},{"key_as_string":"2017-02-08T09:57:00.000Z","key":1486547820000,"doc_count":18},{"key_as_string":"2017-02-08T09:58:00.000Z","key":1486547880000,"doc_count":11},{"key_as_string":"2017-02-08T09:59:00.000Z","key":1486547940000,"doc_count":17},{"key_as_string":"2017-02-08T10:00:00.000Z","key":1486548000000,"doc_count":12},{"key_as_string":"2017-02-08T10:01:00.000Z","key":1486548060000,"doc_count":13},{"key_as_string":"2017-02-08T10:02:00.000Z","key":1486548120000,"doc_count":21},{"key_as_string":"2017-02-08T10:03:00.000Z","key":1486548180000,"doc_count":17},{"key_as_string":"2017-02-08T10:04:00.000Z","key":1486548240000,"doc_count":16},{"key_as_string":"2017-02-08T10:05:00.000Z","key":1486548300000,"doc_count":11},{"key_as_string":"2017-02-08T10:06:00.000Z","key":1486548360000,"doc_count":16},{"key_as_string":"2017-02-08T10:07:00.000Z","key":1486548420000,"doc_count":11},{"key_as_string":"2017-02-08T10:08:00.000Z","key":1486548480000,"doc_count":15},{"key_as_string":"2017-02-08T10:09:00.000Z","key":1486548540000,"doc_count":12},{"key_as_string":"2017-02-08T10:10:00.000Z","key":1486548600000,"doc_count":14},{"key_as_string":"2017-02-08T10:11:00.000Z","key":1486548660000,"doc_count":11},{"key_as_string":"2017-02-08T10:12:00.000Z","key":1486548720000,"doc_count":19},{"key_as_string":"2017-02-08T10:13:00.000Z","key":1486548780000,"doc_count":13},{"key_as_string":"2017-02-08T10:14:00.000Z","key":1486548840000,"doc_count":16},{"key_as_string":"2017-02-08T10:15:00.000Z","key":1486548900000,"doc_count":10},{"key_as_string":"2017-02-08T10:16:00.000Z","key":1486548960000,"doc_count":13},{"key_as_string":"2017-02-08T10:17:00.000Z","key":1486549020000,"doc_count":8},{"key_as_string":"2017-02-08T10:18:00.000Z","key":1486549080000,"doc_count":15},{"key_as_string":"2017-02-08T10:19:00.000Z","key":1486549140000,"doc_count":14},{"key_as_string":"2017-02-08T10:20:00.000Z","key":1486549200000,"doc_count":14},{"key_as_string":"2017-02-08T10:21:00.000Z","key":1486549260000,"doc_count":10},{"key_as_string":"2017-02-08T10:22:00.000Z","key":1486549320000,"doc_count":15},{"key_as_string":"2017-02-08T10:23:00.000Z","key":1486549380000,"doc_count":10},{"key_as_string":"2017-02-08T10:24:00.000Z","key":1486549440000,"doc_count":13},{"key_as_string":"2017-02-08T10:25:00.000Z","key":1486549500000,"doc_count":14},{"key_as_string":"2017-02-08T10:26:00.000Z","key":1486549560000,"doc_count":14},{"key_as_string":"2017-02-08T10:27:00.000Z","key":1486549620000,"doc_count":17},{"key_as_string":"2017-02-08T10:28:00.000Z","key":1486549680000,"doc_count":10},{"key_as_string":"2017-02-08T10:29:00.000Z","key":1486549740000,"doc_count":13},{"key_as_string":"2017-02-08T10:30:00.000Z","key":1486549800000,"doc_count":16},{"key_as_string":"2017-02-08T10:31:00.000Z","key":1486549860000,"doc_count":14},{"key_as_string":"2017-02-08T10:32:00.000Z","key":1486549920000,"doc_count":16},{"key_as_string":"2017-02-08T10:33:00.000Z","key":1486549980000,"doc_count":15},{"key_as_string":"2017-02-08T10:34:00.000Z","key":1486550040000,"doc_count":15},{"key_as_string":"2017-02-08T10:35:00.000Z","key":1486550100000,"doc_count":15},{"key_as_string":"2017-02-08T10:36:00.000Z","key":1486550160000,"doc_count":10},{"key_as_string":"2017-02-08T10:37:00.000Z","key":1486550220000,"doc_count":15},{"key_as_string":"2017-02-08T10:38:00.000Z","key":1486550280000,"doc_count":14},{"key_as_string":"2017-02-08T10:39:00.000Z","key":1486550340000,"doc_count":17},{"key_as_string":"2017-02-08T10:40:00.000Z","key":1486550400000,"doc_count":15},{"key_as_string":"2017-02-08T10:41:00.000Z","key":1486550460000,"doc_count":19},{"key_as_string":"2017-02-08T10:42:00.000Z","key":1486550520000,"doc_count":9},{"key_as_string":"2017-02-08T10:43:00.000Z","key":1486550580000,"doc_count":14},{"key_as_string":"2017-02-08T10:44:00.000Z","key":1486550640000,"doc_count":10},{"key_as_string":"2017-02-08T10:45:00.000Z","key":1486550700000,"doc_count":20},{"key_as_string":"2017-02-08T10:46:00.000Z","key":1486550760000,"doc_count":10},{"key_as_string":"2017-02-08T10:47:00.000Z","key":1486550820000,"doc_count":16},{"key_as_string":"2017-02-08T10:48:00.000Z","key":1486550880000,"doc_count":13},{"key_as_string":"2017-02-08T10:49:00.000Z","key":1486550940000,"doc_count":14},{"key_as_string":"2017-02-08T10:50:00.000Z","key":1486551000000,"doc_count":10},{"key_as_string":"2017-02-08T10:51:00.000Z","key":1486551060000,"doc_count":17},{"key_as_string":"2017-02-08T10:52:00.000Z","key":1486551120000,"doc_count":16},{"key_as_string":"2017-02-08T10:53:00.000Z","key":1486551180000,"doc_count":9},{"key_as_string":"2017-02-08T10:54:00.000Z","key":1486551240000,"doc_count":15},{"key_as_string":"2017-02-08T10:55:00.000Z","key":1486551300000,"doc_count":16},{"key_as_string":"2017-02-08T10:56:00.000Z","key":1486551360000,"doc_count":5},{"key_as_string":"2017-02-08T10:57:00.000Z","key":1486551420000,"doc_count":15},{"key_as_string":"2017-02-08T10:58:00.000Z","key":1486551480000,"doc_count":9},{"key_as_string":"2017-02-08T10:59:00.000Z","key":1486551540000,"doc_count":14},{"key_as_string":"2017-02-08T11:00:00.000Z","key":1486551600000,"doc_count":16},{"key_as_string":"2017-02-08T11:01:00.000Z","key":1486551660000,"doc_count":9},{"key_as_string":"2017-02-08T11:02:00.000Z","key":1486551720000,"doc_count":17},{"key_as_string":"2017-02-08T11:03:00.000Z","key":1486551780000,"doc_count":9},{"key_as_string":"2017-02-08T11:04:00.000Z","key":1486551840000,"doc_count":18},{"key_as_string":"2017-02-08T11:05:00.000Z","key":1486551900000,"doc_count":12},{"key_as_string":"2017-02-08T11:06:00.000Z","key":1486551960000,"doc_count":16},{"key_as_string":"2017-02-08T11:07:00.000Z","key":1486552020000,"doc_count":13},{"key_as_string":"2017-02-08T11:08:00.000Z","key":1486552080000,"doc_count":14},{"key_as_string":"2017-02-08T11:09:00.000Z","key":1486552140000,"doc_count":13},{"key_as_string":"2017-02-08T11:10:00.000Z","key":1486552200000,"doc_count":17},{"key_as_string":"2017-02-08T11:11:00.000Z","key":1486552260000,"doc_count":12},{"key_as_string":"2017-02-08T11:12:00.000Z","key":1486552320000,"doc_count":11},{"key_as_string":"2017-02-08T11:13:00.000Z","key":1486552380000,"doc_count":15},{"key_as_string":"2017-02-08T11:14:00.000Z","key":1486552440000,"doc_count":10},{"key_as_string":"2017-02-08T11:15:00.000Z","key":1486552500000,"doc_count":9},{"key_as_string":"2017-02-08T11:16:00.000Z","key":1486552560000,"doc_count":14},{"key_as_string":"2017-02-08T11:17:00.000Z","key":1486552620000,"doc_count":16},{"key_as_string":"2017-02-08T11:18:00.000Z","key":1486552680000,"doc_count":19},{"key_as_string":"2017-02-08T11:19:00.000Z","key":1486552740000,"doc_count":14},{"key_as_string":"2017-02-08T11:20:00.000Z","key":1486552800000,"doc_count":10},{"key_as_string":"2017-02-08T11:21:00.000Z","key":1486552860000,"doc_count":14},{"key_as_string":"2017-02-08T11:22:00.000Z","key":1486552920000,"doc_count":13},{"key_as_string":"2017-02-08T11:23:00.000Z","key":1486552980000,"doc_count":16},{"key_as_string":"2017-02-08T11:24:00.000Z","key":1486553040000,"doc_count":15},{"key_as_string":"2017-02-08T11:25:00.000Z","key":1486553100000,"doc_count":16},{"key_as_string":"2017-02-08T11:26:00.000Z","key":1486553160000,"doc_count":18},{"key_as_string":"2017-02-08T11:27:00.000Z","key":1486553220000,"doc_count":13},{"key_as_string":"2017-02-08T11:28:00.000Z","key":1486553280000,"doc_count":13},{"key_as_string":"2017-02-08T11:29:00.000Z","key":1486553340000,"doc_count":16},{"key_as_string":"2017-02-08T11:30:00.000Z","key":1486553400000,"doc_count":13},{"key_as_string":"2017-02-08T11:31:00.000Z","key":1486553460000,"doc_count":16},{"key_as_string":"2017-02-08T11:32:00.000Z","key":1486553520000,"doc_count":13},{"key_as_string":"2017-02-08T11:33:00.000Z","key":1486553580000,"doc_count":18},{"key_as_string":"2017-02-08T11:34:00.000Z","key":1486553640000,"doc_count":12},{"key_as_string":"2017-02-08T11:35:00.000Z","key":1486553700000,"doc_count":11},{"key_as_string":"2017-02-08T11:36:00.000Z","key":1486553760000,"doc_count":16},{"key_as_string":"2017-02-08T11:37:00.000Z","key":1486553820000,"doc_count":18},{"key_as_string":"2017-02-08T11:38:00.000Z","key":1486553880000,"doc_count":10},{"key_as_string":"2017-02-08T11:39:00.000Z","key":1486553940000,"doc_count":17},{"key_as_string":"2017-02-08T11:40:00.000Z","key":1486554000000,"doc_count":11},{"key_as_string":"2017-02-08T11:41:00.000Z","key":1486554060000,"doc_count":20},{"key_as_string":"2017-02-08T11:42:00.000Z","key":1486554120000,"doc_count":10},{"key_as_string":"2017-02-08T11:43:00.000Z","key":1486554180000,"doc_count":17},{"key_as_string":"2017-02-08T11:44:00.000Z","key":1486554240000,"doc_count":21},{"key_as_string":"2017-02-08T11:45:00.000Z","key":1486554300000,"doc_count":13},{"key_as_string":"2017-02-08T11:46:00.000Z","key":1486554360000,"doc_count":12},{"key_as_string":"2017-02-08T11:47:00.000Z","key":1486554420000,"doc_count":11},{"key_as_string":"2017-02-08T11:48:00.000Z","key":1486554480000,"doc_count":14},{"key_as_string":"2017-02-08T11:49:00.000Z","key":1486554540000,"doc_count":11},{"key_as_string":"2017-02-08T11:50:00.000Z","key":1486554600000,"doc_count":9},{"key_as_string":"2017-02-08T11:51:00.000Z","key":1486554660000,"doc_count":14},{"key_as_string":"2017-02-08T11:52:00.000Z","key":1486554720000,"doc_count":13},{"key_as_string":"2017-02-08T11:53:00.000Z","key":1486554780000,"doc_count":12},{"key_as_string":"2017-02-08T11:54:00.000Z","key":1486554840000,"doc_count":13},{"key_as_string":"2017-02-08T11:55:00.000Z","key":1486554900000,"doc_count":18},{"key_as_string":"2017-02-08T11:56:00.000Z","key":1486554960000,"doc_count":18},{"key_as_string":"2017-02-08T11:57:00.000Z","key":1486555020000,"doc_count":13},{"key_as_string":"2017-02-08T11:58:00.000Z","key":1486555080000,"doc_count":13},{"key_as_string":"2017-02-08T11:59:00.000Z","key":1486555140000,"doc_count":14},{"key_as_string":"2017-02-08T12:00:00.000Z","key":1486555200000,"doc_count":18},{"key_as_string":"2017-02-08T12:01:00.000Z","key":1486555260000,"doc_count":14},{"key_as_string":"2017-02-08T12:02:00.000Z","key":1486555320000,"doc_count":14},{"key_as_string":"2017-02-08T12:03:00.000Z","key":1486555380000,"doc_count":13},{"key_as_string":"2017-02-08T12:04:00.000Z","key":1486555440000,"doc_count":18},{"key_as_string":"2017-02-08T12:05:00.000Z","key":1486555500000,"doc_count":13},{"key_as_string":"2017-02-08T12:06:00.000Z","key":1486555560000,"doc_count":16},{"key_as_string":"2017-02-08T12:07:00.000Z","key":1486555620000,"doc_count":13},{"key_as_string":"2017-02-08T12:08:00.000Z","key":1486555680000,"doc_count":14},{"key_as_string":"2017-02-08T12:09:00.000Z","key":1486555740000,"doc_count":16},{"key_as_string":"2017-02-08T12:10:00.000Z","key":1486555800000,"doc_count":13},{"key_as_string":"2017-02-08T12:11:00.000Z","key":1486555860000,"doc_count":13},{"key_as_string":"2017-02-08T12:12:00.000Z","key":1486555920000,"doc_count":15},{"key_as_string":"2017-02-08T12:13:00.000Z","key":1486555980000,"doc_count":11},{"key_as_string":"2017-02-08T12:14:00.000Z","key":1486556040000,"doc_count":15},{"key_as_string":"2017-02-08T12:15:00.000Z","key":1486556100000,"doc_count":12},{"key_as_string":"2017-02-08T12:16:00.000Z","key":1486556160000,"doc_count":14},{"key_as_string":"2017-02-08T12:17:00.000Z","key":1486556220000,"doc_count":13},{"key_as_string":"2017-02-08T12:18:00.000Z","key":1486556280000,"doc_count":19},{"key_as_string":"2017-02-08T12:19:00.000Z","key":1486556340000,"doc_count":13},{"key_as_string":"2017-02-08T12:20:00.000Z","key":1486556400000,"doc_count":14},{"key_as_string":"2017-02-08T12:21:00.000Z","key":1486556460000,"doc_count":12},{"key_as_string":"2017-02-08T12:22:00.000Z","key":1486556520000,"doc_count":15},{"key_as_string":"2017-02-08T12:23:00.000Z","key":1486556580000,"doc_count":17},{"key_as_string":"2017-02-08T12:24:00.000Z","key":1486556640000,"doc_count":14},{"key_as_string":"2017-02-08T12:25:00.000Z","key":1486556700000,"doc_count":10},{"key_as_string":"2017-02-08T12:26:00.000Z","key":1486556760000,"doc_count":13},{"key_as_string":"2017-02-08T12:27:00.000Z","key":1486556820000,"doc_count":14},{"key_as_string":"2017-02-08T12:28:00.000Z","key":1486556880000,"doc_count":10},{"key_as_string":"2017-02-08T12:29:00.000Z","key":1486556940000,"doc_count":13},{"key_as_string":"2017-02-08T12:30:00.000Z","key":1486557000000,"doc_count":16},{"key_as_string":"2017-02-08T12:31:00.000Z","key":1486557060000,"doc_count":15},{"key_as_string":"2017-02-08T12:32:00.000Z","key":1486557120000,"doc_count":9},{"key_as_string":"2017-02-08T12:33:00.000Z","key":1486557180000,"doc_count":16},{"key_as_string":"2017-02-08T12:34:00.000Z","key":1486557240000,"doc_count":16},{"key_as_string":"2017-02-08T12:35:00.000Z","key":1486557300000,"doc_count":9},{"key_as_string":"2017-02-08T12:36:00.000Z","key":1486557360000,"doc_count":11},{"key_as_string":"2017-02-08T12:37:00.000Z","key":1486557420000,"doc_count":12},{"key_as_string":"2017-02-08T12:38:00.000Z","key":1486557480000,"doc_count":16},{"key_as_string":"2017-02-08T12:39:00.000Z","key":1486557540000,"doc_count":13},{"key_as_string":"2017-02-08T12:40:00.000Z","key":1486557600000,"doc_count":12},{"key_as_string":"2017-02-08T12:41:00.000Z","key":1486557660000,"doc_count":15},{"key_as_string":"2017-02-08T12:42:00.000Z","key":1486557720000,"doc_count":12},{"key_as_string":"2017-02-08T12:43:00.000Z","key":1486557780000,"doc_count":13},{"key_as_string":"2017-02-08T12:44:00.000Z","key":1486557840000,"doc_count":13},{"key_as_string":"2017-02-08T12:45:00.000Z","key":1486557900000,"doc_count":15},{"key_as_string":"2017-02-08T12:46:00.000Z","key":1486557960000,"doc_count":15},{"key_as_string":"2017-02-08T12:47:00.000Z","key":1486558020000,"doc_count":12},{"key_as_string":"2017-02-08T12:48:00.000Z","key":1486558080000,"doc_count":13},{"key_as_string":"2017-02-08T12:49:00.000Z","key":1486558140000,"doc_count":15},{"key_as_string":"2017-02-08T12:50:00.000Z","key":1486558200000,"doc_count":16},{"key_as_string":"2017-02-08T12:51:00.000Z","key":1486558260000,"doc_count":14},{"key_as_string":"2017-02-08T12:52:00.000Z","key":1486558320000,"doc_count":11},{"key_as_string":"2017-02-08T12:53:00.000Z","key":1486558380000,"doc_count":10},{"key_as_string":"2017-02-08T12:54:00.000Z","key":1486558440000,"doc_count":14},{"key_as_string":"2017-02-08T12:55:00.000Z","key":1486558500000,"doc_count":11},{"key_as_string":"2017-02-08T12:56:00.000Z","key":1486558560000,"doc_count":9},{"key_as_string":"2017-02-08T12:57:00.000Z","key":1486558620000,"doc_count":13},{"key_as_string":"2017-02-08T12:58:00.000Z","key":1486558680000,"doc_count":13},{"key_as_string":"2017-02-08T12:59:00.000Z","key":1486558740000,"doc_count":13},{"key_as_string":"2017-02-08T13:00:00.000Z","key":1486558800000,"doc_count":18},{"key_as_string":"2017-02-08T13:01:00.000Z","key":1486558860000,"doc_count":13},{"key_as_string":"2017-02-08T13:02:00.000Z","key":1486558920000,"doc_count":20},{"key_as_string":"2017-02-08T13:03:00.000Z","key":1486558980000,"doc_count":10},{"key_as_string":"2017-02-08T13:04:00.000Z","key":1486559040000,"doc_count":16},{"key_as_string":"2017-02-08T13:05:00.000Z","key":1486559100000,"doc_count":11},{"key_as_string":"2017-02-08T13:06:00.000Z","key":1486559160000,"doc_count":18},{"key_as_string":"2017-02-08T13:07:00.000Z","key":1486559220000,"doc_count":10},{"key_as_string":"2017-02-08T13:08:00.000Z","key":1486559280000,"doc_count":15},{"key_as_string":"2017-02-08T13:09:00.000Z","key":1486559340000,"doc_count":17},{"key_as_string":"2017-02-08T13:10:00.000Z","key":1486559400000,"doc_count":6},{"key_as_string":"2017-02-08T13:11:00.000Z","key":1486559460000,"doc_count":19},{"key_as_string":"2017-02-08T13:12:00.000Z","key":1486559520000,"doc_count":12},{"key_as_string":"2017-02-08T13:13:00.000Z","key":1486559580000,"doc_count":11},{"key_as_string":"2017-02-08T13:14:00.000Z","key":1486559640000,"doc_count":15},{"key_as_string":"2017-02-08T13:15:00.000Z","key":1486559700000,"doc_count":10},{"key_as_string":"2017-02-08T13:16:00.000Z","key":1486559760000,"doc_count":16},{"key_as_string":"2017-02-08T13:17:00.000Z","key":1486559820000,"doc_count":16},{"key_as_string":"2017-02-08T13:18:00.000Z","key":1486559880000,"doc_count":14},{"key_as_string":"2017-02-08T13:19:00.000Z","key":1486559940000,"doc_count":12},{"key_as_string":"2017-02-08T13:20:00.000Z","key":1486560000000,"doc_count":10},{"key_as_string":"2017-02-08T13:21:00.000Z","key":1486560060000,"doc_count":18},{"key_as_string":"2017-02-08T13:22:00.000Z","key":1486560120000,"doc_count":14},{"key_as_string":"2017-02-08T13:23:00.000Z","key":1486560180000,"doc_count":11},{"key_as_string":"2017-02-08T13:24:00.000Z","key":1486560240000,"doc_count":15},{"key_as_string":"2017-02-08T13:25:00.000Z","key":1486560300000,"doc_count":17},{"key_as_string":"2017-02-08T13:26:00.000Z","key":1486560360000,"doc_count":10},{"key_as_string":"2017-02-08T13:27:00.000Z","key":1486560420000,"doc_count":14},{"key_as_string":"2017-02-08T13:28:00.000Z","key":1486560480000,"doc_count":14},{"key_as_string":"2017-02-08T13:29:00.000Z","key":1486560540000,"doc_count":13},{"key_as_string":"2017-02-08T13:30:00.000Z","key":1486560600000,"doc_count":15},{"key_as_string":"2017-02-08T13:31:00.000Z","key":1486560660000,"doc_count":16},{"key_as_string":"2017-02-08T13:32:00.000Z","key":1486560720000,"doc_count":13},{"key_as_string":"2017-02-08T13:33:00.000Z","key":1486560780000,"doc_count":13},{"key_as_string":"2017-02-08T13:34:00.000Z","key":1486560840000,"doc_count":15},{"key_as_string":"2017-02-08T13:35:00.000Z","key":1486560900000,"doc_count":8},{"key_as_string":"2017-02-08T13:36:00.000Z","key":1486560960000,"doc_count":18},{"key_as_string":"2017-02-08T13:37:00.000Z","key":1486561020000,"doc_count":13},{"key_as_string":"2017-02-08T13:38:00.000Z","key":1486561080000,"doc_count":14},{"key_as_string":"2017-02-08T13:39:00.000Z","key":1486561140000,"doc_count":14},{"key_as_string":"2017-02-08T13:40:00.000Z","key":1486561200000,"doc_count":17},{"key_as_string":"2017-02-08T13:41:00.000Z","key":1486561260000,"doc_count":12},{"key_as_string":"2017-02-08T13:42:00.000Z","key":1486561320000,"doc_count":10},{"key_as_string":"2017-02-08T13:43:00.000Z","key":1486561380000,"doc_count":17},{"key_as_string":"2017-02-08T13:44:00.000Z","key":1486561440000,"doc_count":12},{"key_as_string":"2017-02-08T13:45:00.000Z","key":1486561500000,"doc_count":9},{"key_as_string":"2017-02-08T13:46:00.000Z","key":1486561560000,"doc_count":21},{"key_as_string":"2017-02-08T13:47:00.000Z","key":1486561620000,"doc_count":16},{"key_as_string":"2017-02-08T13:48:00.000Z","key":1486561680000,"doc_count":17},{"key_as_string":"2017-02-08T13:49:00.000Z","key":1486561740000,"doc_count":11},{"key_as_string":"2017-02-08T13:50:00.000Z","key":1486561800000,"doc_count":17},{"key_as_string":"2017-02-08T13:51:00.000Z","key":1486561860000,"doc_count":9},{"key_as_string":"2017-02-08T13:52:00.000Z","key":1486561920000,"doc_count":14},{"key_as_string":"2017-02-08T13:53:00.000Z","key":1486561980000,"doc_count":10},{"key_as_string":"2017-02-08T13:54:00.000Z","key":1486562040000,"doc_count":9},{"key_as_string":"2017-02-08T13:55:00.000Z","key":1486562100000,"doc_count":21},{"key_as_string":"2017-02-08T13:56:00.000Z","key":1486562160000,"doc_count":14},{"key_as_string":"2017-02-08T13:57:00.000Z","key":1486562220000,"doc_count":11},{"key_as_string":"2017-02-08T13:58:00.000Z","key":1486562280000,"doc_count":15},{"key_as_string":"2017-02-08T13:59:00.000Z","key":1486562340000,"doc_count":10},{"key_as_string":"2017-02-08T14:00:00.000Z","key":1486562400000,"doc_count":12},{"key_as_string":"2017-02-08T14:01:00.000Z","key":1486562460000,"doc_count":15},{"key_as_string":"2017-02-08T14:02:00.000Z","key":1486562520000,"doc_count":11},{"key_as_string":"2017-02-08T14:03:00.000Z","key":1486562580000,"doc_count":14},{"key_as_string":"2017-02-08T14:04:00.000Z","key":1486562640000,"doc_count":18},{"key_as_string":"2017-02-08T14:05:00.000Z","key":1486562700000,"doc_count":11},{"key_as_string":"2017-02-08T14:06:00.000Z","key":1486562760000,"doc_count":11},{"key_as_string":"2017-02-08T14:07:00.000Z","key":1486562820000,"doc_count":16},{"key_as_string":"2017-02-08T14:08:00.000Z","key":1486562880000,"doc_count":10},{"key_as_string":"2017-02-08T14:09:00.000Z","key":1486562940000,"doc_count":14},{"key_as_string":"2017-02-08T14:10:00.000Z","key":1486563000000,"doc_count":14},{"key_as_string":"2017-02-08T14:11:00.000Z","key":1486563060000,"doc_count":9},{"key_as_string":"2017-02-08T14:12:00.000Z","key":1486563120000,"doc_count":15},{"key_as_string":"2017-02-08T14:13:00.000Z","key":1486563180000,"doc_count":17},{"key_as_string":"2017-02-08T14:14:00.000Z","key":1486563240000,"doc_count":5},{"key_as_string":"2017-02-08T14:15:00.000Z","key":1486563300000,"doc_count":15},{"key_as_string":"2017-02-08T14:16:00.000Z","key":1486563360000,"doc_count":15},{"key_as_string":"2017-02-08T14:17:00.000Z","key":1486563420000,"doc_count":13},{"key_as_string":"2017-02-08T14:18:00.000Z","key":1486563480000,"doc_count":15},{"key_as_string":"2017-02-08T14:19:00.000Z","key":1486563540000,"doc_count":14},{"key_as_string":"2017-02-08T14:20:00.000Z","key":1486563600000,"doc_count":8},{"key_as_string":"2017-02-08T14:21:00.000Z","key":1486563660000,"doc_count":14},{"key_as_string":"2017-02-08T14:22:00.000Z","key":1486563720000,"doc_count":11},{"key_as_string":"2017-02-08T14:23:00.000Z","key":1486563780000,"doc_count":14},{"key_as_string":"2017-02-08T14:24:00.000Z","key":1486563840000,"doc_count":15},{"key_as_string":"2017-02-08T14:25:00.000Z","key":1486563900000,"doc_count":9},{"key_as_string":"2017-02-08T14:26:00.000Z","key":1486563960000,"doc_count":16},{"key_as_string":"2017-02-08T14:27:00.000Z","key":1486564020000,"doc_count":15},{"key_as_string":"2017-02-08T14:28:00.000Z","key":1486564080000,"doc_count":13},{"key_as_string":"2017-02-08T14:29:00.000Z","key":1486564140000,"doc_count":14},{"key_as_string":"2017-02-08T14:30:00.000Z","key":1486564200000,"doc_count":10},{"key_as_string":"2017-02-08T14:31:00.000Z","key":1486564260000,"doc_count":15},{"key_as_string":"2017-02-08T14:32:00.000Z","key":1486564320000,"doc_count":15},{"key_as_string":"2017-02-08T14:33:00.000Z","key":1486564380000,"doc_count":11},{"key_as_string":"2017-02-08T14:34:00.000Z","key":1486564440000,"doc_count":13},{"key_as_string":"2017-02-08T14:35:00.000Z","key":1486564500000,"doc_count":14},{"key_as_string":"2017-02-08T14:36:00.000Z","key":1486564560000,"doc_count":11},{"key_as_string":"2017-02-08T14:37:00.000Z","key":1486564620000,"doc_count":10},{"key_as_string":"2017-02-08T14:38:00.000Z","key":1486564680000,"doc_count":18},{"key_as_string":"2017-02-08T14:39:00.000Z","key":1486564740000,"doc_count":12},{"key_as_string":"2017-02-08T14:40:00.000Z","key":1486564800000,"doc_count":14},{"key_as_string":"2017-02-08T14:41:00.000Z","key":1486564860000,"doc_count":15},{"key_as_string":"2017-02-08T14:42:00.000Z","key":1486564920000,"doc_count":17},{"key_as_string":"2017-02-08T14:43:00.000Z","key":1486564980000,"doc_count":9},{"key_as_string":"2017-02-08T14:44:00.000Z","key":1486565040000,"doc_count":10},{"key_as_string":"2017-02-08T14:45:00.000Z","key":1486565100000,"doc_count":9},{"key_as_string":"2017-02-08T14:46:00.000Z","key":1486565160000,"doc_count":15},{"key_as_string":"2017-02-08T14:47:00.000Z","key":1486565220000,"doc_count":12},{"key_as_string":"2017-02-08T14:48:00.000Z","key":1486565280000,"doc_count":10},{"key_as_string":"2017-02-08T14:49:00.000Z","key":1486565340000,"doc_count":18},{"key_as_string":"2017-02-08T14:50:00.000Z","key":1486565400000,"doc_count":9},{"key_as_string":"2017-02-08T14:51:00.000Z","key":1486565460000,"doc_count":15},{"key_as_string":"2017-02-08T14:52:00.000Z","key":1486565520000,"doc_count":19},{"key_as_string":"2017-02-08T14:53:00.000Z","key":1486565580000,"doc_count":11},{"key_as_string":"2017-02-08T14:54:00.000Z","key":1486565640000,"doc_count":12},{"key_as_string":"2017-02-08T14:55:00.000Z","key":1486565700000,"doc_count":10},{"key_as_string":"2017-02-08T14:56:00.000Z","key":1486565760000,"doc_count":9},{"key_as_string":"2017-02-08T14:57:00.000Z","key":1486565820000,"doc_count":21},{"key_as_string":"2017-02-08T14:58:00.000Z","key":1486565880000,"doc_count":13},{"key_as_string":"2017-02-08T14:59:00.000Z","key":1486565940000,"doc_count":16},{"key_as_string":"2017-02-08T15:00:00.000Z","key":1486566000000,"doc_count":9},{"key_as_string":"2017-02-08T15:01:00.000Z","key":1486566060000,"doc_count":16},{"key_as_string":"2017-02-08T15:02:00.000Z","key":1486566120000,"doc_count":11},{"key_as_string":"2017-02-08T15:03:00.000Z","key":1486566180000,"doc_count":14},{"key_as_string":"2017-02-08T15:04:00.000Z","key":1486566240000,"doc_count":16},{"key_as_string":"2017-02-08T15:05:00.000Z","key":1486566300000,"doc_count":10},{"key_as_string":"2017-02-08T15:06:00.000Z","key":1486566360000,"doc_count":14},{"key_as_string":"2017-02-08T15:07:00.000Z","key":1486566420000,"doc_count":9},{"key_as_string":"2017-02-08T15:08:00.000Z","key":1486566480000,"doc_count":13},{"key_as_string":"2017-02-08T15:09:00.000Z","key":1486566540000,"doc_count":13},{"key_as_string":"2017-02-08T15:10:00.000Z","key":1486566600000,"doc_count":14},{"key_as_string":"2017-02-08T15:11:00.000Z","key":1486566660000,"doc_count":15},{"key_as_string":"2017-02-08T15:12:00.000Z","key":1486566720000,"doc_count":15},{"key_as_string":"2017-02-08T15:13:00.000Z","key":1486566780000,"doc_count":11},{"key_as_string":"2017-02-08T15:14:00.000Z","key":1486566840000,"doc_count":7},{"key_as_string":"2017-02-08T15:15:00.000Z","key":1486566900000,"doc_count":20},{"key_as_string":"2017-02-08T15:16:00.000Z","key":1486566960000,"doc_count":11},{"key_as_string":"2017-02-08T15:17:00.000Z","key":1486567020000,"doc_count":15},{"key_as_string":"2017-02-08T15:18:00.000Z","key":1486567080000,"doc_count":14},{"key_as_string":"2017-02-08T15:19:00.000Z","key":1486567140000,"doc_count":11},{"key_as_string":"2017-02-08T15:20:00.000Z","key":1486567200000,"doc_count":18},{"key_as_string":"2017-02-08T15:21:00.000Z","key":1486567260000,"doc_count":11},{"key_as_string":"2017-02-08T15:22:00.000Z","key":1486567320000,"doc_count":12},{"key_as_string":"2017-02-08T15:23:00.000Z","key":1486567380000,"doc_count":12},{"key_as_string":"2017-02-08T15:24:00.000Z","key":1486567440000,"doc_count":8},{"key_as_string":"2017-02-08T15:25:00.000Z","key":1486567500000,"doc_count":11},{"key_as_string":"2017-02-08T15:26:00.000Z","key":1486567560000,"doc_count":12},{"key_as_string":"2017-02-08T15:27:00.000Z","key":1486567620000,"doc_count":12},{"key_as_string":"2017-02-08T15:28:00.000Z","key":1486567680000,"doc_count":14},{"key_as_string":"2017-02-08T15:29:00.000Z","key":1486567740000,"doc_count":13},{"key_as_string":"2017-02-08T15:30:00.000Z","key":1486567800000,"doc_count":13},{"key_as_string":"2017-02-08T15:31:00.000Z","key":1486567860000,"doc_count":14},{"key_as_string":"2017-02-08T15:32:00.000Z","key":1486567920000,"doc_count":15},{"key_as_string":"2017-02-08T15:33:00.000Z","key":1486567980000,"doc_count":12},{"key_as_string":"2017-02-08T15:34:00.000Z","key":1486568040000,"doc_count":15},{"key_as_string":"2017-02-08T15:35:00.000Z","key":1486568100000,"doc_count":20},{"key_as_string":"2017-02-08T15:36:00.000Z","key":1486568160000,"doc_count":11},{"key_as_string":"2017-02-08T15:37:00.000Z","key":1486568220000,"doc_count":13},{"key_as_string":"2017-02-08T15:38:00.000Z","key":1486568280000,"doc_count":13},{"key_as_string":"2017-02-08T15:39:00.000Z","key":1486568340000,"doc_count":10},{"key_as_string":"2017-02-08T15:40:00.000Z","key":1486568400000,"doc_count":14},{"key_as_string":"2017-02-08T15:41:00.000Z","key":1486568460000,"doc_count":16},{"key_as_string":"2017-02-08T15:42:00.000Z","key":1486568520000,"doc_count":17},{"key_as_string":"2017-02-08T15:43:00.000Z","key":1486568580000,"doc_count":15},{"key_as_string":"2017-02-08T15:44:00.000Z","key":1486568640000,"doc_count":14},{"key_as_string":"2017-02-08T15:45:00.000Z","key":1486568700000,"doc_count":14},{"key_as_string":"2017-02-08T15:46:00.000Z","key":1486568760000,"doc_count":12},{"key_as_string":"2017-02-08T15:47:00.000Z","key":1486568820000,"doc_count":16},{"key_as_string":"2017-02-08T15:48:00.000Z","key":1486568880000,"doc_count":16},{"key_as_string":"2017-02-08T15:49:00.000Z","key":1486568940000,"doc_count":12},{"key_as_string":"2017-02-08T15:50:00.000Z","key":1486569000000,"doc_count":15},{"key_as_string":"2017-02-08T15:51:00.000Z","key":1486569060000,"doc_count":10},{"key_as_string":"2017-02-08T15:52:00.000Z","key":1486569120000,"doc_count":12},{"key_as_string":"2017-02-08T15:53:00.000Z","key":1486569180000,"doc_count":12},{"key_as_string":"2017-02-08T15:54:00.000Z","key":1486569240000,"doc_count":16},{"key_as_string":"2017-02-08T15:55:00.000Z","key":1486569300000,"doc_count":12},{"key_as_string":"2017-02-08T15:56:00.000Z","key":1486569360000,"doc_count":9},{"key_as_string":"2017-02-08T15:57:00.000Z","key":1486569420000,"doc_count":11},{"key_as_string":"2017-02-08T15:58:00.000Z","key":1486569480000,"doc_count":10},{"key_as_string":"2017-02-08T15:59:00.000Z","key":1486569540000,"doc_count":12},{"key_as_string":"2017-02-08T16:00:00.000Z","key":1486569600000,"doc_count":12},{"key_as_string":"2017-02-08T16:01:00.000Z","key":1486569660000,"doc_count":14},{"key_as_string":"2017-02-08T16:02:00.000Z","key":1486569720000,"doc_count":13},{"key_as_string":"2017-02-08T16:03:00.000Z","key":1486569780000,"doc_count":11},{"key_as_string":"2017-02-08T16:04:00.000Z","key":1486569840000,"doc_count":12},{"key_as_string":"2017-02-08T16:05:00.000Z","key":1486569900000,"doc_count":16},{"key_as_string":"2017-02-08T16:06:00.000Z","key":1486569960000,"doc_count":13},{"key_as_string":"2017-02-08T16:07:00.000Z","key":1486570020000,"doc_count":12},{"key_as_string":"2017-02-08T16:08:00.000Z","key":1486570080000,"doc_count":15},{"key_as_string":"2017-02-08T16:09:00.000Z","key":1486570140000,"doc_count":7},{"key_as_string":"2017-02-08T16:10:00.000Z","key":1486570200000,"doc_count":15},{"key_as_string":"2017-02-08T16:11:00.000Z","key":1486570260000,"doc_count":13},{"key_as_string":"2017-02-08T16:12:00.000Z","key":1486570320000,"doc_count":10},{"key_as_string":"2017-02-08T16:13:00.000Z","key":1486570380000,"doc_count":13},{"key_as_string":"2017-02-08T16:14:00.000Z","key":1486570440000,"doc_count":13},{"key_as_string":"2017-02-08T16:15:00.000Z","key":1486570500000,"doc_count":7},{"key_as_string":"2017-02-08T16:16:00.000Z","key":1486570560000,"doc_count":12},{"key_as_string":"2017-02-08T16:17:00.000Z","key":1486570620000,"doc_count":11},{"key_as_string":"2017-02-08T16:18:00.000Z","key":1486570680000,"doc_count":10},{"key_as_string":"2017-02-08T16:19:00.000Z","key":1486570740000,"doc_count":8},{"key_as_string":"2017-02-08T16:20:00.000Z","key":1486570800000,"doc_count":13},{"key_as_string":"2017-02-08T16:21:00.000Z","key":1486570860000,"doc_count":12},{"key_as_string":"2017-02-08T16:22:00.000Z","key":1486570920000,"doc_count":12},{"key_as_string":"2017-02-08T16:23:00.000Z","key":1486570980000,"doc_count":9},{"key_as_string":"2017-02-08T16:24:00.000Z","key":1486571040000,"doc_count":12},{"key_as_string":"2017-02-08T16:25:00.000Z","key":1486571100000,"doc_count":13},{"key_as_string":"2017-02-08T16:26:00.000Z","key":1486571160000,"doc_count":17},{"key_as_string":"2017-02-08T16:27:00.000Z","key":1486571220000,"doc_count":15},{"key_as_string":"2017-02-08T16:28:00.000Z","key":1486571280000,"doc_count":14},{"key_as_string":"2017-02-08T16:29:00.000Z","key":1486571340000,"doc_count":15},{"key_as_string":"2017-02-08T16:30:00.000Z","key":1486571400000,"doc_count":12},{"key_as_string":"2017-02-08T16:31:00.000Z","key":1486571460000,"doc_count":11},{"key_as_string":"2017-02-08T16:32:00.000Z","key":1486571520000,"doc_count":16},{"key_as_string":"2017-02-08T16:33:00.000Z","key":1486571580000,"doc_count":9},{"key_as_string":"2017-02-08T16:34:00.000Z","key":1486571640000,"doc_count":10},{"key_as_string":"2017-02-08T16:35:00.000Z","key":1486571700000,"doc_count":16},{"key_as_string":"2017-02-08T16:36:00.000Z","key":1486571760000,"doc_count":18},{"key_as_string":"2017-02-08T16:37:00.000Z","key":1486571820000,"doc_count":13},{"key_as_string":"2017-02-08T16:38:00.000Z","key":1486571880000,"doc_count":12},{"key_as_string":"2017-02-08T16:39:00.000Z","key":1486571940000,"doc_count":12},{"key_as_string":"2017-02-08T16:40:00.000Z","key":1486572000000,"doc_count":15},{"key_as_string":"2017-02-08T16:41:00.000Z","key":1486572060000,"doc_count":14},{"key_as_string":"2017-02-08T16:42:00.000Z","key":1486572120000,"doc_count":9},{"key_as_string":"2017-02-08T16:43:00.000Z","key":1486572180000,"doc_count":10},{"key_as_string":"2017-02-08T16:44:00.000Z","key":1486572240000,"doc_count":14},{"key_as_string":"2017-02-08T16:45:00.000Z","key":1486572300000,"doc_count":15},{"key_as_string":"2017-02-08T16:46:00.000Z","key":1486572360000,"doc_count":18},{"key_as_string":"2017-02-08T16:47:00.000Z","key":1486572420000,"doc_count":8},{"key_as_string":"2017-02-08T16:48:00.000Z","key":1486572480000,"doc_count":14},{"key_as_string":"2017-02-08T16:49:00.000Z","key":1486572540000,"doc_count":11},{"key_as_string":"2017-02-08T16:50:00.000Z","key":1486572600000,"doc_count":12},{"key_as_string":"2017-02-08T16:51:00.000Z","key":1486572660000,"doc_count":8},{"key_as_string":"2017-02-08T16:52:00.000Z","key":1486572720000,"doc_count":13},{"key_as_string":"2017-02-08T16:53:00.000Z","key":1486572780000,"doc_count":12},{"key_as_string":"2017-02-08T16:54:00.000Z","key":1486572840000,"doc_count":15},{"key_as_string":"2017-02-08T16:55:00.000Z","key":1486572900000,"doc_count":15},{"key_as_string":"2017-02-08T16:56:00.000Z","key":1486572960000,"doc_count":12},{"key_as_string":"2017-02-08T16:57:00.000Z","key":1486573020000,"doc_count":10},{"key_as_string":"2017-02-08T16:58:00.000Z","key":1486573080000,"doc_count":11},{"key_as_string":"2017-02-08T16:59:00.000Z","key":1486573140000,"doc_count":18},{"key_as_string":"2017-02-08T17:00:00.000Z","key":1486573200000,"doc_count":11},{"key_as_string":"2017-02-08T17:01:00.000Z","key":1486573260000,"doc_count":10},{"key_as_string":"2017-02-08T17:02:00.000Z","key":1486573320000,"doc_count":14},{"key_as_string":"2017-02-08T17:03:00.000Z","key":1486573380000,"doc_count":11},{"key_as_string":"2017-02-08T17:04:00.000Z","key":1486573440000,"doc_count":14},{"key_as_string":"2017-02-08T17:05:00.000Z","key":1486573500000,"doc_count":7},{"key_as_string":"2017-02-08T17:06:00.000Z","key":1486573560000,"doc_count":17},{"key_as_string":"2017-02-08T17:07:00.000Z","key":1486573620000,"doc_count":6},{"key_as_string":"2017-02-08T17:08:00.000Z","key":1486573680000,"doc_count":12},{"key_as_string":"2017-02-08T17:09:00.000Z","key":1486573740000,"doc_count":7},{"key_as_string":"2017-02-08T17:10:00.000Z","key":1486573800000,"doc_count":17},{"key_as_string":"2017-02-08T17:11:00.000Z","key":1486573860000,"doc_count":19},{"key_as_string":"2017-02-08T17:12:00.000Z","key":1486573920000,"doc_count":13},{"key_as_string":"2017-02-08T17:13:00.000Z","key":1486573980000,"doc_count":9},{"key_as_string":"2017-02-08T17:14:00.000Z","key":1486574040000,"doc_count":16},{"key_as_string":"2017-02-08T17:15:00.000Z","key":1486574100000,"doc_count":15},{"key_as_string":"2017-02-08T17:16:00.000Z","key":1486574160000,"doc_count":11},{"key_as_string":"2017-02-08T17:17:00.000Z","key":1486574220000,"doc_count":18},{"key_as_string":"2017-02-08T17:18:00.000Z","key":1486574280000,"doc_count":13},{"key_as_string":"2017-02-08T17:19:00.000Z","key":1486574340000,"doc_count":16},{"key_as_string":"2017-02-08T17:20:00.000Z","key":1486574400000,"doc_count":14},{"key_as_string":"2017-02-08T17:21:00.000Z","key":1486574460000,"doc_count":13},{"key_as_string":"2017-02-08T17:22:00.000Z","key":1486574520000,"doc_count":13},{"key_as_string":"2017-02-08T17:23:00.000Z","key":1486574580000,"doc_count":11},{"key_as_string":"2017-02-08T17:24:00.000Z","key":1486574640000,"doc_count":8},{"key_as_string":"2017-02-08T17:25:00.000Z","key":1486574700000,"doc_count":9},{"key_as_string":"2017-02-08T17:26:00.000Z","key":1486574760000,"doc_count":16},{"key_as_string":"2017-02-08T17:27:00.000Z","key":1486574820000,"doc_count":13},{"key_as_string":"2017-02-08T17:28:00.000Z","key":1486574880000,"doc_count":9},{"key_as_string":"2017-02-08T17:29:00.000Z","key":1486574940000,"doc_count":13},{"key_as_string":"2017-02-08T17:30:00.000Z","key":1486575000000,"doc_count":13},{"key_as_string":"2017-02-08T17:31:00.000Z","key":1486575060000,"doc_count":15},{"key_as_string":"2017-02-08T17:32:00.000Z","key":1486575120000,"doc_count":10},{"key_as_string":"2017-02-08T17:33:00.000Z","key":1486575180000,"doc_count":14},{"key_as_string":"2017-02-08T17:34:00.000Z","key":1486575240000,"doc_count":10},{"key_as_string":"2017-02-08T17:35:00.000Z","key":1486575300000,"doc_count":14},{"key_as_string":"2017-02-08T17:36:00.000Z","key":1486575360000,"doc_count":10},{"key_as_string":"2017-02-08T17:37:00.000Z","key":1486575420000,"doc_count":9},{"key_as_string":"2017-02-08T17:38:00.000Z","key":1486575480000,"doc_count":11},{"key_as_string":"2017-02-08T17:39:00.000Z","key":1486575540000,"doc_count":12},{"key_as_string":"2017-02-08T17:40:00.000Z","key":1486575600000,"doc_count":10},{"key_as_string":"2017-02-08T17:41:00.000Z","key":1486575660000,"doc_count":15},{"key_as_string":"2017-02-08T17:42:00.000Z","key":1486575720000,"doc_count":18},{"key_as_string":"2017-02-08T17:43:00.000Z","key":1486575780000,"doc_count":9},{"key_as_string":"2017-02-08T17:44:00.000Z","key":1486575840000,"doc_count":14},{"key_as_string":"2017-02-08T17:45:00.000Z","key":1486575900000,"doc_count":9},{"key_as_string":"2017-02-08T17:46:00.000Z","key":1486575960000,"doc_count":11},{"key_as_string":"2017-02-08T17:47:00.000Z","key":1486576020000,"doc_count":13},{"key_as_string":"2017-02-08T17:48:00.000Z","key":1486576080000,"doc_count":10},{"key_as_string":"2017-02-08T17:49:00.000Z","key":1486576140000,"doc_count":9},{"key_as_string":"2017-02-08T17:50:00.000Z","key":1486576200000,"doc_count":13},{"key_as_string":"2017-02-08T17:51:00.000Z","key":1486576260000,"doc_count":12},{"key_as_string":"2017-02-08T17:52:00.000Z","key":1486576320000,"doc_count":11},{"key_as_string":"2017-02-08T17:53:00.000Z","key":1486576380000,"doc_count":12},{"key_as_string":"2017-02-08T17:54:00.000Z","key":1486576440000,"doc_count":11},{"key_as_string":"2017-02-08T17:55:00.000Z","key":1486576500000,"doc_count":14},{"key_as_string":"2017-02-08T17:56:00.000Z","key":1486576560000,"doc_count":17},{"key_as_string":"2017-02-08T17:57:00.000Z","key":1486576620000,"doc_count":12},{"key_as_string":"2017-02-08T17:58:00.000Z","key":1486576680000,"doc_count":8},{"key_as_string":"2017-02-08T17:59:00.000Z","key":1486576740000,"doc_count":12},{"key_as_string":"2017-02-08T18:00:00.000Z","key":1486576800000,"doc_count":15},{"key_as_string":"2017-02-08T18:01:00.000Z","key":1486576860000,"doc_count":10},{"key_as_string":"2017-02-08T18:02:00.000Z","key":1486576920000,"doc_count":11},{"key_as_string":"2017-02-08T18:03:00.000Z","key":1486576980000,"doc_count":11},{"key_as_string":"2017-02-08T18:04:00.000Z","key":1486577040000,"doc_count":13},{"key_as_string":"2017-02-08T18:05:00.000Z","key":1486577100000,"doc_count":11},{"key_as_string":"2017-02-08T18:06:00.000Z","key":1486577160000,"doc_count":10},{"key_as_string":"2017-02-08T18:07:00.000Z","key":1486577220000,"doc_count":12},{"key_as_string":"2017-02-08T18:08:00.000Z","key":1486577280000,"doc_count":13},{"key_as_string":"2017-02-08T18:09:00.000Z","key":1486577340000,"doc_count":8},{"key_as_string":"2017-02-08T18:10:00.000Z","key":1486577400000,"doc_count":6},{"key_as_string":"2017-02-08T18:11:00.000Z","key":1486577460000,"doc_count":17},{"key_as_string":"2017-02-08T18:12:00.000Z","key":1486577520000,"doc_count":11},{"key_as_string":"2017-02-08T18:13:00.000Z","key":1486577580000,"doc_count":13},{"key_as_string":"2017-02-08T18:14:00.000Z","key":1486577640000,"doc_count":10},{"key_as_string":"2017-02-08T18:15:00.000Z","key":1486577700000,"doc_count":11},{"key_as_string":"2017-02-08T18:16:00.000Z","key":1486577760000,"doc_count":13},{"key_as_string":"2017-02-08T18:17:00.000Z","key":1486577820000,"doc_count":12},{"key_as_string":"2017-02-08T18:18:00.000Z","key":1486577880000,"doc_count":15},{"key_as_string":"2017-02-08T18:19:00.000Z","key":1486577940000,"doc_count":10},{"key_as_string":"2017-02-08T18:20:00.000Z","key":1486578000000,"doc_count":10},{"key_as_string":"2017-02-08T18:21:00.000Z","key":1486578060000,"doc_count":12},{"key_as_string":"2017-02-08T18:22:00.000Z","key":1486578120000,"doc_count":16},{"key_as_string":"2017-02-08T18:23:00.000Z","key":1486578180000,"doc_count":7},{"key_as_string":"2017-02-08T18:24:00.000Z","key":1486578240000,"doc_count":12},{"key_as_string":"2017-02-08T18:25:00.000Z","key":1486578300000,"doc_count":10},{"key_as_string":"2017-02-08T18:26:00.000Z","key":1486578360000,"doc_count":8},{"key_as_string":"2017-02-08T18:27:00.000Z","key":1486578420000,"doc_count":15},{"key_as_string":"2017-02-08T18:28:00.000Z","key":1486578480000,"doc_count":10},{"key_as_string":"2017-02-08T18:29:00.000Z","key":1486578540000,"doc_count":13},{"key_as_string":"2017-02-08T18:30:00.000Z","key":1486578600000,"doc_count":10},{"key_as_string":"2017-02-08T18:31:00.000Z","key":1486578660000,"doc_count":11},{"key_as_string":"2017-02-08T18:32:00.000Z","key":1486578720000,"doc_count":12},{"key_as_string":"2017-02-08T18:33:00.000Z","key":1486578780000,"doc_count":10},{"key_as_string":"2017-02-08T18:34:00.000Z","key":1486578840000,"doc_count":15},{"key_as_string":"2017-02-08T18:35:00.000Z","key":1486578900000,"doc_count":13},{"key_as_string":"2017-02-08T18:36:00.000Z","key":1486578960000,"doc_count":13},{"key_as_string":"2017-02-08T18:37:00.000Z","key":1486579020000,"doc_count":11},{"key_as_string":"2017-02-08T18:38:00.000Z","key":1486579080000,"doc_count":9},{"key_as_string":"2017-02-08T18:39:00.000Z","key":1486579140000,"doc_count":14},{"key_as_string":"2017-02-08T18:40:00.000Z","key":1486579200000,"doc_count":12},{"key_as_string":"2017-02-08T18:41:00.000Z","key":1486579260000,"doc_count":14},{"key_as_string":"2017-02-08T18:42:00.000Z","key":1486579320000,"doc_count":13},{"key_as_string":"2017-02-08T18:43:00.000Z","key":1486579380000,"doc_count":11},{"key_as_string":"2017-02-08T18:44:00.000Z","key":1486579440000,"doc_count":11},{"key_as_string":"2017-02-08T18:45:00.000Z","key":1486579500000,"doc_count":11},{"key_as_string":"2017-02-08T18:46:00.000Z","key":1486579560000,"doc_count":9},{"key_as_string":"2017-02-08T18:47:00.000Z","key":1486579620000,"doc_count":14},{"key_as_string":"2017-02-08T18:48:00.000Z","key":1486579680000,"doc_count":9},{"key_as_string":"2017-02-08T18:49:00.000Z","key":1486579740000,"doc_count":13},{"key_as_string":"2017-02-08T18:50:00.000Z","key":1486579800000,"doc_count":16},{"key_as_string":"2017-02-08T18:51:00.000Z","key":1486579860000,"doc_count":10},{"key_as_string":"2017-02-08T18:52:00.000Z","key":1486579920000,"doc_count":9},{"key_as_string":"2017-02-08T18:53:00.000Z","key":1486579980000,"doc_count":9},{"key_as_string":"2017-02-08T18:54:00.000Z","key":1486580040000,"doc_count":11},{"key_as_string":"2017-02-08T18:55:00.000Z","key":1486580100000,"doc_count":13},{"key_as_string":"2017-02-08T18:56:00.000Z","key":1486580160000,"doc_count":11},{"key_as_string":"2017-02-08T18:57:00.000Z","key":1486580220000,"doc_count":16},{"key_as_string":"2017-02-08T18:58:00.000Z","key":1486580280000,"doc_count":11},{"key_as_string":"2017-02-08T18:59:00.000Z","key":1486580340000,"doc_count":14},{"key_as_string":"2017-02-08T19:00:00.000Z","key":1486580400000,"doc_count":14},{"key_as_string":"2017-02-08T19:01:00.000Z","key":1486580460000,"doc_count":7},{"key_as_string":"2017-02-08T19:02:00.000Z","key":1486580520000,"doc_count":12},{"key_as_string":"2017-02-08T19:03:00.000Z","key":1486580580000,"doc_count":12},{"key_as_string":"2017-02-08T19:04:00.000Z","key":1486580640000,"doc_count":8},{"key_as_string":"2017-02-08T19:05:00.000Z","key":1486580700000,"doc_count":7},{"key_as_string":"2017-02-08T19:06:00.000Z","key":1486580760000,"doc_count":17},{"key_as_string":"2017-02-08T19:07:00.000Z","key":1486580820000,"doc_count":8},{"key_as_string":"2017-02-08T19:08:00.000Z","key":1486580880000,"doc_count":15},{"key_as_string":"2017-02-08T19:09:00.000Z","key":1486580940000,"doc_count":11},{"key_as_string":"2017-02-08T19:10:00.000Z","key":1486581000000,"doc_count":9},{"key_as_string":"2017-02-08T19:11:00.000Z","key":1486581060000,"doc_count":14},{"key_as_string":"2017-02-08T19:12:00.000Z","key":1486581120000,"doc_count":15},{"key_as_string":"2017-02-08T19:13:00.000Z","key":1486581180000,"doc_count":12},{"key_as_string":"2017-02-08T19:14:00.000Z","key":1486581240000,"doc_count":7},{"key_as_string":"2017-02-08T19:15:00.000Z","key":1486581300000,"doc_count":10},{"key_as_string":"2017-02-08T19:16:00.000Z","key":1486581360000,"doc_count":13},{"key_as_string":"2017-02-08T19:17:00.000Z","key":1486581420000,"doc_count":13},{"key_as_string":"2017-02-08T19:18:00.000Z","key":1486581480000,"doc_count":8},{"key_as_string":"2017-02-08T19:19:00.000Z","key":1486581540000,"doc_count":9},{"key_as_string":"2017-02-08T19:20:00.000Z","key":1486581600000,"doc_count":12},{"key_as_string":"2017-02-08T19:21:00.000Z","key":1486581660000,"doc_count":8},{"key_as_string":"2017-02-08T19:22:00.000Z","key":1486581720000,"doc_count":16},{"key_as_string":"2017-02-08T19:23:00.000Z","key":1486581780000,"doc_count":10},{"key_as_string":"2017-02-08T19:24:00.000Z","key":1486581840000,"doc_count":12},{"key_as_string":"2017-02-08T19:25:00.000Z","key":1486581900000,"doc_count":11},{"key_as_string":"2017-02-08T19:26:00.000Z","key":1486581960000,"doc_count":8},{"key_as_string":"2017-02-08T19:27:00.000Z","key":1486582020000,"doc_count":11},{"key_as_string":"2017-02-08T19:28:00.000Z","key":1486582080000,"doc_count":19},{"key_as_string":"2017-02-08T19:29:00.000Z","key":1486582140000,"doc_count":8},{"key_as_string":"2017-02-08T19:30:00.000Z","key":1486582200000,"doc_count":13},{"key_as_string":"2017-02-08T19:31:00.000Z","key":1486582260000,"doc_count":17},{"key_as_string":"2017-02-08T19:32:00.000Z","key":1486582320000,"doc_count":11},{"key_as_string":"2017-02-08T19:33:00.000Z","key":1486582380000,"doc_count":9},{"key_as_string":"2017-02-08T19:34:00.000Z","key":1486582440000,"doc_count":9},{"key_as_string":"2017-02-08T19:35:00.000Z","key":1486582500000,"doc_count":18},{"key_as_string":"2017-02-08T19:36:00.000Z","key":1486582560000,"doc_count":8},{"key_as_string":"2017-02-08T19:37:00.000Z","key":1486582620000,"doc_count":9},{"key_as_string":"2017-02-08T19:38:00.000Z","key":1486582680000,"doc_count":12},{"key_as_string":"2017-02-08T19:39:00.000Z","key":1486582740000,"doc_count":11},{"key_as_string":"2017-02-08T19:40:00.000Z","key":1486582800000,"doc_count":13},{"key_as_string":"2017-02-08T19:41:00.000Z","key":1486582860000,"doc_count":8},{"key_as_string":"2017-02-08T19:42:00.000Z","key":1486582920000,"doc_count":10},{"key_as_string":"2017-02-08T19:43:00.000Z","key":1486582980000,"doc_count":10},{"key_as_string":"2017-02-08T19:44:00.000Z","key":1486583040000,"doc_count":15},{"key_as_string":"2017-02-08T19:45:00.000Z","key":1486583100000,"doc_count":7},{"key_as_string":"2017-02-08T19:46:00.000Z","key":1486583160000,"doc_count":12},{"key_as_string":"2017-02-08T19:47:00.000Z","key":1486583220000,"doc_count":8},{"key_as_string":"2017-02-08T19:48:00.000Z","key":1486583280000,"doc_count":11},{"key_as_string":"2017-02-08T19:49:00.000Z","key":1486583340000,"doc_count":10},{"key_as_string":"2017-02-08T19:50:00.000Z","key":1486583400000,"doc_count":10},{"key_as_string":"2017-02-08T19:51:00.000Z","key":1486583460000,"doc_count":9},{"key_as_string":"2017-02-08T19:52:00.000Z","key":1486583520000,"doc_count":13},{"key_as_string":"2017-02-08T19:53:00.000Z","key":1486583580000,"doc_count":6},{"key_as_string":"2017-02-08T19:54:00.000Z","key":1486583640000,"doc_count":16},{"key_as_string":"2017-02-08T19:55:00.000Z","key":1486583700000,"doc_count":15},{"key_as_string":"2017-02-08T19:56:00.000Z","key":1486583760000,"doc_count":14},{"key_as_string":"2017-02-08T19:57:00.000Z","key":1486583820000,"doc_count":6},{"key_as_string":"2017-02-08T19:58:00.000Z","key":1486583880000,"doc_count":13},{"key_as_string":"2017-02-08T19:59:00.000Z","key":1486583940000,"doc_count":11},{"key_as_string":"2017-02-08T20:00:00.000Z","key":1486584000000,"doc_count":9},{"key_as_string":"2017-02-08T20:01:00.000Z","key":1486584060000,"doc_count":11},{"key_as_string":"2017-02-08T20:02:00.000Z","key":1486584120000,"doc_count":16},{"key_as_string":"2017-02-08T20:03:00.000Z","key":1486584180000,"doc_count":8},{"key_as_string":"2017-02-08T20:04:00.000Z","key":1486584240000,"doc_count":9},{"key_as_string":"2017-02-08T20:05:00.000Z","key":1486584300000,"doc_count":8},{"key_as_string":"2017-02-08T20:06:00.000Z","key":1486584360000,"doc_count":13},{"key_as_string":"2017-02-08T20:07:00.000Z","key":1486584420000,"doc_count":15},{"key_as_string":"2017-02-08T20:08:00.000Z","key":1486584480000,"doc_count":7},{"key_as_string":"2017-02-08T20:09:00.000Z","key":1486584540000,"doc_count":13},{"key_as_string":"2017-02-08T20:10:00.000Z","key":1486584600000,"doc_count":12},{"key_as_string":"2017-02-08T20:11:00.000Z","key":1486584660000,"doc_count":6},{"key_as_string":"2017-02-08T20:12:00.000Z","key":1486584720000,"doc_count":9},{"key_as_string":"2017-02-08T20:13:00.000Z","key":1486584780000,"doc_count":12},{"key_as_string":"2017-02-08T20:14:00.000Z","key":1486584840000,"doc_count":13},{"key_as_string":"2017-02-08T20:15:00.000Z","key":1486584900000,"doc_count":13},{"key_as_string":"2017-02-08T20:16:00.000Z","key":1486584960000,"doc_count":10},{"key_as_string":"2017-02-08T20:17:00.000Z","key":1486585020000,"doc_count":8},{"key_as_string":"2017-02-08T20:18:00.000Z","key":1486585080000,"doc_count":12},{"key_as_string":"2017-02-08T20:19:00.000Z","key":1486585140000,"doc_count":12},{"key_as_string":"2017-02-08T20:20:00.000Z","key":1486585200000,"doc_count":14},{"key_as_string":"2017-02-08T20:21:00.000Z","key":1486585260000,"doc_count":11},{"key_as_string":"2017-02-08T20:22:00.000Z","key":1486585320000,"doc_count":13},{"key_as_string":"2017-02-08T20:23:00.000Z","key":1486585380000,"doc_count":13},{"key_as_string":"2017-02-08T20:24:00.000Z","key":1486585440000,"doc_count":9},{"key_as_string":"2017-02-08T20:25:00.000Z","key":1486585500000,"doc_count":11},{"key_as_string":"2017-02-08T20:26:00.000Z","key":1486585560000,"doc_count":9},{"key_as_string":"2017-02-08T20:27:00.000Z","key":1486585620000,"doc_count":20},{"key_as_string":"2017-02-08T20:28:00.000Z","key":1486585680000,"doc_count":6},{"key_as_string":"2017-02-08T20:29:00.000Z","key":1486585740000,"doc_count":12},{"key_as_string":"2017-02-08T20:30:00.000Z","key":1486585800000,"doc_count":11},{"key_as_string":"2017-02-08T20:31:00.000Z","key":1486585860000,"doc_count":9},{"key_as_string":"2017-02-08T20:32:00.000Z","key":1486585920000,"doc_count":12},{"key_as_string":"2017-02-08T20:33:00.000Z","key":1486585980000,"doc_count":8},{"key_as_string":"2017-02-08T20:34:00.000Z","key":1486586040000,"doc_count":12},{"key_as_string":"2017-02-08T20:35:00.000Z","key":1486586100000,"doc_count":11},{"key_as_string":"2017-02-08T20:36:00.000Z","key":1486586160000,"doc_count":9},{"key_as_string":"2017-02-08T20:37:00.000Z","key":1486586220000,"doc_count":14},{"key_as_string":"2017-02-08T20:38:00.000Z","key":1486586280000,"doc_count":12},{"key_as_string":"2017-02-08T20:39:00.000Z","key":1486586340000,"doc_count":8},{"key_as_string":"2017-02-08T20:40:00.000Z","key":1486586400000,"doc_count":11},{"key_as_string":"2017-02-08T20:41:00.000Z","key":1486586460000,"doc_count":10},{"key_as_string":"2017-02-08T20:42:00.000Z","key":1486586520000,"doc_count":8},{"key_as_string":"2017-02-08T20:43:00.000Z","key":1486586580000,"doc_count":11},{"key_as_string":"2017-02-08T20:44:00.000Z","key":1486586640000,"doc_count":16},{"key_as_string":"2017-02-08T20:45:00.000Z","key":1486586700000,"doc_count":9},{"key_as_string":"2017-02-08T20:46:00.000Z","key":1486586760000,"doc_count":9},{"key_as_string":"2017-02-08T20:47:00.000Z","key":1486586820000,"doc_count":8},{"key_as_string":"2017-02-08T20:48:00.000Z","key":1486586880000,"doc_count":13},{"key_as_string":"2017-02-08T20:49:00.000Z","key":1486586940000,"doc_count":10},{"key_as_string":"2017-02-08T20:50:00.000Z","key":1486587000000,"doc_count":9},{"key_as_string":"2017-02-08T20:51:00.000Z","key":1486587060000,"doc_count":10},{"key_as_string":"2017-02-08T20:52:00.000Z","key":1486587120000,"doc_count":10},{"key_as_string":"2017-02-08T20:53:00.000Z","key":1486587180000,"doc_count":10},{"key_as_string":"2017-02-08T20:54:00.000Z","key":1486587240000,"doc_count":12},{"key_as_string":"2017-02-08T20:55:00.000Z","key":1486587300000,"doc_count":8},{"key_as_string":"2017-02-08T20:56:00.000Z","key":1486587360000,"doc_count":10},{"key_as_string":"2017-02-08T20:57:00.000Z","key":1486587420000,"doc_count":11},{"key_as_string":"2017-02-08T20:58:00.000Z","key":1486587480000,"doc_count":9},{"key_as_string":"2017-02-08T20:59:00.000Z","key":1486587540000,"doc_count":15},{"key_as_string":"2017-02-08T21:00:00.000Z","key":1486587600000,"doc_count":12},{"key_as_string":"2017-02-08T21:01:00.000Z","key":1486587660000,"doc_count":13},{"key_as_string":"2017-02-08T21:02:00.000Z","key":1486587720000,"doc_count":10},{"key_as_string":"2017-02-08T21:03:00.000Z","key":1486587780000,"doc_count":7},{"key_as_string":"2017-02-08T21:04:00.000Z","key":1486587840000,"doc_count":9},{"key_as_string":"2017-02-08T21:05:00.000Z","key":1486587900000,"doc_count":13},{"key_as_string":"2017-02-08T21:06:00.000Z","key":1486587960000,"doc_count":9},{"key_as_string":"2017-02-08T21:07:00.000Z","key":1486588020000,"doc_count":12},{"key_as_string":"2017-02-08T21:08:00.000Z","key":1486588080000,"doc_count":6},{"key_as_string":"2017-02-08T21:09:00.000Z","key":1486588140000,"doc_count":17},{"key_as_string":"2017-02-08T21:10:00.000Z","key":1486588200000,"doc_count":4},{"key_as_string":"2017-02-08T21:11:00.000Z","key":1486588260000,"doc_count":13},{"key_as_string":"2017-02-08T21:12:00.000Z","key":1486588320000,"doc_count":9},{"key_as_string":"2017-02-08T21:13:00.000Z","key":1486588380000,"doc_count":13},{"key_as_string":"2017-02-08T21:14:00.000Z","key":1486588440000,"doc_count":10},{"key_as_string":"2017-02-08T21:15:00.000Z","key":1486588500000,"doc_count":8},{"key_as_string":"2017-02-08T21:16:00.000Z","key":1486588560000,"doc_count":8},{"key_as_string":"2017-02-08T21:17:00.000Z","key":1486588620000,"doc_count":10},{"key_as_string":"2017-02-08T21:18:00.000Z","key":1486588680000,"doc_count":12},{"key_as_string":"2017-02-08T21:19:00.000Z","key":1486588740000,"doc_count":10},{"key_as_string":"2017-02-08T21:20:00.000Z","key":1486588800000,"doc_count":13},{"key_as_string":"2017-02-08T21:21:00.000Z","key":1486588860000,"doc_count":14},{"key_as_string":"2017-02-08T21:22:00.000Z","key":1486588920000,"doc_count":9},{"key_as_string":"2017-02-08T21:23:00.000Z","key":1486588980000,"doc_count":9},{"key_as_string":"2017-02-08T21:24:00.000Z","key":1486589040000,"doc_count":12},{"key_as_string":"2017-02-08T21:25:00.000Z","key":1486589100000,"doc_count":12},{"key_as_string":"2017-02-08T21:26:00.000Z","key":1486589160000,"doc_count":19},{"key_as_string":"2017-02-08T21:27:00.000Z","key":1486589220000,"doc_count":6},{"key_as_string":"2017-02-08T21:28:00.000Z","key":1486589280000,"doc_count":15},{"key_as_string":"2017-02-08T21:29:00.000Z","key":1486589340000,"doc_count":10},{"key_as_string":"2017-02-08T21:30:00.000Z","key":1486589400000,"doc_count":9},{"key_as_string":"2017-02-08T21:31:00.000Z","key":1486589460000,"doc_count":9},{"key_as_string":"2017-02-08T21:32:00.000Z","key":1486589520000,"doc_count":12},{"key_as_string":"2017-02-08T21:33:00.000Z","key":1486589580000,"doc_count":10},{"key_as_string":"2017-02-08T21:34:00.000Z","key":1486589640000,"doc_count":11},{"key_as_string":"2017-02-08T21:35:00.000Z","key":1486589700000,"doc_count":10},{"key_as_string":"2017-02-08T21:36:00.000Z","key":1486589760000,"doc_count":15},{"key_as_string":"2017-02-08T21:37:00.000Z","key":1486589820000,"doc_count":8},{"key_as_string":"2017-02-08T21:38:00.000Z","key":1486589880000,"doc_count":11},{"key_as_string":"2017-02-08T21:39:00.000Z","key":1486589940000,"doc_count":9},{"key_as_string":"2017-02-08T21:40:00.000Z","key":1486590000000,"doc_count":9},{"key_as_string":"2017-02-08T21:41:00.000Z","key":1486590060000,"doc_count":10},{"key_as_string":"2017-02-08T21:42:00.000Z","key":1486590120000,"doc_count":8},{"key_as_string":"2017-02-08T21:43:00.000Z","key":1486590180000,"doc_count":13},{"key_as_string":"2017-02-08T21:44:00.000Z","key":1486590240000,"doc_count":13},{"key_as_string":"2017-02-08T21:45:00.000Z","key":1486590300000,"doc_count":15},{"key_as_string":"2017-02-08T21:46:00.000Z","key":1486590360000,"doc_count":8},{"key_as_string":"2017-02-08T21:47:00.000Z","key":1486590420000,"doc_count":7},{"key_as_string":"2017-02-08T21:48:00.000Z","key":1486590480000,"doc_count":14},{"key_as_string":"2017-02-08T21:49:00.000Z","key":1486590540000,"doc_count":8},{"key_as_string":"2017-02-08T21:50:00.000Z","key":1486590600000,"doc_count":11},{"key_as_string":"2017-02-08T21:51:00.000Z","key":1486590660000,"doc_count":12},{"key_as_string":"2017-02-08T21:52:00.000Z","key":1486590720000,"doc_count":9},{"key_as_string":"2017-02-08T21:53:00.000Z","key":1486590780000,"doc_count":11},{"key_as_string":"2017-02-08T21:54:00.000Z","key":1486590840000,"doc_count":9},{"key_as_string":"2017-02-08T21:55:00.000Z","key":1486590900000,"doc_count":7},{"key_as_string":"2017-02-08T21:56:00.000Z","key":1486590960000,"doc_count":13},{"key_as_string":"2017-02-08T21:57:00.000Z","key":1486591020000,"doc_count":10},{"key_as_string":"2017-02-08T21:58:00.000Z","key":1486591080000,"doc_count":12},{"key_as_string":"2017-02-08T21:59:00.000Z","key":1486591140000,"doc_count":15},{"key_as_string":"2017-02-08T22:00:00.000Z","key":1486591200000,"doc_count":13},{"key_as_string":"2017-02-08T22:01:00.000Z","key":1486591260000,"doc_count":14},{"key_as_string":"2017-02-08T22:02:00.000Z","key":1486591320000,"doc_count":7},{"key_as_string":"2017-02-08T22:03:00.000Z","key":1486591380000,"doc_count":12},{"key_as_string":"2017-02-08T22:04:00.000Z","key":1486591440000,"doc_count":11},{"key_as_string":"2017-02-08T22:05:00.000Z","key":1486591500000,"doc_count":11},{"key_as_string":"2017-02-08T22:06:00.000Z","key":1486591560000,"doc_count":11},{"key_as_string":"2017-02-08T22:07:00.000Z","key":1486591620000,"doc_count":8},{"key_as_string":"2017-02-08T22:08:00.000Z","key":1486591680000,"doc_count":14},{"key_as_string":"2017-02-08T22:09:00.000Z","key":1486591740000,"doc_count":11},{"key_as_string":"2017-02-08T22:10:00.000Z","key":1486591800000,"doc_count":16},{"key_as_string":"2017-02-08T22:11:00.000Z","key":1486591860000,"doc_count":8},{"key_as_string":"2017-02-08T22:12:00.000Z","key":1486591920000,"doc_count":15},{"key_as_string":"2017-02-08T22:13:00.000Z","key":1486591980000,"doc_count":11},{"key_as_string":"2017-02-08T22:14:00.000Z","key":1486592040000,"doc_count":6},{"key_as_string":"2017-02-08T22:15:00.000Z","key":1486592100000,"doc_count":11},{"key_as_string":"2017-02-08T22:16:00.000Z","key":1486592160000,"doc_count":9},{"key_as_string":"2017-02-08T22:17:00.000Z","key":1486592220000,"doc_count":15},{"key_as_string":"2017-02-08T22:18:00.000Z","key":1486592280000,"doc_count":10},{"key_as_string":"2017-02-08T22:19:00.000Z","key":1486592340000,"doc_count":7},{"key_as_string":"2017-02-08T22:20:00.000Z","key":1486592400000,"doc_count":11},{"key_as_string":"2017-02-08T22:21:00.000Z","key":1486592460000,"doc_count":8},{"key_as_string":"2017-02-08T22:22:00.000Z","key":1486592520000,"doc_count":11},{"key_as_string":"2017-02-08T22:23:00.000Z","key":1486592580000,"doc_count":5},{"key_as_string":"2017-02-08T22:24:00.000Z","key":1486592640000,"doc_count":13},{"key_as_string":"2017-02-08T22:25:00.000Z","key":1486592700000,"doc_count":11},{"key_as_string":"2017-02-08T22:26:00.000Z","key":1486592760000,"doc_count":13},{"key_as_string":"2017-02-08T22:27:00.000Z","key":1486592820000,"doc_count":15},{"key_as_string":"2017-02-08T22:28:00.000Z","key":1486592880000,"doc_count":12},{"key_as_string":"2017-02-08T22:29:00.000Z","key":1486592940000,"doc_count":7},{"key_as_string":"2017-02-08T22:30:00.000Z","key":1486593000000,"doc_count":14},{"key_as_string":"2017-02-08T22:31:00.000Z","key":1486593060000,"doc_count":13},{"key_as_string":"2017-02-08T22:32:00.000Z","key":1486593120000,"doc_count":13},{"key_as_string":"2017-02-08T22:33:00.000Z","key":1486593180000,"doc_count":9},{"key_as_string":"2017-02-08T22:34:00.000Z","key":1486593240000,"doc_count":13},{"key_as_string":"2017-02-08T22:35:00.000Z","key":1486593300000,"doc_count":14},{"key_as_string":"2017-02-08T22:36:00.000Z","key":1486593360000,"doc_count":9},{"key_as_string":"2017-02-08T22:37:00.000Z","key":1486593420000,"doc_count":11},{"key_as_string":"2017-02-08T22:38:00.000Z","key":1486593480000,"doc_count":8},{"key_as_string":"2017-02-08T22:39:00.000Z","key":1486593540000,"doc_count":14},{"key_as_string":"2017-02-08T22:40:00.000Z","key":1486593600000,"doc_count":16},{"key_as_string":"2017-02-08T22:41:00.000Z","key":1486593660000,"doc_count":7},{"key_as_string":"2017-02-08T22:42:00.000Z","key":1486593720000,"doc_count":14},{"key_as_string":"2017-02-08T22:43:00.000Z","key":1486593780000,"doc_count":10},{"key_as_string":"2017-02-08T22:44:00.000Z","key":1486593840000,"doc_count":12},{"key_as_string":"2017-02-08T22:45:00.000Z","key":1486593900000,"doc_count":7},{"key_as_string":"2017-02-08T22:46:00.000Z","key":1486593960000,"doc_count":13},{"key_as_string":"2017-02-08T22:47:00.000Z","key":1486594020000,"doc_count":11},{"key_as_string":"2017-02-08T22:48:00.000Z","key":1486594080000,"doc_count":18},{"key_as_string":"2017-02-08T22:49:00.000Z","key":1486594140000,"doc_count":4},{"key_as_string":"2017-02-08T22:50:00.000Z","key":1486594200000,"doc_count":5},{"key_as_string":"2017-02-08T22:51:00.000Z","key":1486594260000,"doc_count":16},{"key_as_string":"2017-02-08T22:52:00.000Z","key":1486594320000,"doc_count":8},{"key_as_string":"2017-02-08T22:53:00.000Z","key":1486594380000,"doc_count":11},{"key_as_string":"2017-02-08T22:54:00.000Z","key":1486594440000,"doc_count":10},{"key_as_string":"2017-02-08T22:55:00.000Z","key":1486594500000,"doc_count":10},{"key_as_string":"2017-02-08T22:56:00.000Z","key":1486594560000,"doc_count":15},{"key_as_string":"2017-02-08T22:57:00.000Z","key":1486594620000,"doc_count":7},{"key_as_string":"2017-02-08T22:58:00.000Z","key":1486594680000,"doc_count":14},{"key_as_string":"2017-02-08T22:59:00.000Z","key":1486594740000,"doc_count":14},{"key_as_string":"2017-02-08T23:00:00.000Z","key":1486594800000,"doc_count":6},{"key_as_string":"2017-02-08T23:01:00.000Z","key":1486594860000,"doc_count":9},{"key_as_string":"2017-02-08T23:02:00.000Z","key":1486594920000,"doc_count":14},{"key_as_string":"2017-02-08T23:03:00.000Z","key":1486594980000,"doc_count":9},{"key_as_string":"2017-02-08T23:04:00.000Z","key":1486595040000,"doc_count":11},{"key_as_string":"2017-02-08T23:05:00.000Z","key":1486595100000,"doc_count":14},{"key_as_string":"2017-02-08T23:06:00.000Z","key":1486595160000,"doc_count":6},{"key_as_string":"2017-02-08T23:07:00.000Z","key":1486595220000,"doc_count":10},{"key_as_string":"2017-02-08T23:08:00.000Z","key":1486595280000,"doc_count":9},{"key_as_string":"2017-02-08T23:09:00.000Z","key":1486595340000,"doc_count":5},{"key_as_string":"2017-02-08T23:10:00.000Z","key":1486595400000,"doc_count":12},{"key_as_string":"2017-02-08T23:11:00.000Z","key":1486595460000,"doc_count":11},{"key_as_string":"2017-02-08T23:12:00.000Z","key":1486595520000,"doc_count":8},{"key_as_string":"2017-02-08T23:13:00.000Z","key":1486595580000,"doc_count":10},{"key_as_string":"2017-02-08T23:14:00.000Z","key":1486595640000,"doc_count":14},{"key_as_string":"2017-02-08T23:15:00.000Z","key":1486595700000,"doc_count":6},{"key_as_string":"2017-02-08T23:16:00.000Z","key":1486595760000,"doc_count":9},{"key_as_string":"2017-02-08T23:17:00.000Z","key":1486595820000,"doc_count":12},{"key_as_string":"2017-02-08T23:18:00.000Z","key":1486595880000,"doc_count":12},{"key_as_string":"2017-02-08T23:19:00.000Z","key":1486595940000,"doc_count":5},{"key_as_string":"2017-02-08T23:20:00.000Z","key":1486596000000,"doc_count":12},{"key_as_string":"2017-02-08T23:21:00.000Z","key":1486596060000,"doc_count":10},{"key_as_string":"2017-02-08T23:22:00.000Z","key":1486596120000,"doc_count":13},{"key_as_string":"2017-02-08T23:23:00.000Z","key":1486596180000,"doc_count":10},{"key_as_string":"2017-02-08T23:24:00.000Z","key":1486596240000,"doc_count":8},{"key_as_string":"2017-02-08T23:25:00.000Z","key":1486596300000,"doc_count":11},{"key_as_string":"2017-02-08T23:26:00.000Z","key":1486596360000,"doc_count":11},{"key_as_string":"2017-02-08T23:27:00.000Z","key":1486596420000,"doc_count":5},{"key_as_string":"2017-02-08T23:28:00.000Z","key":1486596480000,"doc_count":9},{"key_as_string":"2017-02-08T23:29:00.000Z","key":1486596540000,"doc_count":12},{"key_as_string":"2017-02-08T23:30:00.000Z","key":1486596600000,"doc_count":7},{"key_as_string":"2017-02-08T23:31:00.000Z","key":1486596660000,"doc_count":14},{"key_as_string":"2017-02-08T23:32:00.000Z","key":1486596720000,"doc_count":12},{"key_as_string":"2017-02-08T23:33:00.000Z","key":1486596780000,"doc_count":12},{"key_as_string":"2017-02-08T23:34:00.000Z","key":1486596840000,"doc_count":18},{"key_as_string":"2017-02-08T23:35:00.000Z","key":1486596900000,"doc_count":6},{"key_as_string":"2017-02-08T23:36:00.000Z","key":1486596960000,"doc_count":7},{"key_as_string":"2017-02-08T23:37:00.000Z","key":1486597020000,"doc_count":12},{"key_as_string":"2017-02-08T23:38:00.000Z","key":1486597080000,"doc_count":11},{"key_as_string":"2017-02-08T23:39:00.000Z","key":1486597140000,"doc_count":7},{"key_as_string":"2017-02-08T23:40:00.000Z","key":1486597200000,"doc_count":12},{"key_as_string":"2017-02-08T23:41:00.000Z","key":1486597260000,"doc_count":16},{"key_as_string":"2017-02-08T23:42:00.000Z","key":1486597320000,"doc_count":8},{"key_as_string":"2017-02-08T23:43:00.000Z","key":1486597380000,"doc_count":11},{"key_as_string":"2017-02-08T23:44:00.000Z","key":1486597440000,"doc_count":8},{"key_as_string":"2017-02-08T23:45:00.000Z","key":1486597500000,"doc_count":11},{"key_as_string":"2017-02-08T23:46:00.000Z","key":1486597560000,"doc_count":11},{"key_as_string":"2017-02-08T23:47:00.000Z","key":1486597620000,"doc_count":9},{"key_as_string":"2017-02-08T23:48:00.000Z","key":1486597680000,"doc_count":7},{"key_as_string":"2017-02-08T23:49:00.000Z","key":1486597740000,"doc_count":11},{"key_as_string":"2017-02-08T23:50:00.000Z","key":1486597800000,"doc_count":11},{"key_as_string":"2017-02-08T23:51:00.000Z","key":1486597860000,"doc_count":8},{"key_as_string":"2017-02-08T23:52:00.000Z","key":1486597920000,"doc_count":11},{"key_as_string":"2017-02-08T23:53:00.000Z","key":1486597980000,"doc_count":10},{"key_as_string":"2017-02-08T23:54:00.000Z","key":1486598040000,"doc_count":11},{"key_as_string":"2017-02-08T23:55:00.000Z","key":1486598100000,"doc_count":9},{"key_as_string":"2017-02-08T23:56:00.000Z","key":1486598160000,"doc_count":6},{"key_as_string":"2017-02-08T23:57:00.000Z","key":1486598220000,"doc_count":13},{"key_as_string":"2017-02-08T23:58:00.000Z","key":1486598280000,"doc_count":10},{"key_as_string":"2017-02-08T23:59:00.000Z","key":1486598340000,"doc_count":9},{"key_as_string":"2017-02-09T00:00:00.000Z","key":1486598400000,"doc_count":14},{"key_as_string":"2017-02-09T00:01:00.000Z","key":1486598460000,"doc_count":7},{"key_as_string":"2017-02-09T00:02:00.000Z","key":1486598520000,"doc_count":11},{"key_as_string":"2017-02-09T00:03:00.000Z","key":1486598580000,"doc_count":14},{"key_as_string":"2017-02-09T00:04:00.000Z","key":1486598640000,"doc_count":7},{"key_as_string":"2017-02-09T00:05:00.000Z","key":1486598700000,"doc_count":15},{"key_as_string":"2017-02-09T00:06:00.000Z","key":1486598760000,"doc_count":4},{"key_as_string":"2017-02-09T00:07:00.000Z","key":1486598820000,"doc_count":14},{"key_as_string":"2017-02-09T00:08:00.000Z","key":1486598880000,"doc_count":11},{"key_as_string":"2017-02-09T00:09:00.000Z","key":1486598940000,"doc_count":8},{"key_as_string":"2017-02-09T00:10:00.000Z","key":1486599000000,"doc_count":9},{"key_as_string":"2017-02-09T00:11:00.000Z","key":1486599060000,"doc_count":13},{"key_as_string":"2017-02-09T00:12:00.000Z","key":1486599120000,"doc_count":8},{"key_as_string":"2017-02-09T00:13:00.000Z","key":1486599180000,"doc_count":9},{"key_as_string":"2017-02-09T00:14:00.000Z","key":1486599240000,"doc_count":14},{"key_as_string":"2017-02-09T00:15:00.000Z","key":1486599300000,"doc_count":13},{"key_as_string":"2017-02-09T00:16:00.000Z","key":1486599360000,"doc_count":8},{"key_as_string":"2017-02-09T00:17:00.000Z","key":1486599420000,"doc_count":6},{"key_as_string":"2017-02-09T00:18:00.000Z","key":1486599480000,"doc_count":14},{"key_as_string":"2017-02-09T00:19:00.000Z","key":1486599540000,"doc_count":15},{"key_as_string":"2017-02-09T00:20:00.000Z","key":1486599600000,"doc_count":9},{"key_as_string":"2017-02-09T00:21:00.000Z","key":1486599660000,"doc_count":6},{"key_as_string":"2017-02-09T00:22:00.000Z","key":1486599720000,"doc_count":9},{"key_as_string":"2017-02-09T00:23:00.000Z","key":1486599780000,"doc_count":15},{"key_as_string":"2017-02-09T00:24:00.000Z","key":1486599840000,"doc_count":10},{"key_as_string":"2017-02-09T00:25:00.000Z","key":1486599900000,"doc_count":9},{"key_as_string":"2017-02-09T00:26:00.000Z","key":1486599960000,"doc_count":11},{"key_as_string":"2017-02-09T00:27:00.000Z","key":1486600020000,"doc_count":10},{"key_as_string":"2017-02-09T00:28:00.000Z","key":1486600080000,"doc_count":11},{"key_as_string":"2017-02-09T00:29:00.000Z","key":1486600140000,"doc_count":8},{"key_as_string":"2017-02-09T00:30:00.000Z","key":1486600200000,"doc_count":5},{"key_as_string":"2017-02-09T00:31:00.000Z","key":1486600260000,"doc_count":15},{"key_as_string":"2017-02-09T00:32:00.000Z","key":1486600320000,"doc_count":5},{"key_as_string":"2017-02-09T00:33:00.000Z","key":1486600380000,"doc_count":12},{"key_as_string":"2017-02-09T00:34:00.000Z","key":1486600440000,"doc_count":11},{"key_as_string":"2017-02-09T00:35:00.000Z","key":1486600500000,"doc_count":8},{"key_as_string":"2017-02-09T00:36:00.000Z","key":1486600560000,"doc_count":10},{"key_as_string":"2017-02-09T00:37:00.000Z","key":1486600620000,"doc_count":11},{"key_as_string":"2017-02-09T00:38:00.000Z","key":1486600680000,"doc_count":11},{"key_as_string":"2017-02-09T00:39:00.000Z","key":1486600740000,"doc_count":7},{"key_as_string":"2017-02-09T00:40:00.000Z","key":1486600800000,"doc_count":8},{"key_as_string":"2017-02-09T00:41:00.000Z","key":1486600860000,"doc_count":10},{"key_as_string":"2017-02-09T00:42:00.000Z","key":1486600920000,"doc_count":11},{"key_as_string":"2017-02-09T00:43:00.000Z","key":1486600980000,"doc_count":10},{"key_as_string":"2017-02-09T00:44:00.000Z","key":1486601040000,"doc_count":9},{"key_as_string":"2017-02-09T00:45:00.000Z","key":1486601100000,"doc_count":10},{"key_as_string":"2017-02-09T00:46:00.000Z","key":1486601160000,"doc_count":9},{"key_as_string":"2017-02-09T00:47:00.000Z","key":1486601220000,"doc_count":9},{"key_as_string":"2017-02-09T00:48:00.000Z","key":1486601280000,"doc_count":11},{"key_as_string":"2017-02-09T00:49:00.000Z","key":1486601340000,"doc_count":6},{"key_as_string":"2017-02-09T00:50:00.000Z","key":1486601400000,"doc_count":13},{"key_as_string":"2017-02-09T00:51:00.000Z","key":1486601460000,"doc_count":8},{"key_as_string":"2017-02-09T00:52:00.000Z","key":1486601520000,"doc_count":9},{"key_as_string":"2017-02-09T00:53:00.000Z","key":1486601580000,"doc_count":12},{"key_as_string":"2017-02-09T00:54:00.000Z","key":1486601640000,"doc_count":11},{"key_as_string":"2017-02-09T00:55:00.000Z","key":1486601700000,"doc_count":7},{"key_as_string":"2017-02-09T00:56:00.000Z","key":1486601760000,"doc_count":16},{"key_as_string":"2017-02-09T00:57:00.000Z","key":1486601820000,"doc_count":11},{"key_as_string":"2017-02-09T00:58:00.000Z","key":1486601880000,"doc_count":7},{"key_as_string":"2017-02-09T00:59:00.000Z","key":1486601940000,"doc_count":9},{"key_as_string":"2017-02-09T01:00:00.000Z","key":1486602000000,"doc_count":14},{"key_as_string":"2017-02-09T01:01:00.000Z","key":1486602060000,"doc_count":9},{"key_as_string":"2017-02-09T01:02:00.000Z","key":1486602120000,"doc_count":15},{"key_as_string":"2017-02-09T01:03:00.000Z","key":1486602180000,"doc_count":10},{"key_as_string":"2017-02-09T01:04:00.000Z","key":1486602240000,"doc_count":14},{"key_as_string":"2017-02-09T01:05:00.000Z","key":1486602300000,"doc_count":8},{"key_as_string":"2017-02-09T01:06:00.000Z","key":1486602360000,"doc_count":13},{"key_as_string":"2017-02-09T01:07:00.000Z","key":1486602420000,"doc_count":10},{"key_as_string":"2017-02-09T01:08:00.000Z","key":1486602480000,"doc_count":7},{"key_as_string":"2017-02-09T01:09:00.000Z","key":1486602540000,"doc_count":15},{"key_as_string":"2017-02-09T01:10:00.000Z","key":1486602600000,"doc_count":10},{"key_as_string":"2017-02-09T01:11:00.000Z","key":1486602660000,"doc_count":7},{"key_as_string":"2017-02-09T01:12:00.000Z","key":1486602720000,"doc_count":14},{"key_as_string":"2017-02-09T01:13:00.000Z","key":1486602780000,"doc_count":11},{"key_as_string":"2017-02-09T01:14:00.000Z","key":1486602840000,"doc_count":10},{"key_as_string":"2017-02-09T01:15:00.000Z","key":1486602900000,"doc_count":12},{"key_as_string":"2017-02-09T01:16:00.000Z","key":1486602960000,"doc_count":10},{"key_as_string":"2017-02-09T01:17:00.000Z","key":1486603020000,"doc_count":9},{"key_as_string":"2017-02-09T01:18:00.000Z","key":1486603080000,"doc_count":12},{"key_as_string":"2017-02-09T01:19:00.000Z","key":1486603140000,"doc_count":6},{"key_as_string":"2017-02-09T01:20:00.000Z","key":1486603200000,"doc_count":12},{"key_as_string":"2017-02-09T01:21:00.000Z","key":1486603260000,"doc_count":7},{"key_as_string":"2017-02-09T01:22:00.000Z","key":1486603320000,"doc_count":11},{"key_as_string":"2017-02-09T01:23:00.000Z","key":1486603380000,"doc_count":6},{"key_as_string":"2017-02-09T01:24:00.000Z","key":1486603440000,"doc_count":14},{"key_as_string":"2017-02-09T01:25:00.000Z","key":1486603500000,"doc_count":6},{"key_as_string":"2017-02-09T01:26:00.000Z","key":1486603560000,"doc_count":9},{"key_as_string":"2017-02-09T01:27:00.000Z","key":1486603620000,"doc_count":14},{"key_as_string":"2017-02-09T01:28:00.000Z","key":1486603680000,"doc_count":9},{"key_as_string":"2017-02-09T01:29:00.000Z","key":1486603740000,"doc_count":13},{"key_as_string":"2017-02-09T01:30:00.000Z","key":1486603800000,"doc_count":9},{"key_as_string":"2017-02-09T01:31:00.000Z","key":1486603860000,"doc_count":11},{"key_as_string":"2017-02-09T01:32:00.000Z","key":1486603920000,"doc_count":13},{"key_as_string":"2017-02-09T01:33:00.000Z","key":1486603980000,"doc_count":6},{"key_as_string":"2017-02-09T01:34:00.000Z","key":1486604040000,"doc_count":13},{"key_as_string":"2017-02-09T01:35:00.000Z","key":1486604100000,"doc_count":7},{"key_as_string":"2017-02-09T01:36:00.000Z","key":1486604160000,"doc_count":11},{"key_as_string":"2017-02-09T01:37:00.000Z","key":1486604220000,"doc_count":10},{"key_as_string":"2017-02-09T01:38:00.000Z","key":1486604280000,"doc_count":6},{"key_as_string":"2017-02-09T01:39:00.000Z","key":1486604340000,"doc_count":14},{"key_as_string":"2017-02-09T01:40:00.000Z","key":1486604400000,"doc_count":5},{"key_as_string":"2017-02-09T01:41:00.000Z","key":1486604460000,"doc_count":8},{"key_as_string":"2017-02-09T01:42:00.000Z","key":1486604520000,"doc_count":12},{"key_as_string":"2017-02-09T01:43:00.000Z","key":1486604580000,"doc_count":16},{"key_as_string":"2017-02-09T01:44:00.000Z","key":1486604640000,"doc_count":8},{"key_as_string":"2017-02-09T01:45:00.000Z","key":1486604700000,"doc_count":5},{"key_as_string":"2017-02-09T01:46:00.000Z","key":1486604760000,"doc_count":13},{"key_as_string":"2017-02-09T01:47:00.000Z","key":1486604820000,"doc_count":7},{"key_as_string":"2017-02-09T01:48:00.000Z","key":1486604880000,"doc_count":8},{"key_as_string":"2017-02-09T01:49:00.000Z","key":1486604940000,"doc_count":17},{"key_as_string":"2017-02-09T01:50:00.000Z","key":1486605000000,"doc_count":7},{"key_as_string":"2017-02-09T01:51:00.000Z","key":1486605060000,"doc_count":10},{"key_as_string":"2017-02-09T01:52:00.000Z","key":1486605120000,"doc_count":13},{"key_as_string":"2017-02-09T01:53:00.000Z","key":1486605180000,"doc_count":15},{"key_as_string":"2017-02-09T01:54:00.000Z","key":1486605240000,"doc_count":12},{"key_as_string":"2017-02-09T01:55:00.000Z","key":1486605300000,"doc_count":6},{"key_as_string":"2017-02-09T01:56:00.000Z","key":1486605360000,"doc_count":14},{"key_as_string":"2017-02-09T01:57:00.000Z","key":1486605420000,"doc_count":13},{"key_as_string":"2017-02-09T01:58:00.000Z","key":1486605480000,"doc_count":12},{"key_as_string":"2017-02-09T01:59:00.000Z","key":1486605540000,"doc_count":11},{"key_as_string":"2017-02-09T02:00:00.000Z","key":1486605600000,"doc_count":12},{"key_as_string":"2017-02-09T02:01:00.000Z","key":1486605660000,"doc_count":15},{"key_as_string":"2017-02-09T02:02:00.000Z","key":1486605720000,"doc_count":6},{"key_as_string":"2017-02-09T02:03:00.000Z","key":1486605780000,"doc_count":15},{"key_as_string":"2017-02-09T02:04:00.000Z","key":1486605840000,"doc_count":4},{"key_as_string":"2017-02-09T02:05:00.000Z","key":1486605900000,"doc_count":14},{"key_as_string":"2017-02-09T02:06:00.000Z","key":1486605960000,"doc_count":15},{"key_as_string":"2017-02-09T02:07:00.000Z","key":1486606020000,"doc_count":8},{"key_as_string":"2017-02-09T02:08:00.000Z","key":1486606080000,"doc_count":9},{"key_as_string":"2017-02-09T02:09:00.000Z","key":1486606140000,"doc_count":13},{"key_as_string":"2017-02-09T02:10:00.000Z","key":1486606200000,"doc_count":8},{"key_as_string":"2017-02-09T02:11:00.000Z","key":1486606260000,"doc_count":12},{"key_as_string":"2017-02-09T02:12:00.000Z","key":1486606320000,"doc_count":12},{"key_as_string":"2017-02-09T02:13:00.000Z","key":1486606380000,"doc_count":16},{"key_as_string":"2017-02-09T02:14:00.000Z","key":1486606440000,"doc_count":9},{"key_as_string":"2017-02-09T02:15:00.000Z","key":1486606500000,"doc_count":10},{"key_as_string":"2017-02-09T02:16:00.000Z","key":1486606560000,"doc_count":12},{"key_as_string":"2017-02-09T02:17:00.000Z","key":1486606620000,"doc_count":11},{"key_as_string":"2017-02-09T02:18:00.000Z","key":1486606680000,"doc_count":10},{"key_as_string":"2017-02-09T02:19:00.000Z","key":1486606740000,"doc_count":10},{"key_as_string":"2017-02-09T02:20:00.000Z","key":1486606800000,"doc_count":9},{"key_as_string":"2017-02-09T02:21:00.000Z","key":1486606860000,"doc_count":16},{"key_as_string":"2017-02-09T02:22:00.000Z","key":1486606920000,"doc_count":11},{"key_as_string":"2017-02-09T02:23:00.000Z","key":1486606980000,"doc_count":10},{"key_as_string":"2017-02-09T02:24:00.000Z","key":1486607040000,"doc_count":10},{"key_as_string":"2017-02-09T02:25:00.000Z","key":1486607100000,"doc_count":11},{"key_as_string":"2017-02-09T02:26:00.000Z","key":1486607160000,"doc_count":7},{"key_as_string":"2017-02-09T02:27:00.000Z","key":1486607220000,"doc_count":12},{"key_as_string":"2017-02-09T02:28:00.000Z","key":1486607280000,"doc_count":9},{"key_as_string":"2017-02-09T02:29:00.000Z","key":1486607340000,"doc_count":6},{"key_as_string":"2017-02-09T02:30:00.000Z","key":1486607400000,"doc_count":10},{"key_as_string":"2017-02-09T02:31:00.000Z","key":1486607460000,"doc_count":8},{"key_as_string":"2017-02-09T02:32:00.000Z","key":1486607520000,"doc_count":14},{"key_as_string":"2017-02-09T02:33:00.000Z","key":1486607580000,"doc_count":9},{"key_as_string":"2017-02-09T02:34:00.000Z","key":1486607640000,"doc_count":10},{"key_as_string":"2017-02-09T02:35:00.000Z","key":1486607700000,"doc_count":9},{"key_as_string":"2017-02-09T02:36:00.000Z","key":1486607760000,"doc_count":11},{"key_as_string":"2017-02-09T02:37:00.000Z","key":1486607820000,"doc_count":11},{"key_as_string":"2017-02-09T02:38:00.000Z","key":1486607880000,"doc_count":6},{"key_as_string":"2017-02-09T02:39:00.000Z","key":1486607940000,"doc_count":20},{"key_as_string":"2017-02-09T02:40:00.000Z","key":1486608000000,"doc_count":11},{"key_as_string":"2017-02-09T02:41:00.000Z","key":1486608060000,"doc_count":11},{"key_as_string":"2017-02-09T02:42:00.000Z","key":1486608120000,"doc_count":11},{"key_as_string":"2017-02-09T02:43:00.000Z","key":1486608180000,"doc_count":5},{"key_as_string":"2017-02-09T02:44:00.000Z","key":1486608240000,"doc_count":10},{"key_as_string":"2017-02-09T02:45:00.000Z","key":1486608300000,"doc_count":10},{"key_as_string":"2017-02-09T02:46:00.000Z","key":1486608360000,"doc_count":14},{"key_as_string":"2017-02-09T02:47:00.000Z","key":1486608420000,"doc_count":10},{"key_as_string":"2017-02-09T02:48:00.000Z","key":1486608480000,"doc_count":13},{"key_as_string":"2017-02-09T02:49:00.000Z","key":1486608540000,"doc_count":9},{"key_as_string":"2017-02-09T02:50:00.000Z","key":1486608600000,"doc_count":10},{"key_as_string":"2017-02-09T02:51:00.000Z","key":1486608660000,"doc_count":9},{"key_as_string":"2017-02-09T02:52:00.000Z","key":1486608720000,"doc_count":13},{"key_as_string":"2017-02-09T02:53:00.000Z","key":1486608780000,"doc_count":16},{"key_as_string":"2017-02-09T02:54:00.000Z","key":1486608840000,"doc_count":5},{"key_as_string":"2017-02-09T02:55:00.000Z","key":1486608900000,"doc_count":8},{"key_as_string":"2017-02-09T02:56:00.000Z","key":1486608960000,"doc_count":13},{"key_as_string":"2017-02-09T02:57:00.000Z","key":1486609020000,"doc_count":10},{"key_as_string":"2017-02-09T02:58:00.000Z","key":1486609080000,"doc_count":11},{"key_as_string":"2017-02-09T02:59:00.000Z","key":1486609140000,"doc_count":9},{"key_as_string":"2017-02-09T03:00:00.000Z","key":1486609200000,"doc_count":14},{"key_as_string":"2017-02-09T03:01:00.000Z","key":1486609260000,"doc_count":7},{"key_as_string":"2017-02-09T03:02:00.000Z","key":1486609320000,"doc_count":9},{"key_as_string":"2017-02-09T03:03:00.000Z","key":1486609380000,"doc_count":11},{"key_as_string":"2017-02-09T03:04:00.000Z","key":1486609440000,"doc_count":21},{"key_as_string":"2017-02-09T03:05:00.000Z","key":1486609500000,"doc_count":12},{"key_as_string":"2017-02-09T03:06:00.000Z","key":1486609560000,"doc_count":14},{"key_as_string":"2017-02-09T03:07:00.000Z","key":1486609620000,"doc_count":9},{"key_as_string":"2017-02-09T03:08:00.000Z","key":1486609680000,"doc_count":12},{"key_as_string":"2017-02-09T03:09:00.000Z","key":1486609740000,"doc_count":12},{"key_as_string":"2017-02-09T03:10:00.000Z","key":1486609800000,"doc_count":10},{"key_as_string":"2017-02-09T03:11:00.000Z","key":1486609860000,"doc_count":7},{"key_as_string":"2017-02-09T03:12:00.000Z","key":1486609920000,"doc_count":11},{"key_as_string":"2017-02-09T03:13:00.000Z","key":1486609980000,"doc_count":9},{"key_as_string":"2017-02-09T03:14:00.000Z","key":1486610040000,"doc_count":5},{"key_as_string":"2017-02-09T03:15:00.000Z","key":1486610100000,"doc_count":14},{"key_as_string":"2017-02-09T03:16:00.000Z","key":1486610160000,"doc_count":7},{"key_as_string":"2017-02-09T03:17:00.000Z","key":1486610220000,"doc_count":11},{"key_as_string":"2017-02-09T03:18:00.000Z","key":1486610280000,"doc_count":14},{"key_as_string":"2017-02-09T03:19:00.000Z","key":1486610340000,"doc_count":12},{"key_as_string":"2017-02-09T03:20:00.000Z","key":1486610400000,"doc_count":7},{"key_as_string":"2017-02-09T03:21:00.000Z","key":1486610460000,"doc_count":15},{"key_as_string":"2017-02-09T03:22:00.000Z","key":1486610520000,"doc_count":12},{"key_as_string":"2017-02-09T03:23:00.000Z","key":1486610580000,"doc_count":10},{"key_as_string":"2017-02-09T03:24:00.000Z","key":1486610640000,"doc_count":6},{"key_as_string":"2017-02-09T03:25:00.000Z","key":1486610700000,"doc_count":11},{"key_as_string":"2017-02-09T03:26:00.000Z","key":1486610760000,"doc_count":10},{"key_as_string":"2017-02-09T03:27:00.000Z","key":1486610820000,"doc_count":11},{"key_as_string":"2017-02-09T03:28:00.000Z","key":1486610880000,"doc_count":12},{"key_as_string":"2017-02-09T03:29:00.000Z","key":1486610940000,"doc_count":11},{"key_as_string":"2017-02-09T03:30:00.000Z","key":1486611000000,"doc_count":15},{"key_as_string":"2017-02-09T03:31:00.000Z","key":1486611060000,"doc_count":10},{"key_as_string":"2017-02-09T03:32:00.000Z","key":1486611120000,"doc_count":14},{"key_as_string":"2017-02-09T03:33:00.000Z","key":1486611180000,"doc_count":10},{"key_as_string":"2017-02-09T03:34:00.000Z","key":1486611240000,"doc_count":10},{"key_as_string":"2017-02-09T03:35:00.000Z","key":1486611300000,"doc_count":15},{"key_as_string":"2017-02-09T03:36:00.000Z","key":1486611360000,"doc_count":8},{"key_as_string":"2017-02-09T03:37:00.000Z","key":1486611420000,"doc_count":8},{"key_as_string":"2017-02-09T03:38:00.000Z","key":1486611480000,"doc_count":15},{"key_as_string":"2017-02-09T03:39:00.000Z","key":1486611540000,"doc_count":7},{"key_as_string":"2017-02-09T03:40:00.000Z","key":1486611600000,"doc_count":15},{"key_as_string":"2017-02-09T03:41:00.000Z","key":1486611660000,"doc_count":7},{"key_as_string":"2017-02-09T03:42:00.000Z","key":1486611720000,"doc_count":14},{"key_as_string":"2017-02-09T03:43:00.000Z","key":1486611780000,"doc_count":8},{"key_as_string":"2017-02-09T03:44:00.000Z","key":1486611840000,"doc_count":9},{"key_as_string":"2017-02-09T03:45:00.000Z","key":1486611900000,"doc_count":11},{"key_as_string":"2017-02-09T03:46:00.000Z","key":1486611960000,"doc_count":15},{"key_as_string":"2017-02-09T03:47:00.000Z","key":1486612020000,"doc_count":6},{"key_as_string":"2017-02-09T03:48:00.000Z","key":1486612080000,"doc_count":9},{"key_as_string":"2017-02-09T03:49:00.000Z","key":1486612140000,"doc_count":13},{"key_as_string":"2017-02-09T03:50:00.000Z","key":1486612200000,"doc_count":9},{"key_as_string":"2017-02-09T03:51:00.000Z","key":1486612260000,"doc_count":11},{"key_as_string":"2017-02-09T03:52:00.000Z","key":1486612320000,"doc_count":12},{"key_as_string":"2017-02-09T03:53:00.000Z","key":1486612380000,"doc_count":13},{"key_as_string":"2017-02-09T03:54:00.000Z","key":1486612440000,"doc_count":7},{"key_as_string":"2017-02-09T03:55:00.000Z","key":1486612500000,"doc_count":14},{"key_as_string":"2017-02-09T03:56:00.000Z","key":1486612560000,"doc_count":10},{"key_as_string":"2017-02-09T03:57:00.000Z","key":1486612620000,"doc_count":8},{"key_as_string":"2017-02-09T03:58:00.000Z","key":1486612680000,"doc_count":6},{"key_as_string":"2017-02-09T03:59:00.000Z","key":1486612740000,"doc_count":11},{"key_as_string":"2017-02-09T04:00:00.000Z","key":1486612800000,"doc_count":8},{"key_as_string":"2017-02-09T04:01:00.000Z","key":1486612860000,"doc_count":12},{"key_as_string":"2017-02-09T04:02:00.000Z","key":1486612920000,"doc_count":13},{"key_as_string":"2017-02-09T04:03:00.000Z","key":1486612980000,"doc_count":10},{"key_as_string":"2017-02-09T04:04:00.000Z","key":1486613040000,"doc_count":12},{"key_as_string":"2017-02-09T04:05:00.000Z","key":1486613100000,"doc_count":9},{"key_as_string":"2017-02-09T04:06:00.000Z","key":1486613160000,"doc_count":16},{"key_as_string":"2017-02-09T04:07:00.000Z","key":1486613220000,"doc_count":12},{"key_as_string":"2017-02-09T04:08:00.000Z","key":1486613280000,"doc_count":16},{"key_as_string":"2017-02-09T04:09:00.000Z","key":1486613340000,"doc_count":10},{"key_as_string":"2017-02-09T04:10:00.000Z","key":1486613400000,"doc_count":9},{"key_as_string":"2017-02-09T04:11:00.000Z","key":1486613460000,"doc_count":9},{"key_as_string":"2017-02-09T04:12:00.000Z","key":1486613520000,"doc_count":11},{"key_as_string":"2017-02-09T04:13:00.000Z","key":1486613580000,"doc_count":8},{"key_as_string":"2017-02-09T04:14:00.000Z","key":1486613640000,"doc_count":11},{"key_as_string":"2017-02-09T04:15:00.000Z","key":1486613700000,"doc_count":7},{"key_as_string":"2017-02-09T04:16:00.000Z","key":1486613760000,"doc_count":9},{"key_as_string":"2017-02-09T04:17:00.000Z","key":1486613820000,"doc_count":14},{"key_as_string":"2017-02-09T04:18:00.000Z","key":1486613880000,"doc_count":12},{"key_as_string":"2017-02-09T04:19:00.000Z","key":1486613940000,"doc_count":12},{"key_as_string":"2017-02-09T04:20:00.000Z","key":1486614000000,"doc_count":11},{"key_as_string":"2017-02-09T04:21:00.000Z","key":1486614060000,"doc_count":15},{"key_as_string":"2017-02-09T04:22:00.000Z","key":1486614120000,"doc_count":15},{"key_as_string":"2017-02-09T04:23:00.000Z","key":1486614180000,"doc_count":14},{"key_as_string":"2017-02-09T04:24:00.000Z","key":1486614240000,"doc_count":10},{"key_as_string":"2017-02-09T04:25:00.000Z","key":1486614300000,"doc_count":8},{"key_as_string":"2017-02-09T04:26:00.000Z","key":1486614360000,"doc_count":8},{"key_as_string":"2017-02-09T04:27:00.000Z","key":1486614420000,"doc_count":14},{"key_as_string":"2017-02-09T04:28:00.000Z","key":1486614480000,"doc_count":9},{"key_as_string":"2017-02-09T04:29:00.000Z","key":1486614540000,"doc_count":16},{"key_as_string":"2017-02-09T04:30:00.000Z","key":1486614600000,"doc_count":11},{"key_as_string":"2017-02-09T04:31:00.000Z","key":1486614660000,"doc_count":13},{"key_as_string":"2017-02-09T04:32:00.000Z","key":1486614720000,"doc_count":9},{"key_as_string":"2017-02-09T04:33:00.000Z","key":1486614780000,"doc_count":16},{"key_as_string":"2017-02-09T04:34:00.000Z","key":1486614840000,"doc_count":11},{"key_as_string":"2017-02-09T04:35:00.000Z","key":1486614900000,"doc_count":9},{"key_as_string":"2017-02-09T04:36:00.000Z","key":1486614960000,"doc_count":11},{"key_as_string":"2017-02-09T04:37:00.000Z","key":1486615020000,"doc_count":14},{"key_as_string":"2017-02-09T04:38:00.000Z","key":1486615080000,"doc_count":14},{"key_as_string":"2017-02-09T04:39:00.000Z","key":1486615140000,"doc_count":10},{"key_as_string":"2017-02-09T04:40:00.000Z","key":1486615200000,"doc_count":7},{"key_as_string":"2017-02-09T04:41:00.000Z","key":1486615260000,"doc_count":13},{"key_as_string":"2017-02-09T04:42:00.000Z","key":1486615320000,"doc_count":10},{"key_as_string":"2017-02-09T04:43:00.000Z","key":1486615380000,"doc_count":17},{"key_as_string":"2017-02-09T04:44:00.000Z","key":1486615440000,"doc_count":12},{"key_as_string":"2017-02-09T04:45:00.000Z","key":1486615500000,"doc_count":9},{"key_as_string":"2017-02-09T04:46:00.000Z","key":1486615560000,"doc_count":7},{"key_as_string":"2017-02-09T04:47:00.000Z","key":1486615620000,"doc_count":14},{"key_as_string":"2017-02-09T04:48:00.000Z","key":1486615680000,"doc_count":11},{"key_as_string":"2017-02-09T04:49:00.000Z","key":1486615740000,"doc_count":15},{"key_as_string":"2017-02-09T04:50:00.000Z","key":1486615800000,"doc_count":12},{"key_as_string":"2017-02-09T04:51:00.000Z","key":1486615860000,"doc_count":11},{"key_as_string":"2017-02-09T04:52:00.000Z","key":1486615920000,"doc_count":10},{"key_as_string":"2017-02-09T04:53:00.000Z","key":1486615980000,"doc_count":10},{"key_as_string":"2017-02-09T04:54:00.000Z","key":1486616040000,"doc_count":12},{"key_as_string":"2017-02-09T04:55:00.000Z","key":1486616100000,"doc_count":10},{"key_as_string":"2017-02-09T04:56:00.000Z","key":1486616160000,"doc_count":13},{"key_as_string":"2017-02-09T04:57:00.000Z","key":1486616220000,"doc_count":10},{"key_as_string":"2017-02-09T04:58:00.000Z","key":1486616280000,"doc_count":12},{"key_as_string":"2017-02-09T04:59:00.000Z","key":1486616340000,"doc_count":13},{"key_as_string":"2017-02-09T05:00:00.000Z","key":1486616400000,"doc_count":11},{"key_as_string":"2017-02-09T05:01:00.000Z","key":1486616460000,"doc_count":14},{"key_as_string":"2017-02-09T05:02:00.000Z","key":1486616520000,"doc_count":10},{"key_as_string":"2017-02-09T05:03:00.000Z","key":1486616580000,"doc_count":13},{"key_as_string":"2017-02-09T05:04:00.000Z","key":1486616640000,"doc_count":11},{"key_as_string":"2017-02-09T05:05:00.000Z","key":1486616700000,"doc_count":11},{"key_as_string":"2017-02-09T05:06:00.000Z","key":1486616760000,"doc_count":12},{"key_as_string":"2017-02-09T05:07:00.000Z","key":1486616820000,"doc_count":21},{"key_as_string":"2017-02-09T05:08:00.000Z","key":1486616880000,"doc_count":7},{"key_as_string":"2017-02-09T05:09:00.000Z","key":1486616940000,"doc_count":14},{"key_as_string":"2017-02-09T05:10:00.000Z","key":1486617000000,"doc_count":9},{"key_as_string":"2017-02-09T05:11:00.000Z","key":1486617060000,"doc_count":7},{"key_as_string":"2017-02-09T05:12:00.000Z","key":1486617120000,"doc_count":15},{"key_as_string":"2017-02-09T05:13:00.000Z","key":1486617180000,"doc_count":12},{"key_as_string":"2017-02-09T05:14:00.000Z","key":1486617240000,"doc_count":15},{"key_as_string":"2017-02-09T05:15:00.000Z","key":1486617300000,"doc_count":12},{"key_as_string":"2017-02-09T05:16:00.000Z","key":1486617360000,"doc_count":15},{"key_as_string":"2017-02-09T05:17:00.000Z","key":1486617420000,"doc_count":11},{"key_as_string":"2017-02-09T05:18:00.000Z","key":1486617480000,"doc_count":16},{"key_as_string":"2017-02-09T05:19:00.000Z","key":1486617540000,"doc_count":8},{"key_as_string":"2017-02-09T05:20:00.000Z","key":1486617600000,"doc_count":12},{"key_as_string":"2017-02-09T05:21:00.000Z","key":1486617660000,"doc_count":12},{"key_as_string":"2017-02-09T05:22:00.000Z","key":1486617720000,"doc_count":11},{"key_as_string":"2017-02-09T05:23:00.000Z","key":1486617780000,"doc_count":10},{"key_as_string":"2017-02-09T05:24:00.000Z","key":1486617840000,"doc_count":15},{"key_as_string":"2017-02-09T05:25:00.000Z","key":1486617900000,"doc_count":8},{"key_as_string":"2017-02-09T05:26:00.000Z","key":1486617960000,"doc_count":13},{"key_as_string":"2017-02-09T05:27:00.000Z","key":1486618020000,"doc_count":14},{"key_as_string":"2017-02-09T05:28:00.000Z","key":1486618080000,"doc_count":7},{"key_as_string":"2017-02-09T05:29:00.000Z","key":1486618140000,"doc_count":9},{"key_as_string":"2017-02-09T05:30:00.000Z","key":1486618200000,"doc_count":10},{"key_as_string":"2017-02-09T05:31:00.000Z","key":1486618260000,"doc_count":11},{"key_as_string":"2017-02-09T05:32:00.000Z","key":1486618320000,"doc_count":8},{"key_as_string":"2017-02-09T05:33:00.000Z","key":1486618380000,"doc_count":12},{"key_as_string":"2017-02-09T05:34:00.000Z","key":1486618440000,"doc_count":13},{"key_as_string":"2017-02-09T05:35:00.000Z","key":1486618500000,"doc_count":10},{"key_as_string":"2017-02-09T05:36:00.000Z","key":1486618560000,"doc_count":13},{"key_as_string":"2017-02-09T05:37:00.000Z","key":1486618620000,"doc_count":5},{"key_as_string":"2017-02-09T05:38:00.000Z","key":1486618680000,"doc_count":13},{"key_as_string":"2017-02-09T05:39:00.000Z","key":1486618740000,"doc_count":5},{"key_as_string":"2017-02-09T05:40:00.000Z","key":1486618800000,"doc_count":13},{"key_as_string":"2017-02-09T05:41:00.000Z","key":1486618860000,"doc_count":12},{"key_as_string":"2017-02-09T05:42:00.000Z","key":1486618920000,"doc_count":21},{"key_as_string":"2017-02-09T05:43:00.000Z","key":1486618980000,"doc_count":6},{"key_as_string":"2017-02-09T05:44:00.000Z","key":1486619040000,"doc_count":16},{"key_as_string":"2017-02-09T05:45:00.000Z","key":1486619100000,"doc_count":11},{"key_as_string":"2017-02-09T05:46:00.000Z","key":1486619160000,"doc_count":10},{"key_as_string":"2017-02-09T05:47:00.000Z","key":1486619220000,"doc_count":10},{"key_as_string":"2017-02-09T05:48:00.000Z","key":1486619280000,"doc_count":12},{"key_as_string":"2017-02-09T05:49:00.000Z","key":1486619340000,"doc_count":11},{"key_as_string":"2017-02-09T05:50:00.000Z","key":1486619400000,"doc_count":13},{"key_as_string":"2017-02-09T05:51:00.000Z","key":1486619460000,"doc_count":12},{"key_as_string":"2017-02-09T05:52:00.000Z","key":1486619520000,"doc_count":6},{"key_as_string":"2017-02-09T05:53:00.000Z","key":1486619580000,"doc_count":13},{"key_as_string":"2017-02-09T05:54:00.000Z","key":1486619640000,"doc_count":11},{"key_as_string":"2017-02-09T05:55:00.000Z","key":1486619700000,"doc_count":13},{"key_as_string":"2017-02-09T05:56:00.000Z","key":1486619760000,"doc_count":13},{"key_as_string":"2017-02-09T05:57:00.000Z","key":1486619820000,"doc_count":13},{"key_as_string":"2017-02-09T05:58:00.000Z","key":1486619880000,"doc_count":9},{"key_as_string":"2017-02-09T05:59:00.000Z","key":1486619940000,"doc_count":12},{"key_as_string":"2017-02-09T06:00:00.000Z","key":1486620000000,"doc_count":12},{"key_as_string":"2017-02-09T06:01:00.000Z","key":1486620060000,"doc_count":17},{"key_as_string":"2017-02-09T06:02:00.000Z","key":1486620120000,"doc_count":14},{"key_as_string":"2017-02-09T06:03:00.000Z","key":1486620180000,"doc_count":7},{"key_as_string":"2017-02-09T06:04:00.000Z","key":1486620240000,"doc_count":9},{"key_as_string":"2017-02-09T06:05:00.000Z","key":1486620300000,"doc_count":15},{"key_as_string":"2017-02-09T06:06:00.000Z","key":1486620360000,"doc_count":11},{"key_as_string":"2017-02-09T06:07:00.000Z","key":1486620420000,"doc_count":10},{"key_as_string":"2017-02-09T06:08:00.000Z","key":1486620480000,"doc_count":11},{"key_as_string":"2017-02-09T06:09:00.000Z","key":1486620540000,"doc_count":8},{"key_as_string":"2017-02-09T06:10:00.000Z","key":1486620600000,"doc_count":12},{"key_as_string":"2017-02-09T06:11:00.000Z","key":1486620660000,"doc_count":16},{"key_as_string":"2017-02-09T06:12:00.000Z","key":1486620720000,"doc_count":11},{"key_as_string":"2017-02-09T06:13:00.000Z","key":1486620780000,"doc_count":8},{"key_as_string":"2017-02-09T06:14:00.000Z","key":1486620840000,"doc_count":11},{"key_as_string":"2017-02-09T06:15:00.000Z","key":1486620900000,"doc_count":16},{"key_as_string":"2017-02-09T06:16:00.000Z","key":1486620960000,"doc_count":12},{"key_as_string":"2017-02-09T06:17:00.000Z","key":1486621020000,"doc_count":10},{"key_as_string":"2017-02-09T06:18:00.000Z","key":1486621080000,"doc_count":10},{"key_as_string":"2017-02-09T06:19:00.000Z","key":1486621140000,"doc_count":18},{"key_as_string":"2017-02-09T06:20:00.000Z","key":1486621200000,"doc_count":8},{"key_as_string":"2017-02-09T06:21:00.000Z","key":1486621260000,"doc_count":13},{"key_as_string":"2017-02-09T06:22:00.000Z","key":1486621320000,"doc_count":10},{"key_as_string":"2017-02-09T06:23:00.000Z","key":1486621380000,"doc_count":15},{"key_as_string":"2017-02-09T06:24:00.000Z","key":1486621440000,"doc_count":13},{"key_as_string":"2017-02-09T06:25:00.000Z","key":1486621500000,"doc_count":10},{"key_as_string":"2017-02-09T06:26:00.000Z","key":1486621560000,"doc_count":11},{"key_as_string":"2017-02-09T06:27:00.000Z","key":1486621620000,"doc_count":10},{"key_as_string":"2017-02-09T06:28:00.000Z","key":1486621680000,"doc_count":11},{"key_as_string":"2017-02-09T06:29:00.000Z","key":1486621740000,"doc_count":14},{"key_as_string":"2017-02-09T06:30:00.000Z","key":1486621800000,"doc_count":7},{"key_as_string":"2017-02-09T06:31:00.000Z","key":1486621860000,"doc_count":13},{"key_as_string":"2017-02-09T06:32:00.000Z","key":1486621920000,"doc_count":10},{"key_as_string":"2017-02-09T06:33:00.000Z","key":1486621980000,"doc_count":7},{"key_as_string":"2017-02-09T06:34:00.000Z","key":1486622040000,"doc_count":15},{"key_as_string":"2017-02-09T06:35:00.000Z","key":1486622100000,"doc_count":10},{"key_as_string":"2017-02-09T06:36:00.000Z","key":1486622160000,"doc_count":14},{"key_as_string":"2017-02-09T06:37:00.000Z","key":1486622220000,"doc_count":10},{"key_as_string":"2017-02-09T06:38:00.000Z","key":1486622280000,"doc_count":8},{"key_as_string":"2017-02-09T06:39:00.000Z","key":1486622340000,"doc_count":7},{"key_as_string":"2017-02-09T06:40:00.000Z","key":1486622400000,"doc_count":13},{"key_as_string":"2017-02-09T06:41:00.000Z","key":1486622460000,"doc_count":13},{"key_as_string":"2017-02-09T06:42:00.000Z","key":1486622520000,"doc_count":7},{"key_as_string":"2017-02-09T06:43:00.000Z","key":1486622580000,"doc_count":12},{"key_as_string":"2017-02-09T06:44:00.000Z","key":1486622640000,"doc_count":11},{"key_as_string":"2017-02-09T06:45:00.000Z","key":1486622700000,"doc_count":10},{"key_as_string":"2017-02-09T06:46:00.000Z","key":1486622760000,"doc_count":16},{"key_as_string":"2017-02-09T06:47:00.000Z","key":1486622820000,"doc_count":17},{"key_as_string":"2017-02-09T06:48:00.000Z","key":1486622880000,"doc_count":13},{"key_as_string":"2017-02-09T06:49:00.000Z","key":1486622940000,"doc_count":11},{"key_as_string":"2017-02-09T06:50:00.000Z","key":1486623000000,"doc_count":8},{"key_as_string":"2017-02-09T06:51:00.000Z","key":1486623060000,"doc_count":6},{"key_as_string":"2017-02-09T06:52:00.000Z","key":1486623120000,"doc_count":17},{"key_as_string":"2017-02-09T06:53:00.000Z","key":1486623180000,"doc_count":11},{"key_as_string":"2017-02-09T06:54:00.000Z","key":1486623240000,"doc_count":11},{"key_as_string":"2017-02-09T06:55:00.000Z","key":1486623300000,"doc_count":14},{"key_as_string":"2017-02-09T06:56:00.000Z","key":1486623360000,"doc_count":10},{"key_as_string":"2017-02-09T06:57:00.000Z","key":1486623420000,"doc_count":14},{"key_as_string":"2017-02-09T06:58:00.000Z","key":1486623480000,"doc_count":8},{"key_as_string":"2017-02-09T06:59:00.000Z","key":1486623540000,"doc_count":10},{"key_as_string":"2017-02-09T07:00:00.000Z","key":1486623600000,"doc_count":11},{"key_as_string":"2017-02-09T07:01:00.000Z","key":1486623660000,"doc_count":16},{"key_as_string":"2017-02-09T07:02:00.000Z","key":1486623720000,"doc_count":17},{"key_as_string":"2017-02-09T07:03:00.000Z","key":1486623780000,"doc_count":16},{"key_as_string":"2017-02-09T07:04:00.000Z","key":1486623840000,"doc_count":15},{"key_as_string":"2017-02-09T07:05:00.000Z","key":1486623900000,"doc_count":12},{"key_as_string":"2017-02-09T07:06:00.000Z","key":1486623960000,"doc_count":9},{"key_as_string":"2017-02-09T07:07:00.000Z","key":1486624020000,"doc_count":11},{"key_as_string":"2017-02-09T07:08:00.000Z","key":1486624080000,"doc_count":11},{"key_as_string":"2017-02-09T07:09:00.000Z","key":1486624140000,"doc_count":20},{"key_as_string":"2017-02-09T07:10:00.000Z","key":1486624200000,"doc_count":9},{"key_as_string":"2017-02-09T07:11:00.000Z","key":1486624260000,"doc_count":17},{"key_as_string":"2017-02-09T07:12:00.000Z","key":1486624320000,"doc_count":13},{"key_as_string":"2017-02-09T07:13:00.000Z","key":1486624380000,"doc_count":18},{"key_as_string":"2017-02-09T07:14:00.000Z","key":1486624440000,"doc_count":8},{"key_as_string":"2017-02-09T07:15:00.000Z","key":1486624500000,"doc_count":14},{"key_as_string":"2017-02-09T07:16:00.000Z","key":1486624560000,"doc_count":9},{"key_as_string":"2017-02-09T07:17:00.000Z","key":1486624620000,"doc_count":15},{"key_as_string":"2017-02-09T07:18:00.000Z","key":1486624680000,"doc_count":15},{"key_as_string":"2017-02-09T07:19:00.000Z","key":1486624740000,"doc_count":15},{"key_as_string":"2017-02-09T07:20:00.000Z","key":1486624800000,"doc_count":17},{"key_as_string":"2017-02-09T07:21:00.000Z","key":1486624860000,"doc_count":13},{"key_as_string":"2017-02-09T07:22:00.000Z","key":1486624920000,"doc_count":10},{"key_as_string":"2017-02-09T07:23:00.000Z","key":1486624980000,"doc_count":10},{"key_as_string":"2017-02-09T07:24:00.000Z","key":1486625040000,"doc_count":9},{"key_as_string":"2017-02-09T07:25:00.000Z","key":1486625100000,"doc_count":16},{"key_as_string":"2017-02-09T07:26:00.000Z","key":1486625160000,"doc_count":14},{"key_as_string":"2017-02-09T07:27:00.000Z","key":1486625220000,"doc_count":13},{"key_as_string":"2017-02-09T07:28:00.000Z","key":1486625280000,"doc_count":14},{"key_as_string":"2017-02-09T07:29:00.000Z","key":1486625340000,"doc_count":14},{"key_as_string":"2017-02-09T07:30:00.000Z","key":1486625400000,"doc_count":14},{"key_as_string":"2017-02-09T07:31:00.000Z","key":1486625460000,"doc_count":16},{"key_as_string":"2017-02-09T07:32:00.000Z","key":1486625520000,"doc_count":13},{"key_as_string":"2017-02-09T07:33:00.000Z","key":1486625580000,"doc_count":12},{"key_as_string":"2017-02-09T07:34:00.000Z","key":1486625640000,"doc_count":15},{"key_as_string":"2017-02-09T07:35:00.000Z","key":1486625700000,"doc_count":13},{"key_as_string":"2017-02-09T07:36:00.000Z","key":1486625760000,"doc_count":15},{"key_as_string":"2017-02-09T07:37:00.000Z","key":1486625820000,"doc_count":12},{"key_as_string":"2017-02-09T07:38:00.000Z","key":1486625880000,"doc_count":9},{"key_as_string":"2017-02-09T07:39:00.000Z","key":1486625940000,"doc_count":20},{"key_as_string":"2017-02-09T07:40:00.000Z","key":1486626000000,"doc_count":13},{"key_as_string":"2017-02-09T07:41:00.000Z","key":1486626060000,"doc_count":17},{"key_as_string":"2017-02-09T07:42:00.000Z","key":1486626120000,"doc_count":10},{"key_as_string":"2017-02-09T07:43:00.000Z","key":1486626180000,"doc_count":15},{"key_as_string":"2017-02-09T07:44:00.000Z","key":1486626240000,"doc_count":11},{"key_as_string":"2017-02-09T07:45:00.000Z","key":1486626300000,"doc_count":11},{"key_as_string":"2017-02-09T07:46:00.000Z","key":1486626360000,"doc_count":13},{"key_as_string":"2017-02-09T07:47:00.000Z","key":1486626420000,"doc_count":14},{"key_as_string":"2017-02-09T07:48:00.000Z","key":1486626480000,"doc_count":13},{"key_as_string":"2017-02-09T07:49:00.000Z","key":1486626540000,"doc_count":13},{"key_as_string":"2017-02-09T07:50:00.000Z","key":1486626600000,"doc_count":12},{"key_as_string":"2017-02-09T07:51:00.000Z","key":1486626660000,"doc_count":15},{"key_as_string":"2017-02-09T07:52:00.000Z","key":1486626720000,"doc_count":15},{"key_as_string":"2017-02-09T07:53:00.000Z","key":1486626780000,"doc_count":15},{"key_as_string":"2017-02-09T07:54:00.000Z","key":1486626840000,"doc_count":12},{"key_as_string":"2017-02-09T07:55:00.000Z","key":1486626900000,"doc_count":11},{"key_as_string":"2017-02-09T07:56:00.000Z","key":1486626960000,"doc_count":14},{"key_as_string":"2017-02-09T07:57:00.000Z","key":1486627020000,"doc_count":8},{"key_as_string":"2017-02-09T07:58:00.000Z","key":1486627080000,"doc_count":17},{"key_as_string":"2017-02-09T07:59:00.000Z","key":1486627140000,"doc_count":13},{"key_as_string":"2017-02-09T08:00:00.000Z","key":1486627200000,"doc_count":13},{"key_as_string":"2017-02-09T08:01:00.000Z","key":1486627260000,"doc_count":12},{"key_as_string":"2017-02-09T08:02:00.000Z","key":1486627320000,"doc_count":19},{"key_as_string":"2017-02-09T08:03:00.000Z","key":1486627380000,"doc_count":15},{"key_as_string":"2017-02-09T08:04:00.000Z","key":1486627440000,"doc_count":9},{"key_as_string":"2017-02-09T08:05:00.000Z","key":1486627500000,"doc_count":14},{"key_as_string":"2017-02-09T08:06:00.000Z","key":1486627560000,"doc_count":14},{"key_as_string":"2017-02-09T08:07:00.000Z","key":1486627620000,"doc_count":13},{"key_as_string":"2017-02-09T08:08:00.000Z","key":1486627680000,"doc_count":10},{"key_as_string":"2017-02-09T08:09:00.000Z","key":1486627740000,"doc_count":14},{"key_as_string":"2017-02-09T08:10:00.000Z","key":1486627800000,"doc_count":11},{"key_as_string":"2017-02-09T08:11:00.000Z","key":1486627860000,"doc_count":12},{"key_as_string":"2017-02-09T08:12:00.000Z","key":1486627920000,"doc_count":15},{"key_as_string":"2017-02-09T08:13:00.000Z","key":1486627980000,"doc_count":13},{"key_as_string":"2017-02-09T08:14:00.000Z","key":1486628040000,"doc_count":12},{"key_as_string":"2017-02-09T08:15:00.000Z","key":1486628100000,"doc_count":11},{"key_as_string":"2017-02-09T08:16:00.000Z","key":1486628160000,"doc_count":21},{"key_as_string":"2017-02-09T08:17:00.000Z","key":1486628220000,"doc_count":16},{"key_as_string":"2017-02-09T08:18:00.000Z","key":1486628280000,"doc_count":16},{"key_as_string":"2017-02-09T08:19:00.000Z","key":1486628340000,"doc_count":11},{"key_as_string":"2017-02-09T08:20:00.000Z","key":1486628400000,"doc_count":14},{"key_as_string":"2017-02-09T08:21:00.000Z","key":1486628460000,"doc_count":14},{"key_as_string":"2017-02-09T08:22:00.000Z","key":1486628520000,"doc_count":13},{"key_as_string":"2017-02-09T08:23:00.000Z","key":1486628580000,"doc_count":13},{"key_as_string":"2017-02-09T08:24:00.000Z","key":1486628640000,"doc_count":12},{"key_as_string":"2017-02-09T08:25:00.000Z","key":1486628700000,"doc_count":16},{"key_as_string":"2017-02-09T08:26:00.000Z","key":1486628760000,"doc_count":12},{"key_as_string":"2017-02-09T08:27:00.000Z","key":1486628820000,"doc_count":10},{"key_as_string":"2017-02-09T08:28:00.000Z","key":1486628880000,"doc_count":15},{"key_as_string":"2017-02-09T08:29:00.000Z","key":1486628940000,"doc_count":17},{"key_as_string":"2017-02-09T08:30:00.000Z","key":1486629000000,"doc_count":12},{"key_as_string":"2017-02-09T08:31:00.000Z","key":1486629060000,"doc_count":13},{"key_as_string":"2017-02-09T08:32:00.000Z","key":1486629120000,"doc_count":14},{"key_as_string":"2017-02-09T08:33:00.000Z","key":1486629180000,"doc_count":14},{"key_as_string":"2017-02-09T08:34:00.000Z","key":1486629240000,"doc_count":14},{"key_as_string":"2017-02-09T08:35:00.000Z","key":1486629300000,"doc_count":12},{"key_as_string":"2017-02-09T08:36:00.000Z","key":1486629360000,"doc_count":14},{"key_as_string":"2017-02-09T08:37:00.000Z","key":1486629420000,"doc_count":14},{"key_as_string":"2017-02-09T08:38:00.000Z","key":1486629480000,"doc_count":12},{"key_as_string":"2017-02-09T08:39:00.000Z","key":1486629540000,"doc_count":7},{"key_as_string":"2017-02-09T08:40:00.000Z","key":1486629600000,"doc_count":18},{"key_as_string":"2017-02-09T08:41:00.000Z","key":1486629660000,"doc_count":10},{"key_as_string":"2017-02-09T08:42:00.000Z","key":1486629720000,"doc_count":13},{"key_as_string":"2017-02-09T08:43:00.000Z","key":1486629780000,"doc_count":14},{"key_as_string":"2017-02-09T08:44:00.000Z","key":1486629840000,"doc_count":16},{"key_as_string":"2017-02-09T08:45:00.000Z","key":1486629900000,"doc_count":14},{"key_as_string":"2017-02-09T08:46:00.000Z","key":1486629960000,"doc_count":13},{"key_as_string":"2017-02-09T08:47:00.000Z","key":1486630020000,"doc_count":15},{"key_as_string":"2017-02-09T08:48:00.000Z","key":1486630080000,"doc_count":14},{"key_as_string":"2017-02-09T08:49:00.000Z","key":1486630140000,"doc_count":16},{"key_as_string":"2017-02-09T08:50:00.000Z","key":1486630200000,"doc_count":11},{"key_as_string":"2017-02-09T08:51:00.000Z","key":1486630260000,"doc_count":15},{"key_as_string":"2017-02-09T08:52:00.000Z","key":1486630320000,"doc_count":15},{"key_as_string":"2017-02-09T08:53:00.000Z","key":1486630380000,"doc_count":12},{"key_as_string":"2017-02-09T08:54:00.000Z","key":1486630440000,"doc_count":13},{"key_as_string":"2017-02-09T08:55:00.000Z","key":1486630500000,"doc_count":11},{"key_as_string":"2017-02-09T08:56:00.000Z","key":1486630560000,"doc_count":7},{"key_as_string":"2017-02-09T08:57:00.000Z","key":1486630620000,"doc_count":14},{"key_as_string":"2017-02-09T08:58:00.000Z","key":1486630680000,"doc_count":12},{"key_as_string":"2017-02-09T08:59:00.000Z","key":1486630740000,"doc_count":8},{"key_as_string":"2017-02-09T09:00:00.000Z","key":1486630800000,"doc_count":15},{"key_as_string":"2017-02-09T09:01:00.000Z","key":1486630860000,"doc_count":13},{"key_as_string":"2017-02-09T09:02:00.000Z","key":1486630920000,"doc_count":17},{"key_as_string":"2017-02-09T09:03:00.000Z","key":1486630980000,"doc_count":10},{"key_as_string":"2017-02-09T09:04:00.000Z","key":1486631040000,"doc_count":12},{"key_as_string":"2017-02-09T09:05:00.000Z","key":1486631100000,"doc_count":12},{"key_as_string":"2017-02-09T09:06:00.000Z","key":1486631160000,"doc_count":10},{"key_as_string":"2017-02-09T09:07:00.000Z","key":1486631220000,"doc_count":13},{"key_as_string":"2017-02-09T09:08:00.000Z","key":1486631280000,"doc_count":15},{"key_as_string":"2017-02-09T09:09:00.000Z","key":1486631340000,"doc_count":10},{"key_as_string":"2017-02-09T09:10:00.000Z","key":1486631400000,"doc_count":17},{"key_as_string":"2017-02-09T09:11:00.000Z","key":1486631460000,"doc_count":14},{"key_as_string":"2017-02-09T09:12:00.000Z","key":1486631520000,"doc_count":10},{"key_as_string":"2017-02-09T09:13:00.000Z","key":1486631580000,"doc_count":15},{"key_as_string":"2017-02-09T09:14:00.000Z","key":1486631640000,"doc_count":14},{"key_as_string":"2017-02-09T09:15:00.000Z","key":1486631700000,"doc_count":16},{"key_as_string":"2017-02-09T09:16:00.000Z","key":1486631760000,"doc_count":11},{"key_as_string":"2017-02-09T09:17:00.000Z","key":1486631820000,"doc_count":16},{"key_as_string":"2017-02-09T09:18:00.000Z","key":1486631880000,"doc_count":11},{"key_as_string":"2017-02-09T09:19:00.000Z","key":1486631940000,"doc_count":11},{"key_as_string":"2017-02-09T09:20:00.000Z","key":1486632000000,"doc_count":18},{"key_as_string":"2017-02-09T09:21:00.000Z","key":1486632060000,"doc_count":10},{"key_as_string":"2017-02-09T09:22:00.000Z","key":1486632120000,"doc_count":10},{"key_as_string":"2017-02-09T09:23:00.000Z","key":1486632180000,"doc_count":14},{"key_as_string":"2017-02-09T09:24:00.000Z","key":1486632240000,"doc_count":18},{"key_as_string":"2017-02-09T09:25:00.000Z","key":1486632300000,"doc_count":12},{"key_as_string":"2017-02-09T09:26:00.000Z","key":1486632360000,"doc_count":16},{"key_as_string":"2017-02-09T09:27:00.000Z","key":1486632420000,"doc_count":15},{"key_as_string":"2017-02-09T09:28:00.000Z","key":1486632480000,"doc_count":25},{"key_as_string":"2017-02-09T09:29:00.000Z","key":1486632540000,"doc_count":13},{"key_as_string":"2017-02-09T09:30:00.000Z","key":1486632600000,"doc_count":11},{"key_as_string":"2017-02-09T09:31:00.000Z","key":1486632660000,"doc_count":12},{"key_as_string":"2017-02-09T09:32:00.000Z","key":1486632720000,"doc_count":16},{"key_as_string":"2017-02-09T09:33:00.000Z","key":1486632780000,"doc_count":10},{"key_as_string":"2017-02-09T09:34:00.000Z","key":1486632840000,"doc_count":14},{"key_as_string":"2017-02-09T09:35:00.000Z","key":1486632900000,"doc_count":14},{"key_as_string":"2017-02-09T09:36:00.000Z","key":1486632960000,"doc_count":16},{"key_as_string":"2017-02-09T09:37:00.000Z","key":1486633020000,"doc_count":19},{"key_as_string":"2017-02-09T09:38:00.000Z","key":1486633080000,"doc_count":15},{"key_as_string":"2017-02-09T09:39:00.000Z","key":1486633140000,"doc_count":13},{"key_as_string":"2017-02-09T09:40:00.000Z","key":1486633200000,"doc_count":10},{"key_as_string":"2017-02-09T09:41:00.000Z","key":1486633260000,"doc_count":16},{"key_as_string":"2017-02-09T09:42:00.000Z","key":1486633320000,"doc_count":20},{"key_as_string":"2017-02-09T09:43:00.000Z","key":1486633380000,"doc_count":7},{"key_as_string":"2017-02-09T09:44:00.000Z","key":1486633440000,"doc_count":16},{"key_as_string":"2017-02-09T09:45:00.000Z","key":1486633500000,"doc_count":10},{"key_as_string":"2017-02-09T09:46:00.000Z","key":1486633560000,"doc_count":15},{"key_as_string":"2017-02-09T09:47:00.000Z","key":1486633620000,"doc_count":10},{"key_as_string":"2017-02-09T09:48:00.000Z","key":1486633680000,"doc_count":19},{"key_as_string":"2017-02-09T09:49:00.000Z","key":1486633740000,"doc_count":11},{"key_as_string":"2017-02-09T09:50:00.000Z","key":1486633800000,"doc_count":12},{"key_as_string":"2017-02-09T09:51:00.000Z","key":1486633860000,"doc_count":13},{"key_as_string":"2017-02-09T09:52:00.000Z","key":1486633920000,"doc_count":10},{"key_as_string":"2017-02-09T09:53:00.000Z","key":1486633980000,"doc_count":15},{"key_as_string":"2017-02-09T09:54:00.000Z","key":1486634040000,"doc_count":11},{"key_as_string":"2017-02-09T09:55:00.000Z","key":1486634100000,"doc_count":15},{"key_as_string":"2017-02-09T09:56:00.000Z","key":1486634160000,"doc_count":14},{"key_as_string":"2017-02-09T09:57:00.000Z","key":1486634220000,"doc_count":11},{"key_as_string":"2017-02-09T09:58:00.000Z","key":1486634280000,"doc_count":9},{"key_as_string":"2017-02-09T09:59:00.000Z","key":1486634340000,"doc_count":18},{"key_as_string":"2017-02-09T10:00:00.000Z","key":1486634400000,"doc_count":13},{"key_as_string":"2017-02-09T10:01:00.000Z","key":1486634460000,"doc_count":13},{"key_as_string":"2017-02-09T10:02:00.000Z","key":1486634520000,"doc_count":11},{"key_as_string":"2017-02-09T10:03:00.000Z","key":1486634580000,"doc_count":15},{"key_as_string":"2017-02-09T10:04:00.000Z","key":1486634640000,"doc_count":11},{"key_as_string":"2017-02-09T10:05:00.000Z","key":1486634700000,"doc_count":16},{"key_as_string":"2017-02-09T10:06:00.000Z","key":1486634760000,"doc_count":15},{"key_as_string":"2017-02-09T10:07:00.000Z","key":1486634820000,"doc_count":9},{"key_as_string":"2017-02-09T10:08:00.000Z","key":1486634880000,"doc_count":14},{"key_as_string":"2017-02-09T10:09:00.000Z","key":1486634940000,"doc_count":16},{"key_as_string":"2017-02-09T10:10:00.000Z","key":1486635000000,"doc_count":16},{"key_as_string":"2017-02-09T10:11:00.000Z","key":1486635060000,"doc_count":13},{"key_as_string":"2017-02-09T10:12:00.000Z","key":1486635120000,"doc_count":16},{"key_as_string":"2017-02-09T10:13:00.000Z","key":1486635180000,"doc_count":12},{"key_as_string":"2017-02-09T10:14:00.000Z","key":1486635240000,"doc_count":12},{"key_as_string":"2017-02-09T10:15:00.000Z","key":1486635300000,"doc_count":14},{"key_as_string":"2017-02-09T10:16:00.000Z","key":1486635360000,"doc_count":12},{"key_as_string":"2017-02-09T10:17:00.000Z","key":1486635420000,"doc_count":18},{"key_as_string":"2017-02-09T10:18:00.000Z","key":1486635480000,"doc_count":19},{"key_as_string":"2017-02-09T10:19:00.000Z","key":1486635540000,"doc_count":8},{"key_as_string":"2017-02-09T10:20:00.000Z","key":1486635600000,"doc_count":12},{"key_as_string":"2017-02-09T10:21:00.000Z","key":1486635660000,"doc_count":13},{"key_as_string":"2017-02-09T10:22:00.000Z","key":1486635720000,"doc_count":16},{"key_as_string":"2017-02-09T10:23:00.000Z","key":1486635780000,"doc_count":13},{"key_as_string":"2017-02-09T10:24:00.000Z","key":1486635840000,"doc_count":9},{"key_as_string":"2017-02-09T10:25:00.000Z","key":1486635900000,"doc_count":17},{"key_as_string":"2017-02-09T10:26:00.000Z","key":1486635960000,"doc_count":16},{"key_as_string":"2017-02-09T10:27:00.000Z","key":1486636020000,"doc_count":14},{"key_as_string":"2017-02-09T10:28:00.000Z","key":1486636080000,"doc_count":11},{"key_as_string":"2017-02-09T10:29:00.000Z","key":1486636140000,"doc_count":12},{"key_as_string":"2017-02-09T10:30:00.000Z","key":1486636200000,"doc_count":19},{"key_as_string":"2017-02-09T10:31:00.000Z","key":1486636260000,"doc_count":16},{"key_as_string":"2017-02-09T10:32:00.000Z","key":1486636320000,"doc_count":12},{"key_as_string":"2017-02-09T10:33:00.000Z","key":1486636380000,"doc_count":14},{"key_as_string":"2017-02-09T10:34:00.000Z","key":1486636440000,"doc_count":11},{"key_as_string":"2017-02-09T10:35:00.000Z","key":1486636500000,"doc_count":15},{"key_as_string":"2017-02-09T10:36:00.000Z","key":1486636560000,"doc_count":18},{"key_as_string":"2017-02-09T10:37:00.000Z","key":1486636620000,"doc_count":15},{"key_as_string":"2017-02-09T10:38:00.000Z","key":1486636680000,"doc_count":12},{"key_as_string":"2017-02-09T10:39:00.000Z","key":1486636740000,"doc_count":14},{"key_as_string":"2017-02-09T10:40:00.000Z","key":1486636800000,"doc_count":14},{"key_as_string":"2017-02-09T10:41:00.000Z","key":1486636860000,"doc_count":17},{"key_as_string":"2017-02-09T10:42:00.000Z","key":1486636920000,"doc_count":13},{"key_as_string":"2017-02-09T10:43:00.000Z","key":1486636980000,"doc_count":9},{"key_as_string":"2017-02-09T10:44:00.000Z","key":1486637040000,"doc_count":11},{"key_as_string":"2017-02-09T10:45:00.000Z","key":1486637100000,"doc_count":14},{"key_as_string":"2017-02-09T10:46:00.000Z","key":1486637160000,"doc_count":15},{"key_as_string":"2017-02-09T10:47:00.000Z","key":1486637220000,"doc_count":14},{"key_as_string":"2017-02-09T10:48:00.000Z","key":1486637280000,"doc_count":13},{"key_as_string":"2017-02-09T10:49:00.000Z","key":1486637340000,"doc_count":16},{"key_as_string":"2017-02-09T10:50:00.000Z","key":1486637400000,"doc_count":11},{"key_as_string":"2017-02-09T10:51:00.000Z","key":1486637460000,"doc_count":15},{"key_as_string":"2017-02-09T10:52:00.000Z","key":1486637520000,"doc_count":16},{"key_as_string":"2017-02-09T10:53:00.000Z","key":1486637580000,"doc_count":14},{"key_as_string":"2017-02-09T10:54:00.000Z","key":1486637640000,"doc_count":13},{"key_as_string":"2017-02-09T10:55:00.000Z","key":1486637700000,"doc_count":12},{"key_as_string":"2017-02-09T10:56:00.000Z","key":1486637760000,"doc_count":14},{"key_as_string":"2017-02-09T10:57:00.000Z","key":1486637820000,"doc_count":14},{"key_as_string":"2017-02-09T10:58:00.000Z","key":1486637880000,"doc_count":15},{"key_as_string":"2017-02-09T10:59:00.000Z","key":1486637940000,"doc_count":11},{"key_as_string":"2017-02-09T11:00:00.000Z","key":1486638000000,"doc_count":14},{"key_as_string":"2017-02-09T11:01:00.000Z","key":1486638060000,"doc_count":12},{"key_as_string":"2017-02-09T11:02:00.000Z","key":1486638120000,"doc_count":17},{"key_as_string":"2017-02-09T11:03:00.000Z","key":1486638180000,"doc_count":10},{"key_as_string":"2017-02-09T11:04:00.000Z","key":1486638240000,"doc_count":14},{"key_as_string":"2017-02-09T11:05:00.000Z","key":1486638300000,"doc_count":13},{"key_as_string":"2017-02-09T11:06:00.000Z","key":1486638360000,"doc_count":11},{"key_as_string":"2017-02-09T11:07:00.000Z","key":1486638420000,"doc_count":14},{"key_as_string":"2017-02-09T11:08:00.000Z","key":1486638480000,"doc_count":8},{"key_as_string":"2017-02-09T11:09:00.000Z","key":1486638540000,"doc_count":20},{"key_as_string":"2017-02-09T11:10:00.000Z","key":1486638600000,"doc_count":15},{"key_as_string":"2017-02-09T11:11:00.000Z","key":1486638660000,"doc_count":17},{"key_as_string":"2017-02-09T11:12:00.000Z","key":1486638720000,"doc_count":14},{"key_as_string":"2017-02-09T11:13:00.000Z","key":1486638780000,"doc_count":12},{"key_as_string":"2017-02-09T11:14:00.000Z","key":1486638840000,"doc_count":12},{"key_as_string":"2017-02-09T11:15:00.000Z","key":1486638900000,"doc_count":18},{"key_as_string":"2017-02-09T11:16:00.000Z","key":1486638960000,"doc_count":17},{"key_as_string":"2017-02-09T11:17:00.000Z","key":1486639020000,"doc_count":16},{"key_as_string":"2017-02-09T11:18:00.000Z","key":1486639080000,"doc_count":11},{"key_as_string":"2017-02-09T11:19:00.000Z","key":1486639140000,"doc_count":14},{"key_as_string":"2017-02-09T11:20:00.000Z","key":1486639200000,"doc_count":17},{"key_as_string":"2017-02-09T11:21:00.000Z","key":1486639260000,"doc_count":16},{"key_as_string":"2017-02-09T11:22:00.000Z","key":1486639320000,"doc_count":13},{"key_as_string":"2017-02-09T11:23:00.000Z","key":1486639380000,"doc_count":15},{"key_as_string":"2017-02-09T11:24:00.000Z","key":1486639440000,"doc_count":11},{"key_as_string":"2017-02-09T11:25:00.000Z","key":1486639500000,"doc_count":12},{"key_as_string":"2017-02-09T11:26:00.000Z","key":1486639560000,"doc_count":20},{"key_as_string":"2017-02-09T11:27:00.000Z","key":1486639620000,"doc_count":13},{"key_as_string":"2017-02-09T11:28:00.000Z","key":1486639680000,"doc_count":15},{"key_as_string":"2017-02-09T11:29:00.000Z","key":1486639740000,"doc_count":19},{"key_as_string":"2017-02-09T11:30:00.000Z","key":1486639800000,"doc_count":12},{"key_as_string":"2017-02-09T11:31:00.000Z","key":1486639860000,"doc_count":12},{"key_as_string":"2017-02-09T11:32:00.000Z","key":1486639920000,"doc_count":13},{"key_as_string":"2017-02-09T11:33:00.000Z","key":1486639980000,"doc_count":15},{"key_as_string":"2017-02-09T11:34:00.000Z","key":1486640040000,"doc_count":14},{"key_as_string":"2017-02-09T11:35:00.000Z","key":1486640100000,"doc_count":16},{"key_as_string":"2017-02-09T11:36:00.000Z","key":1486640160000,"doc_count":18},{"key_as_string":"2017-02-09T11:37:00.000Z","key":1486640220000,"doc_count":13},{"key_as_string":"2017-02-09T11:38:00.000Z","key":1486640280000,"doc_count":11},{"key_as_string":"2017-02-09T11:39:00.000Z","key":1486640340000,"doc_count":18},{"key_as_string":"2017-02-09T11:40:00.000Z","key":1486640400000,"doc_count":10},{"key_as_string":"2017-02-09T11:41:00.000Z","key":1486640460000,"doc_count":16},{"key_as_string":"2017-02-09T11:42:00.000Z","key":1486640520000,"doc_count":16},{"key_as_string":"2017-02-09T11:43:00.000Z","key":1486640580000,"doc_count":10},{"key_as_string":"2017-02-09T11:44:00.000Z","key":1486640640000,"doc_count":8},{"key_as_string":"2017-02-09T11:45:00.000Z","key":1486640700000,"doc_count":17},{"key_as_string":"2017-02-09T11:46:00.000Z","key":1486640760000,"doc_count":15},{"key_as_string":"2017-02-09T11:47:00.000Z","key":1486640820000,"doc_count":11},{"key_as_string":"2017-02-09T11:48:00.000Z","key":1486640880000,"doc_count":14},{"key_as_string":"2017-02-09T11:49:00.000Z","key":1486640940000,"doc_count":16},{"key_as_string":"2017-02-09T11:50:00.000Z","key":1486641000000,"doc_count":15},{"key_as_string":"2017-02-09T11:51:00.000Z","key":1486641060000,"doc_count":16},{"key_as_string":"2017-02-09T11:52:00.000Z","key":1486641120000,"doc_count":19},{"key_as_string":"2017-02-09T11:53:00.000Z","key":1486641180000,"doc_count":14},{"key_as_string":"2017-02-09T11:54:00.000Z","key":1486641240000,"doc_count":11},{"key_as_string":"2017-02-09T11:55:00.000Z","key":1486641300000,"doc_count":7},{"key_as_string":"2017-02-09T11:56:00.000Z","key":1486641360000,"doc_count":15},{"key_as_string":"2017-02-09T11:57:00.000Z","key":1486641420000,"doc_count":10},{"key_as_string":"2017-02-09T11:58:00.000Z","key":1486641480000,"doc_count":11},{"key_as_string":"2017-02-09T11:59:00.000Z","key":1486641540000,"doc_count":16},{"key_as_string":"2017-02-09T12:00:00.000Z","key":1486641600000,"doc_count":18},{"key_as_string":"2017-02-09T12:01:00.000Z","key":1486641660000,"doc_count":11},{"key_as_string":"2017-02-09T12:02:00.000Z","key":1486641720000,"doc_count":16},{"key_as_string":"2017-02-09T12:03:00.000Z","key":1486641780000,"doc_count":12},{"key_as_string":"2017-02-09T12:04:00.000Z","key":1486641840000,"doc_count":14},{"key_as_string":"2017-02-09T12:05:00.000Z","key":1486641900000,"doc_count":13},{"key_as_string":"2017-02-09T12:06:00.000Z","key":1486641960000,"doc_count":11},{"key_as_string":"2017-02-09T12:07:00.000Z","key":1486642020000,"doc_count":19},{"key_as_string":"2017-02-09T12:08:00.000Z","key":1486642080000,"doc_count":19},{"key_as_string":"2017-02-09T12:09:00.000Z","key":1486642140000,"doc_count":9},{"key_as_string":"2017-02-09T12:10:00.000Z","key":1486642200000,"doc_count":16},{"key_as_string":"2017-02-09T12:11:00.000Z","key":1486642260000,"doc_count":14},{"key_as_string":"2017-02-09T12:12:00.000Z","key":1486642320000,"doc_count":12},{"key_as_string":"2017-02-09T12:13:00.000Z","key":1486642380000,"doc_count":19},{"key_as_string":"2017-02-09T12:14:00.000Z","key":1486642440000,"doc_count":16},{"key_as_string":"2017-02-09T12:15:00.000Z","key":1486642500000,"doc_count":15},{"key_as_string":"2017-02-09T12:16:00.000Z","key":1486642560000,"doc_count":13},{"key_as_string":"2017-02-09T12:17:00.000Z","key":1486642620000,"doc_count":10},{"key_as_string":"2017-02-09T12:18:00.000Z","key":1486642680000,"doc_count":13},{"key_as_string":"2017-02-09T12:19:00.000Z","key":1486642740000,"doc_count":13},{"key_as_string":"2017-02-09T12:20:00.000Z","key":1486642800000,"doc_count":11},{"key_as_string":"2017-02-09T12:21:00.000Z","key":1486642860000,"doc_count":19},{"key_as_string":"2017-02-09T12:22:00.000Z","key":1486642920000,"doc_count":13},{"key_as_string":"2017-02-09T12:23:00.000Z","key":1486642980000,"doc_count":17},{"key_as_string":"2017-02-09T12:24:00.000Z","key":1486643040000,"doc_count":10},{"key_as_string":"2017-02-09T12:25:00.000Z","key":1486643100000,"doc_count":12},{"key_as_string":"2017-02-09T12:26:00.000Z","key":1486643160000,"doc_count":13},{"key_as_string":"2017-02-09T12:27:00.000Z","key":1486643220000,"doc_count":12},{"key_as_string":"2017-02-09T12:28:00.000Z","key":1486643280000,"doc_count":16},{"key_as_string":"2017-02-09T12:29:00.000Z","key":1486643340000,"doc_count":15},{"key_as_string":"2017-02-09T12:30:00.000Z","key":1486643400000,"doc_count":14},{"key_as_string":"2017-02-09T12:31:00.000Z","key":1486643460000,"doc_count":18},{"key_as_string":"2017-02-09T12:32:00.000Z","key":1486643520000,"doc_count":10},{"key_as_string":"2017-02-09T12:33:00.000Z","key":1486643580000,"doc_count":13},{"key_as_string":"2017-02-09T12:34:00.000Z","key":1486643640000,"doc_count":9},{"key_as_string":"2017-02-09T12:35:00.000Z","key":1486643700000,"doc_count":16},{"key_as_string":"2017-02-09T12:36:00.000Z","key":1486643760000,"doc_count":12},{"key_as_string":"2017-02-09T12:37:00.000Z","key":1486643820000,"doc_count":13},{"key_as_string":"2017-02-09T12:38:00.000Z","key":1486643880000,"doc_count":16},{"key_as_string":"2017-02-09T12:39:00.000Z","key":1486643940000,"doc_count":9},{"key_as_string":"2017-02-09T12:40:00.000Z","key":1486644000000,"doc_count":12},{"key_as_string":"2017-02-09T12:41:00.000Z","key":1486644060000,"doc_count":11},{"key_as_string":"2017-02-09T12:42:00.000Z","key":1486644120000,"doc_count":16},{"key_as_string":"2017-02-09T12:43:00.000Z","key":1486644180000,"doc_count":13},{"key_as_string":"2017-02-09T12:44:00.000Z","key":1486644240000,"doc_count":15},{"key_as_string":"2017-02-09T12:45:00.000Z","key":1486644300000,"doc_count":13},{"key_as_string":"2017-02-09T12:46:00.000Z","key":1486644360000,"doc_count":15},{"key_as_string":"2017-02-09T12:47:00.000Z","key":1486644420000,"doc_count":13},{"key_as_string":"2017-02-09T12:48:00.000Z","key":1486644480000,"doc_count":9},{"key_as_string":"2017-02-09T12:49:00.000Z","key":1486644540000,"doc_count":18},{"key_as_string":"2017-02-09T12:50:00.000Z","key":1486644600000,"doc_count":13},{"key_as_string":"2017-02-09T12:51:00.000Z","key":1486644660000,"doc_count":13},{"key_as_string":"2017-02-09T12:52:00.000Z","key":1486644720000,"doc_count":13},{"key_as_string":"2017-02-09T12:53:00.000Z","key":1486644780000,"doc_count":11},{"key_as_string":"2017-02-09T12:54:00.000Z","key":1486644840000,"doc_count":19},{"key_as_string":"2017-02-09T12:55:00.000Z","key":1486644900000,"doc_count":12},{"key_as_string":"2017-02-09T12:56:00.000Z","key":1486644960000,"doc_count":15},{"key_as_string":"2017-02-09T12:57:00.000Z","key":1486645020000,"doc_count":14},{"key_as_string":"2017-02-09T12:58:00.000Z","key":1486645080000,"doc_count":17},{"key_as_string":"2017-02-09T12:59:00.000Z","key":1486645140000,"doc_count":12},{"key_as_string":"2017-02-09T13:00:00.000Z","key":1486645200000,"doc_count":17},{"key_as_string":"2017-02-09T13:01:00.000Z","key":1486645260000,"doc_count":13},{"key_as_string":"2017-02-09T13:02:00.000Z","key":1486645320000,"doc_count":12},{"key_as_string":"2017-02-09T13:03:00.000Z","key":1486645380000,"doc_count":11},{"key_as_string":"2017-02-09T13:04:00.000Z","key":1486645440000,"doc_count":14},{"key_as_string":"2017-02-09T13:05:00.000Z","key":1486645500000,"doc_count":18},{"key_as_string":"2017-02-09T13:06:00.000Z","key":1486645560000,"doc_count":10},{"key_as_string":"2017-02-09T13:07:00.000Z","key":1486645620000,"doc_count":14},{"key_as_string":"2017-02-09T13:08:00.000Z","key":1486645680000,"doc_count":15},{"key_as_string":"2017-02-09T13:09:00.000Z","key":1486645740000,"doc_count":11},{"key_as_string":"2017-02-09T13:10:00.000Z","key":1486645800000,"doc_count":15},{"key_as_string":"2017-02-09T13:11:00.000Z","key":1486645860000,"doc_count":14},{"key_as_string":"2017-02-09T13:12:00.000Z","key":1486645920000,"doc_count":9},{"key_as_string":"2017-02-09T13:13:00.000Z","key":1486645980000,"doc_count":16},{"key_as_string":"2017-02-09T13:14:00.000Z","key":1486646040000,"doc_count":16},{"key_as_string":"2017-02-09T13:15:00.000Z","key":1486646100000,"doc_count":13},{"key_as_string":"2017-02-09T13:16:00.000Z","key":1486646160000,"doc_count":9},{"key_as_string":"2017-02-09T13:17:00.000Z","key":1486646220000,"doc_count":11},{"key_as_string":"2017-02-09T13:18:00.000Z","key":1486646280000,"doc_count":14},{"key_as_string":"2017-02-09T13:19:00.000Z","key":1486646340000,"doc_count":15},{"key_as_string":"2017-02-09T13:20:00.000Z","key":1486646400000,"doc_count":16},{"key_as_string":"2017-02-09T13:21:00.000Z","key":1486646460000,"doc_count":9},{"key_as_string":"2017-02-09T13:22:00.000Z","key":1486646520000,"doc_count":16},{"key_as_string":"2017-02-09T13:23:00.000Z","key":1486646580000,"doc_count":13},{"key_as_string":"2017-02-09T13:24:00.000Z","key":1486646640000,"doc_count":11},{"key_as_string":"2017-02-09T13:25:00.000Z","key":1486646700000,"doc_count":13},{"key_as_string":"2017-02-09T13:26:00.000Z","key":1486646760000,"doc_count":10},{"key_as_string":"2017-02-09T13:27:00.000Z","key":1486646820000,"doc_count":18},{"key_as_string":"2017-02-09T13:28:00.000Z","key":1486646880000,"doc_count":13},{"key_as_string":"2017-02-09T13:29:00.000Z","key":1486646940000,"doc_count":13},{"key_as_string":"2017-02-09T13:30:00.000Z","key":1486647000000,"doc_count":18},{"key_as_string":"2017-02-09T13:31:00.000Z","key":1486647060000,"doc_count":10},{"key_as_string":"2017-02-09T13:32:00.000Z","key":1486647120000,"doc_count":15},{"key_as_string":"2017-02-09T13:33:00.000Z","key":1486647180000,"doc_count":15},{"key_as_string":"2017-02-09T13:34:00.000Z","key":1486647240000,"doc_count":18},{"key_as_string":"2017-02-09T13:35:00.000Z","key":1486647300000,"doc_count":15},{"key_as_string":"2017-02-09T13:36:00.000Z","key":1486647360000,"doc_count":20},{"key_as_string":"2017-02-09T13:37:00.000Z","key":1486647420000,"doc_count":13},{"key_as_string":"2017-02-09T13:38:00.000Z","key":1486647480000,"doc_count":15},{"key_as_string":"2017-02-09T13:39:00.000Z","key":1486647540000,"doc_count":15},{"key_as_string":"2017-02-09T13:40:00.000Z","key":1486647600000,"doc_count":16},{"key_as_string":"2017-02-09T13:41:00.000Z","key":1486647660000,"doc_count":7},{"key_as_string":"2017-02-09T13:42:00.000Z","key":1486647720000,"doc_count":17},{"key_as_string":"2017-02-09T13:43:00.000Z","key":1486647780000,"doc_count":12},{"key_as_string":"2017-02-09T13:44:00.000Z","key":1486647840000,"doc_count":15},{"key_as_string":"2017-02-09T13:45:00.000Z","key":1486647900000,"doc_count":16},{"key_as_string":"2017-02-09T13:46:00.000Z","key":1486647960000,"doc_count":14},{"key_as_string":"2017-02-09T13:47:00.000Z","key":1486648020000,"doc_count":18},{"key_as_string":"2017-02-09T13:48:00.000Z","key":1486648080000,"doc_count":17},{"key_as_string":"2017-02-09T13:49:00.000Z","key":1486648140000,"doc_count":16},{"key_as_string":"2017-02-09T13:50:00.000Z","key":1486648200000,"doc_count":9},{"key_as_string":"2017-02-09T13:51:00.000Z","key":1486648260000,"doc_count":14},{"key_as_string":"2017-02-09T13:52:00.000Z","key":1486648320000,"doc_count":14},{"key_as_string":"2017-02-09T13:53:00.000Z","key":1486648380000,"doc_count":20},{"key_as_string":"2017-02-09T13:54:00.000Z","key":1486648440000,"doc_count":9},{"key_as_string":"2017-02-09T13:55:00.000Z","key":1486648500000,"doc_count":12},{"key_as_string":"2017-02-09T13:56:00.000Z","key":1486648560000,"doc_count":15},{"key_as_string":"2017-02-09T13:57:00.000Z","key":1486648620000,"doc_count":11},{"key_as_string":"2017-02-09T13:58:00.000Z","key":1486648680000,"doc_count":15},{"key_as_string":"2017-02-09T13:59:00.000Z","key":1486648740000,"doc_count":13},{"key_as_string":"2017-02-09T14:00:00.000Z","key":1486648800000,"doc_count":10},{"key_as_string":"2017-02-09T14:01:00.000Z","key":1486648860000,"doc_count":14},{"key_as_string":"2017-02-09T14:02:00.000Z","key":1486648920000,"doc_count":14},{"key_as_string":"2017-02-09T14:03:00.000Z","key":1486648980000,"doc_count":16},{"key_as_string":"2017-02-09T14:04:00.000Z","key":1486649040000,"doc_count":12},{"key_as_string":"2017-02-09T14:05:00.000Z","key":1486649100000,"doc_count":9},{"key_as_string":"2017-02-09T14:06:00.000Z","key":1486649160000,"doc_count":11},{"key_as_string":"2017-02-09T14:07:00.000Z","key":1486649220000,"doc_count":15},{"key_as_string":"2017-02-09T14:08:00.000Z","key":1486649280000,"doc_count":12},{"key_as_string":"2017-02-09T14:09:00.000Z","key":1486649340000,"doc_count":19},{"key_as_string":"2017-02-09T14:10:00.000Z","key":1486649400000,"doc_count":14},{"key_as_string":"2017-02-09T14:11:00.000Z","key":1486649460000,"doc_count":17},{"key_as_string":"2017-02-09T14:12:00.000Z","key":1486649520000,"doc_count":7},{"key_as_string":"2017-02-09T14:13:00.000Z","key":1486649580000,"doc_count":20},{"key_as_string":"2017-02-09T14:14:00.000Z","key":1486649640000,"doc_count":9},{"key_as_string":"2017-02-09T14:15:00.000Z","key":1486649700000,"doc_count":17},{"key_as_string":"2017-02-09T14:16:00.000Z","key":1486649760000,"doc_count":16},{"key_as_string":"2017-02-09T14:17:00.000Z","key":1486649820000,"doc_count":15},{"key_as_string":"2017-02-09T14:18:00.000Z","key":1486649880000,"doc_count":15},{"key_as_string":"2017-02-09T14:19:00.000Z","key":1486649940000,"doc_count":12},{"key_as_string":"2017-02-09T14:20:00.000Z","key":1486650000000,"doc_count":8},{"key_as_string":"2017-02-09T14:21:00.000Z","key":1486650060000,"doc_count":17},{"key_as_string":"2017-02-09T14:22:00.000Z","key":1486650120000,"doc_count":10},{"key_as_string":"2017-02-09T14:23:00.000Z","key":1486650180000,"doc_count":11},{"key_as_string":"2017-02-09T14:24:00.000Z","key":1486650240000,"doc_count":11},{"key_as_string":"2017-02-09T14:25:00.000Z","key":1486650300000,"doc_count":14},{"key_as_string":"2017-02-09T14:26:00.000Z","key":1486650360000,"doc_count":16},{"key_as_string":"2017-02-09T14:27:00.000Z","key":1486650420000,"doc_count":12},{"key_as_string":"2017-02-09T14:28:00.000Z","key":1486650480000,"doc_count":14},{"key_as_string":"2017-02-09T14:29:00.000Z","key":1486650540000,"doc_count":11},{"key_as_string":"2017-02-09T14:30:00.000Z","key":1486650600000,"doc_count":13},{"key_as_string":"2017-02-09T14:31:00.000Z","key":1486650660000,"doc_count":13},{"key_as_string":"2017-02-09T14:32:00.000Z","key":1486650720000,"doc_count":19},{"key_as_string":"2017-02-09T14:33:00.000Z","key":1486650780000,"doc_count":13},{"key_as_string":"2017-02-09T14:34:00.000Z","key":1486650840000,"doc_count":15},{"key_as_string":"2017-02-09T14:35:00.000Z","key":1486650900000,"doc_count":11},{"key_as_string":"2017-02-09T14:36:00.000Z","key":1486650960000,"doc_count":15},{"key_as_string":"2017-02-09T14:37:00.000Z","key":1486651020000,"doc_count":12},{"key_as_string":"2017-02-09T14:38:00.000Z","key":1486651080000,"doc_count":11},{"key_as_string":"2017-02-09T14:39:00.000Z","key":1486651140000,"doc_count":18},{"key_as_string":"2017-02-09T14:40:00.000Z","key":1486651200000,"doc_count":9},{"key_as_string":"2017-02-09T14:41:00.000Z","key":1486651260000,"doc_count":15},{"key_as_string":"2017-02-09T14:42:00.000Z","key":1486651320000,"doc_count":12},{"key_as_string":"2017-02-09T14:43:00.000Z","key":1486651380000,"doc_count":15},{"key_as_string":"2017-02-09T14:44:00.000Z","key":1486651440000,"doc_count":12},{"key_as_string":"2017-02-09T14:45:00.000Z","key":1486651500000,"doc_count":13},{"key_as_string":"2017-02-09T14:46:00.000Z","key":1486651560000,"doc_count":12},{"key_as_string":"2017-02-09T14:47:00.000Z","key":1486651620000,"doc_count":14},{"key_as_string":"2017-02-09T14:48:00.000Z","key":1486651680000,"doc_count":13},{"key_as_string":"2017-02-09T14:49:00.000Z","key":1486651740000,"doc_count":17},{"key_as_string":"2017-02-09T14:50:00.000Z","key":1486651800000,"doc_count":9},{"key_as_string":"2017-02-09T14:51:00.000Z","key":1486651860000,"doc_count":12},{"key_as_string":"2017-02-09T14:52:00.000Z","key":1486651920000,"doc_count":16},{"key_as_string":"2017-02-09T14:53:00.000Z","key":1486651980000,"doc_count":19},{"key_as_string":"2017-02-09T14:54:00.000Z","key":1486652040000,"doc_count":15},{"key_as_string":"2017-02-09T14:55:00.000Z","key":1486652100000,"doc_count":11},{"key_as_string":"2017-02-09T14:56:00.000Z","key":1486652160000,"doc_count":15},{"key_as_string":"2017-02-09T14:57:00.000Z","key":1486652220000,"doc_count":11},{"key_as_string":"2017-02-09T14:58:00.000Z","key":1486652280000,"doc_count":12},{"key_as_string":"2017-02-09T14:59:00.000Z","key":1486652340000,"doc_count":11},{"key_as_string":"2017-02-09T15:00:00.000Z","key":1486652400000,"doc_count":13},{"key_as_string":"2017-02-09T15:01:00.000Z","key":1486652460000,"doc_count":10},{"key_as_string":"2017-02-09T15:02:00.000Z","key":1486652520000,"doc_count":17},{"key_as_string":"2017-02-09T15:03:00.000Z","key":1486652580000,"doc_count":12},{"key_as_string":"2017-02-09T15:04:00.000Z","key":1486652640000,"doc_count":13},{"key_as_string":"2017-02-09T15:05:00.000Z","key":1486652700000,"doc_count":13},{"key_as_string":"2017-02-09T15:06:00.000Z","key":1486652760000,"doc_count":15},{"key_as_string":"2017-02-09T15:07:00.000Z","key":1486652820000,"doc_count":12},{"key_as_string":"2017-02-09T15:08:00.000Z","key":1486652880000,"doc_count":12},{"key_as_string":"2017-02-09T15:09:00.000Z","key":1486652940000,"doc_count":13},{"key_as_string":"2017-02-09T15:10:00.000Z","key":1486653000000,"doc_count":15},{"key_as_string":"2017-02-09T15:11:00.000Z","key":1486653060000,"doc_count":20},{"key_as_string":"2017-02-09T15:12:00.000Z","key":1486653120000,"doc_count":10},{"key_as_string":"2017-02-09T15:13:00.000Z","key":1486653180000,"doc_count":12},{"key_as_string":"2017-02-09T15:14:00.000Z","key":1486653240000,"doc_count":8},{"key_as_string":"2017-02-09T15:15:00.000Z","key":1486653300000,"doc_count":18},{"key_as_string":"2017-02-09T15:16:00.000Z","key":1486653360000,"doc_count":16},{"key_as_string":"2017-02-09T15:17:00.000Z","key":1486653420000,"doc_count":13},{"key_as_string":"2017-02-09T15:18:00.000Z","key":1486653480000,"doc_count":14},{"key_as_string":"2017-02-09T15:19:00.000Z","key":1486653540000,"doc_count":14},{"key_as_string":"2017-02-09T15:20:00.000Z","key":1486653600000,"doc_count":14},{"key_as_string":"2017-02-09T15:21:00.000Z","key":1486653660000,"doc_count":11},{"key_as_string":"2017-02-09T15:22:00.000Z","key":1486653720000,"doc_count":13},{"key_as_string":"2017-02-09T15:23:00.000Z","key":1486653780000,"doc_count":11},{"key_as_string":"2017-02-09T15:24:00.000Z","key":1486653840000,"doc_count":14},{"key_as_string":"2017-02-09T15:25:00.000Z","key":1486653900000,"doc_count":15},{"key_as_string":"2017-02-09T15:26:00.000Z","key":1486653960000,"doc_count":12},{"key_as_string":"2017-02-09T15:27:00.000Z","key":1486654020000,"doc_count":9},{"key_as_string":"2017-02-09T15:28:00.000Z","key":1486654080000,"doc_count":17},{"key_as_string":"2017-02-09T15:29:00.000Z","key":1486654140000,"doc_count":10},{"key_as_string":"2017-02-09T15:30:00.000Z","key":1486654200000,"doc_count":15},{"key_as_string":"2017-02-09T15:31:00.000Z","key":1486654260000,"doc_count":13},{"key_as_string":"2017-02-09T15:32:00.000Z","key":1486654320000,"doc_count":10},{"key_as_string":"2017-02-09T15:33:00.000Z","key":1486654380000,"doc_count":19},{"key_as_string":"2017-02-09T15:34:00.000Z","key":1486654440000,"doc_count":13},{"key_as_string":"2017-02-09T15:35:00.000Z","key":1486654500000,"doc_count":13},{"key_as_string":"2017-02-09T15:36:00.000Z","key":1486654560000,"doc_count":11},{"key_as_string":"2017-02-09T15:37:00.000Z","key":1486654620000,"doc_count":10},{"key_as_string":"2017-02-09T15:38:00.000Z","key":1486654680000,"doc_count":14},{"key_as_string":"2017-02-09T15:39:00.000Z","key":1486654740000,"doc_count":19},{"key_as_string":"2017-02-09T15:40:00.000Z","key":1486654800000,"doc_count":14},{"key_as_string":"2017-02-09T15:41:00.000Z","key":1486654860000,"doc_count":14},{"key_as_string":"2017-02-09T15:42:00.000Z","key":1486654920000,"doc_count":11},{"key_as_string":"2017-02-09T15:43:00.000Z","key":1486654980000,"doc_count":12},{"key_as_string":"2017-02-09T15:44:00.000Z","key":1486655040000,"doc_count":10},{"key_as_string":"2017-02-09T15:45:00.000Z","key":1486655100000,"doc_count":14},{"key_as_string":"2017-02-09T15:46:00.000Z","key":1486655160000,"doc_count":13},{"key_as_string":"2017-02-09T15:47:00.000Z","key":1486655220000,"doc_count":10},{"key_as_string":"2017-02-09T15:48:00.000Z","key":1486655280000,"doc_count":12},{"key_as_string":"2017-02-09T15:49:00.000Z","key":1486655340000,"doc_count":18},{"key_as_string":"2017-02-09T15:50:00.000Z","key":1486655400000,"doc_count":18},{"key_as_string":"2017-02-09T15:51:00.000Z","key":1486655460000,"doc_count":12},{"key_as_string":"2017-02-09T15:52:00.000Z","key":1486655520000,"doc_count":12},{"key_as_string":"2017-02-09T15:53:00.000Z","key":1486655580000,"doc_count":18},{"key_as_string":"2017-02-09T15:54:00.000Z","key":1486655640000,"doc_count":9},{"key_as_string":"2017-02-09T15:55:00.000Z","key":1486655700000,"doc_count":11},{"key_as_string":"2017-02-09T15:56:00.000Z","key":1486655760000,"doc_count":14},{"key_as_string":"2017-02-09T15:57:00.000Z","key":1486655820000,"doc_count":14},{"key_as_string":"2017-02-09T15:58:00.000Z","key":1486655880000,"doc_count":15},{"key_as_string":"2017-02-09T15:59:00.000Z","key":1486655940000,"doc_count":13},{"key_as_string":"2017-02-09T16:00:00.000Z","key":1486656000000,"doc_count":13},{"key_as_string":"2017-02-09T16:01:00.000Z","key":1486656060000,"doc_count":11},{"key_as_string":"2017-02-09T16:02:00.000Z","key":1486656120000,"doc_count":10},{"key_as_string":"2017-02-09T16:03:00.000Z","key":1486656180000,"doc_count":18},{"key_as_string":"2017-02-09T16:04:00.000Z","key":1486656240000,"doc_count":6},{"key_as_string":"2017-02-09T16:05:00.000Z","key":1486656300000,"doc_count":14},{"key_as_string":"2017-02-09T16:06:00.000Z","key":1486656360000,"doc_count":16},{"key_as_string":"2017-02-09T16:07:00.000Z","key":1486656420000,"doc_count":12},{"key_as_string":"2017-02-09T16:08:00.000Z","key":1486656480000,"doc_count":15},{"key_as_string":"2017-02-09T16:09:00.000Z","key":1486656540000,"doc_count":13},{"key_as_string":"2017-02-09T16:10:00.000Z","key":1486656600000,"doc_count":13},{"key_as_string":"2017-02-09T16:11:00.000Z","key":1486656660000,"doc_count":7},{"key_as_string":"2017-02-09T16:12:00.000Z","key":1486656720000,"doc_count":16},{"key_as_string":"2017-02-09T16:13:00.000Z","key":1486656780000,"doc_count":10},{"key_as_string":"2017-02-09T16:14:00.000Z","key":1486656840000,"doc_count":52},{"key_as_string":"2017-02-09T16:15:00.000Z","key":1486656900000,"doc_count":130},{"key_as_string":"2017-02-09T16:16:00.000Z","key":1486656960000,"doc_count":17},{"key_as_string":"2017-02-09T16:17:00.000Z","key":1486657020000,"doc_count":13},{"key_as_string":"2017-02-09T16:18:00.000Z","key":1486657080000,"doc_count":10},{"key_as_string":"2017-02-09T16:19:00.000Z","key":1486657140000,"doc_count":9},{"key_as_string":"2017-02-09T16:20:00.000Z","key":1486657200000,"doc_count":9},{"key_as_string":"2017-02-09T16:21:00.000Z","key":1486657260000,"doc_count":17},{"key_as_string":"2017-02-09T16:22:00.000Z","key":1486657320000,"doc_count":13},{"key_as_string":"2017-02-09T16:23:00.000Z","key":1486657380000,"doc_count":10},{"key_as_string":"2017-02-09T16:24:00.000Z","key":1486657440000,"doc_count":11},{"key_as_string":"2017-02-09T16:25:00.000Z","key":1486657500000,"doc_count":9},{"key_as_string":"2017-02-09T16:26:00.000Z","key":1486657560000,"doc_count":12},{"key_as_string":"2017-02-09T16:27:00.000Z","key":1486657620000,"doc_count":15},{"key_as_string":"2017-02-09T16:28:00.000Z","key":1486657680000,"doc_count":8},{"key_as_string":"2017-02-09T16:29:00.000Z","key":1486657740000,"doc_count":12},{"key_as_string":"2017-02-09T16:30:00.000Z","key":1486657800000,"doc_count":12},{"key_as_string":"2017-02-09T16:31:00.000Z","key":1486657860000,"doc_count":15},{"key_as_string":"2017-02-09T16:32:00.000Z","key":1486657920000,"doc_count":10},{"key_as_string":"2017-02-09T16:33:00.000Z","key":1486657980000,"doc_count":13},{"key_as_string":"2017-02-09T16:34:00.000Z","key":1486658040000,"doc_count":13},{"key_as_string":"2017-02-09T16:35:00.000Z","key":1486658100000,"doc_count":14},{"key_as_string":"2017-02-09T16:36:00.000Z","key":1486658160000,"doc_count":14},{"key_as_string":"2017-02-09T16:37:00.000Z","key":1486658220000,"doc_count":11},{"key_as_string":"2017-02-09T16:38:00.000Z","key":1486658280000,"doc_count":12},{"key_as_string":"2017-02-09T16:39:00.000Z","key":1486658340000,"doc_count":11},{"key_as_string":"2017-02-09T16:40:00.000Z","key":1486658400000,"doc_count":10},{"key_as_string":"2017-02-09T16:41:00.000Z","key":1486658460000,"doc_count":10},{"key_as_string":"2017-02-09T16:42:00.000Z","key":1486658520000,"doc_count":13},{"key_as_string":"2017-02-09T16:43:00.000Z","key":1486658580000,"doc_count":16},{"key_as_string":"2017-02-09T16:44:00.000Z","key":1486658640000,"doc_count":14},{"key_as_string":"2017-02-09T16:45:00.000Z","key":1486658700000,"doc_count":15},{"key_as_string":"2017-02-09T16:46:00.000Z","key":1486658760000,"doc_count":13},{"key_as_string":"2017-02-09T16:47:00.000Z","key":1486658820000,"doc_count":14},{"key_as_string":"2017-02-09T16:48:00.000Z","key":1486658880000,"doc_count":9},{"key_as_string":"2017-02-09T16:49:00.000Z","key":1486658940000,"doc_count":15},{"key_as_string":"2017-02-09T16:50:00.000Z","key":1486659000000,"doc_count":13},{"key_as_string":"2017-02-09T16:51:00.000Z","key":1486659060000,"doc_count":15},{"key_as_string":"2017-02-09T16:52:00.000Z","key":1486659120000,"doc_count":13},{"key_as_string":"2017-02-09T16:53:00.000Z","key":1486659180000,"doc_count":16},{"key_as_string":"2017-02-09T16:54:00.000Z","key":1486659240000,"doc_count":11},{"key_as_string":"2017-02-09T16:55:00.000Z","key":1486659300000,"doc_count":18},{"key_as_string":"2017-02-09T16:56:00.000Z","key":1486659360000,"doc_count":9},{"key_as_string":"2017-02-09T16:57:00.000Z","key":1486659420000,"doc_count":19},{"key_as_string":"2017-02-09T16:58:00.000Z","key":1486659480000,"doc_count":8},{"key_as_string":"2017-02-09T16:59:00.000Z","key":1486659540000,"doc_count":12},{"key_as_string":"2017-02-09T17:00:00.000Z","key":1486659600000,"doc_count":17},{"key_as_string":"2017-02-09T17:01:00.000Z","key":1486659660000,"doc_count":14},{"key_as_string":"2017-02-09T17:02:00.000Z","key":1486659720000,"doc_count":13},{"key_as_string":"2017-02-09T17:03:00.000Z","key":1486659780000,"doc_count":9},{"key_as_string":"2017-02-09T17:04:00.000Z","key":1486659840000,"doc_count":14},{"key_as_string":"2017-02-09T17:05:00.000Z","key":1486659900000,"doc_count":15},{"key_as_string":"2017-02-09T17:06:00.000Z","key":1486659960000,"doc_count":13},{"key_as_string":"2017-02-09T17:07:00.000Z","key":1486660020000,"doc_count":9},{"key_as_string":"2017-02-09T17:08:00.000Z","key":1486660080000,"doc_count":8},{"key_as_string":"2017-02-09T17:09:00.000Z","key":1486660140000,"doc_count":11},{"key_as_string":"2017-02-09T17:10:00.000Z","key":1486660200000,"doc_count":11},{"key_as_string":"2017-02-09T17:11:00.000Z","key":1486660260000,"doc_count":12},{"key_as_string":"2017-02-09T17:12:00.000Z","key":1486660320000,"doc_count":12},{"key_as_string":"2017-02-09T17:13:00.000Z","key":1486660380000,"doc_count":9},{"key_as_string":"2017-02-09T17:14:00.000Z","key":1486660440000,"doc_count":15},{"key_as_string":"2017-02-09T17:15:00.000Z","key":1486660500000,"doc_count":11},{"key_as_string":"2017-02-09T17:16:00.000Z","key":1486660560000,"doc_count":11},{"key_as_string":"2017-02-09T17:17:00.000Z","key":1486660620000,"doc_count":11},{"key_as_string":"2017-02-09T17:18:00.000Z","key":1486660680000,"doc_count":11},{"key_as_string":"2017-02-09T17:19:00.000Z","key":1486660740000,"doc_count":15},{"key_as_string":"2017-02-09T17:20:00.000Z","key":1486660800000,"doc_count":7},{"key_as_string":"2017-02-09T17:21:00.000Z","key":1486660860000,"doc_count":15},{"key_as_string":"2017-02-09T17:22:00.000Z","key":1486660920000,"doc_count":13},{"key_as_string":"2017-02-09T17:23:00.000Z","key":1486660980000,"doc_count":10},{"key_as_string":"2017-02-09T17:24:00.000Z","key":1486661040000,"doc_count":18},{"key_as_string":"2017-02-09T17:25:00.000Z","key":1486661100000,"doc_count":9},{"key_as_string":"2017-02-09T17:26:00.000Z","key":1486661160000,"doc_count":12},{"key_as_string":"2017-02-09T17:27:00.000Z","key":1486661220000,"doc_count":8},{"key_as_string":"2017-02-09T17:28:00.000Z","key":1486661280000,"doc_count":11},{"key_as_string":"2017-02-09T17:29:00.000Z","key":1486661340000,"doc_count":10},{"key_as_string":"2017-02-09T17:30:00.000Z","key":1486661400000,"doc_count":19},{"key_as_string":"2017-02-09T17:31:00.000Z","key":1486661460000,"doc_count":10},{"key_as_string":"2017-02-09T17:32:00.000Z","key":1486661520000,"doc_count":11},{"key_as_string":"2017-02-09T17:33:00.000Z","key":1486661580000,"doc_count":12},{"key_as_string":"2017-02-09T17:34:00.000Z","key":1486661640000,"doc_count":12},{"key_as_string":"2017-02-09T17:35:00.000Z","key":1486661700000,"doc_count":15},{"key_as_string":"2017-02-09T17:36:00.000Z","key":1486661760000,"doc_count":11},{"key_as_string":"2017-02-09T17:37:00.000Z","key":1486661820000,"doc_count":12},{"key_as_string":"2017-02-09T17:38:00.000Z","key":1486661880000,"doc_count":12},{"key_as_string":"2017-02-09T17:39:00.000Z","key":1486661940000,"doc_count":14},{"key_as_string":"2017-02-09T17:40:00.000Z","key":1486662000000,"doc_count":10},{"key_as_string":"2017-02-09T17:41:00.000Z","key":1486662060000,"doc_count":13},{"key_as_string":"2017-02-09T17:42:00.000Z","key":1486662120000,"doc_count":11},{"key_as_string":"2017-02-09T17:43:00.000Z","key":1486662180000,"doc_count":16},{"key_as_string":"2017-02-09T17:44:00.000Z","key":1486662240000,"doc_count":13},{"key_as_string":"2017-02-09T17:45:00.000Z","key":1486662300000,"doc_count":7},{"key_as_string":"2017-02-09T17:46:00.000Z","key":1486662360000,"doc_count":15},{"key_as_string":"2017-02-09T17:47:00.000Z","key":1486662420000,"doc_count":11},{"key_as_string":"2017-02-09T17:48:00.000Z","key":1486662480000,"doc_count":10},{"key_as_string":"2017-02-09T17:49:00.000Z","key":1486662540000,"doc_count":13},{"key_as_string":"2017-02-09T17:50:00.000Z","key":1486662600000,"doc_count":11},{"key_as_string":"2017-02-09T17:51:00.000Z","key":1486662660000,"doc_count":19},{"key_as_string":"2017-02-09T17:52:00.000Z","key":1486662720000,"doc_count":8},{"key_as_string":"2017-02-09T17:53:00.000Z","key":1486662780000,"doc_count":12},{"key_as_string":"2017-02-09T17:54:00.000Z","key":1486662840000,"doc_count":15},{"key_as_string":"2017-02-09T17:55:00.000Z","key":1486662900000,"doc_count":9},{"key_as_string":"2017-02-09T17:56:00.000Z","key":1486662960000,"doc_count":11},{"key_as_string":"2017-02-09T17:57:00.000Z","key":1486663020000,"doc_count":14},{"key_as_string":"2017-02-09T17:58:00.000Z","key":1486663080000,"doc_count":11},{"key_as_string":"2017-02-09T17:59:00.000Z","key":1486663140000,"doc_count":18},{"key_as_string":"2017-02-09T18:00:00.000Z","key":1486663200000,"doc_count":14},{"key_as_string":"2017-02-09T18:01:00.000Z","key":1486663260000,"doc_count":11},{"key_as_string":"2017-02-09T18:02:00.000Z","key":1486663320000,"doc_count":11},{"key_as_string":"2017-02-09T18:03:00.000Z","key":1486663380000,"doc_count":13},{"key_as_string":"2017-02-09T18:04:00.000Z","key":1486663440000,"doc_count":15},{"key_as_string":"2017-02-09T18:05:00.000Z","key":1486663500000,"doc_count":10},{"key_as_string":"2017-02-09T18:06:00.000Z","key":1486663560000,"doc_count":17},{"key_as_string":"2017-02-09T18:07:00.000Z","key":1486663620000,"doc_count":14},{"key_as_string":"2017-02-09T18:08:00.000Z","key":1486663680000,"doc_count":11},{"key_as_string":"2017-02-09T18:09:00.000Z","key":1486663740000,"doc_count":15},{"key_as_string":"2017-02-09T18:10:00.000Z","key":1486663800000,"doc_count":12},{"key_as_string":"2017-02-09T18:11:00.000Z","key":1486663860000,"doc_count":7},{"key_as_string":"2017-02-09T18:12:00.000Z","key":1486663920000,"doc_count":17},{"key_as_string":"2017-02-09T18:13:00.000Z","key":1486663980000,"doc_count":14},{"key_as_string":"2017-02-09T18:14:00.000Z","key":1486664040000,"doc_count":10},{"key_as_string":"2017-02-09T18:15:00.000Z","key":1486664100000,"doc_count":13},{"key_as_string":"2017-02-09T18:16:00.000Z","key":1486664160000,"doc_count":18},{"key_as_string":"2017-02-09T18:17:00.000Z","key":1486664220000,"doc_count":7},{"key_as_string":"2017-02-09T18:18:00.000Z","key":1486664280000,"doc_count":17},{"key_as_string":"2017-02-09T18:19:00.000Z","key":1486664340000,"doc_count":10},{"key_as_string":"2017-02-09T18:20:00.000Z","key":1486664400000,"doc_count":7},{"key_as_string":"2017-02-09T18:21:00.000Z","key":1486664460000,"doc_count":12},{"key_as_string":"2017-02-09T18:22:00.000Z","key":1486664520000,"doc_count":14},{"key_as_string":"2017-02-09T18:23:00.000Z","key":1486664580000,"doc_count":14},{"key_as_string":"2017-02-09T18:24:00.000Z","key":1486664640000,"doc_count":11},{"key_as_string":"2017-02-09T18:25:00.000Z","key":1486664700000,"doc_count":13},{"key_as_string":"2017-02-09T18:26:00.000Z","key":1486664760000,"doc_count":15},{"key_as_string":"2017-02-09T18:27:00.000Z","key":1486664820000,"doc_count":6},{"key_as_string":"2017-02-09T18:28:00.000Z","key":1486664880000,"doc_count":12},{"key_as_string":"2017-02-09T18:29:00.000Z","key":1486664940000,"doc_count":15},{"key_as_string":"2017-02-09T18:30:00.000Z","key":1486665000000,"doc_count":9},{"key_as_string":"2017-02-09T18:31:00.000Z","key":1486665060000,"doc_count":13},{"key_as_string":"2017-02-09T18:32:00.000Z","key":1486665120000,"doc_count":13},{"key_as_string":"2017-02-09T18:33:00.000Z","key":1486665180000,"doc_count":14},{"key_as_string":"2017-02-09T18:34:00.000Z","key":1486665240000,"doc_count":8},{"key_as_string":"2017-02-09T18:35:00.000Z","key":1486665300000,"doc_count":9},{"key_as_string":"2017-02-09T18:36:00.000Z","key":1486665360000,"doc_count":11},{"key_as_string":"2017-02-09T18:37:00.000Z","key":1486665420000,"doc_count":12},{"key_as_string":"2017-02-09T18:38:00.000Z","key":1486665480000,"doc_count":11},{"key_as_string":"2017-02-09T18:39:00.000Z","key":1486665540000,"doc_count":12},{"key_as_string":"2017-02-09T18:40:00.000Z","key":1486665600000,"doc_count":16},{"key_as_string":"2017-02-09T18:41:00.000Z","key":1486665660000,"doc_count":7},{"key_as_string":"2017-02-09T18:42:00.000Z","key":1486665720000,"doc_count":13},{"key_as_string":"2017-02-09T18:43:00.000Z","key":1486665780000,"doc_count":13},{"key_as_string":"2017-02-09T18:44:00.000Z","key":1486665840000,"doc_count":14},{"key_as_string":"2017-02-09T18:45:00.000Z","key":1486665900000,"doc_count":7},{"key_as_string":"2017-02-09T18:46:00.000Z","key":1486665960000,"doc_count":9},{"key_as_string":"2017-02-09T18:47:00.000Z","key":1486666020000,"doc_count":11},{"key_as_string":"2017-02-09T18:48:00.000Z","key":1486666080000,"doc_count":11},{"key_as_string":"2017-02-09T18:49:00.000Z","key":1486666140000,"doc_count":11},{"key_as_string":"2017-02-09T18:50:00.000Z","key":1486666200000,"doc_count":8},{"key_as_string":"2017-02-09T18:51:00.000Z","key":1486666260000,"doc_count":15},{"key_as_string":"2017-02-09T18:52:00.000Z","key":1486666320000,"doc_count":8},{"key_as_string":"2017-02-09T18:53:00.000Z","key":1486666380000,"doc_count":13},{"key_as_string":"2017-02-09T18:54:00.000Z","key":1486666440000,"doc_count":13},{"key_as_string":"2017-02-09T18:55:00.000Z","key":1486666500000,"doc_count":11},{"key_as_string":"2017-02-09T18:56:00.000Z","key":1486666560000,"doc_count":16},{"key_as_string":"2017-02-09T18:57:00.000Z","key":1486666620000,"doc_count":6},{"key_as_string":"2017-02-09T18:58:00.000Z","key":1486666680000,"doc_count":12},{"key_as_string":"2017-02-09T18:59:00.000Z","key":1486666740000,"doc_count":12},{"key_as_string":"2017-02-09T19:00:00.000Z","key":1486666800000,"doc_count":9},{"key_as_string":"2017-02-09T19:01:00.000Z","key":1486666860000,"doc_count":12},{"key_as_string":"2017-02-09T19:02:00.000Z","key":1486666920000,"doc_count":11},{"key_as_string":"2017-02-09T19:03:00.000Z","key":1486666980000,"doc_count":14},{"key_as_string":"2017-02-09T19:04:00.000Z","key":1486667040000,"doc_count":10},{"key_as_string":"2017-02-09T19:05:00.000Z","key":1486667100000,"doc_count":9},{"key_as_string":"2017-02-09T19:06:00.000Z","key":1486667160000,"doc_count":8},{"key_as_string":"2017-02-09T19:07:00.000Z","key":1486667220000,"doc_count":19},{"key_as_string":"2017-02-09T19:08:00.000Z","key":1486667280000,"doc_count":8},{"key_as_string":"2017-02-09T19:09:00.000Z","key":1486667340000,"doc_count":12},{"key_as_string":"2017-02-09T19:10:00.000Z","key":1486667400000,"doc_count":10},{"key_as_string":"2017-02-09T19:11:00.000Z","key":1486667460000,"doc_count":9},{"key_as_string":"2017-02-09T19:12:00.000Z","key":1486667520000,"doc_count":10},{"key_as_string":"2017-02-09T19:13:00.000Z","key":1486667580000,"doc_count":7},{"key_as_string":"2017-02-09T19:14:00.000Z","key":1486667640000,"doc_count":7},{"key_as_string":"2017-02-09T19:15:00.000Z","key":1486667700000,"doc_count":15},{"key_as_string":"2017-02-09T19:16:00.000Z","key":1486667760000,"doc_count":8},{"key_as_string":"2017-02-09T19:17:00.000Z","key":1486667820000,"doc_count":9},{"key_as_string":"2017-02-09T19:18:00.000Z","key":1486667880000,"doc_count":14},{"key_as_string":"2017-02-09T19:19:00.000Z","key":1486667940000,"doc_count":10},{"key_as_string":"2017-02-09T19:20:00.000Z","key":1486668000000,"doc_count":8},{"key_as_string":"2017-02-09T19:21:00.000Z","key":1486668060000,"doc_count":19},{"key_as_string":"2017-02-09T19:22:00.000Z","key":1486668120000,"doc_count":7},{"key_as_string":"2017-02-09T19:23:00.000Z","key":1486668180000,"doc_count":9},{"key_as_string":"2017-02-09T19:24:00.000Z","key":1486668240000,"doc_count":9},{"key_as_string":"2017-02-09T19:25:00.000Z","key":1486668300000,"doc_count":14},{"key_as_string":"2017-02-09T19:26:00.000Z","key":1486668360000,"doc_count":15},{"key_as_string":"2017-02-09T19:27:00.000Z","key":1486668420000,"doc_count":13},{"key_as_string":"2017-02-09T19:28:00.000Z","key":1486668480000,"doc_count":15},{"key_as_string":"2017-02-09T19:29:00.000Z","key":1486668540000,"doc_count":9},{"key_as_string":"2017-02-09T19:30:00.000Z","key":1486668600000,"doc_count":7},{"key_as_string":"2017-02-09T19:31:00.000Z","key":1486668660000,"doc_count":9},{"key_as_string":"2017-02-09T19:32:00.000Z","key":1486668720000,"doc_count":6},{"key_as_string":"2017-02-09T19:33:00.000Z","key":1486668780000,"doc_count":16},{"key_as_string":"2017-02-09T19:34:00.000Z","key":1486668840000,"doc_count":14},{"key_as_string":"2017-02-09T19:35:00.000Z","key":1486668900000,"doc_count":12},{"key_as_string":"2017-02-09T19:36:00.000Z","key":1486668960000,"doc_count":12},{"key_as_string":"2017-02-09T19:37:00.000Z","key":1486669020000,"doc_count":11},{"key_as_string":"2017-02-09T19:38:00.000Z","key":1486669080000,"doc_count":12},{"key_as_string":"2017-02-09T19:39:00.000Z","key":1486669140000,"doc_count":13},{"key_as_string":"2017-02-09T19:40:00.000Z","key":1486669200000,"doc_count":6},{"key_as_string":"2017-02-09T19:41:00.000Z","key":1486669260000,"doc_count":14},{"key_as_string":"2017-02-09T19:42:00.000Z","key":1486669320000,"doc_count":15},{"key_as_string":"2017-02-09T19:43:00.000Z","key":1486669380000,"doc_count":9},{"key_as_string":"2017-02-09T19:44:00.000Z","key":1486669440000,"doc_count":7},{"key_as_string":"2017-02-09T19:45:00.000Z","key":1486669500000,"doc_count":15},{"key_as_string":"2017-02-09T19:46:00.000Z","key":1486669560000,"doc_count":10},{"key_as_string":"2017-02-09T19:47:00.000Z","key":1486669620000,"doc_count":9},{"key_as_string":"2017-02-09T19:48:00.000Z","key":1486669680000,"doc_count":9},{"key_as_string":"2017-02-09T19:49:00.000Z","key":1486669740000,"doc_count":14},{"key_as_string":"2017-02-09T19:50:00.000Z","key":1486669800000,"doc_count":8},{"key_as_string":"2017-02-09T19:51:00.000Z","key":1486669860000,"doc_count":12},{"key_as_string":"2017-02-09T19:52:00.000Z","key":1486669920000,"doc_count":8},{"key_as_string":"2017-02-09T19:53:00.000Z","key":1486669980000,"doc_count":6},{"key_as_string":"2017-02-09T19:54:00.000Z","key":1486670040000,"doc_count":13},{"key_as_string":"2017-02-09T19:55:00.000Z","key":1486670100000,"doc_count":14},{"key_as_string":"2017-02-09T19:56:00.000Z","key":1486670160000,"doc_count":10},{"key_as_string":"2017-02-09T19:57:00.000Z","key":1486670220000,"doc_count":15},{"key_as_string":"2017-02-09T19:58:00.000Z","key":1486670280000,"doc_count":11},{"key_as_string":"2017-02-09T19:59:00.000Z","key":1486670340000,"doc_count":11},{"key_as_string":"2017-02-09T20:00:00.000Z","key":1486670400000,"doc_count":14},{"key_as_string":"2017-02-09T20:01:00.000Z","key":1486670460000,"doc_count":16},{"key_as_string":"2017-02-09T20:02:00.000Z","key":1486670520000,"doc_count":11},{"key_as_string":"2017-02-09T20:03:00.000Z","key":1486670580000,"doc_count":11},{"key_as_string":"2017-02-09T20:04:00.000Z","key":1486670640000,"doc_count":14},{"key_as_string":"2017-02-09T20:05:00.000Z","key":1486670700000,"doc_count":6},{"key_as_string":"2017-02-09T20:06:00.000Z","key":1486670760000,"doc_count":12},{"key_as_string":"2017-02-09T20:07:00.000Z","key":1486670820000,"doc_count":14},{"key_as_string":"2017-02-09T20:08:00.000Z","key":1486670880000,"doc_count":11},{"key_as_string":"2017-02-09T20:09:00.000Z","key":1486670940000,"doc_count":15},{"key_as_string":"2017-02-09T20:10:00.000Z","key":1486671000000,"doc_count":14},{"key_as_string":"2017-02-09T20:11:00.000Z","key":1486671060000,"doc_count":13},{"key_as_string":"2017-02-09T20:12:00.000Z","key":1486671120000,"doc_count":10},{"key_as_string":"2017-02-09T20:13:00.000Z","key":1486671180000,"doc_count":12},{"key_as_string":"2017-02-09T20:14:00.000Z","key":1486671240000,"doc_count":10},{"key_as_string":"2017-02-09T20:15:00.000Z","key":1486671300000,"doc_count":9},{"key_as_string":"2017-02-09T20:16:00.000Z","key":1486671360000,"doc_count":12},{"key_as_string":"2017-02-09T20:17:00.000Z","key":1486671420000,"doc_count":9},{"key_as_string":"2017-02-09T20:18:00.000Z","key":1486671480000,"doc_count":14},{"key_as_string":"2017-02-09T20:19:00.000Z","key":1486671540000,"doc_count":12},{"key_as_string":"2017-02-09T20:20:00.000Z","key":1486671600000,"doc_count":11},{"key_as_string":"2017-02-09T20:21:00.000Z","key":1486671660000,"doc_count":10},{"key_as_string":"2017-02-09T20:22:00.000Z","key":1486671720000,"doc_count":14},{"key_as_string":"2017-02-09T20:23:00.000Z","key":1486671780000,"doc_count":10},{"key_as_string":"2017-02-09T20:24:00.000Z","key":1486671840000,"doc_count":12},{"key_as_string":"2017-02-09T20:25:00.000Z","key":1486671900000,"doc_count":8},{"key_as_string":"2017-02-09T20:26:00.000Z","key":1486671960000,"doc_count":14},{"key_as_string":"2017-02-09T20:27:00.000Z","key":1486672020000,"doc_count":7},{"key_as_string":"2017-02-09T20:28:00.000Z","key":1486672080000,"doc_count":12},{"key_as_string":"2017-02-09T20:29:00.000Z","key":1486672140000,"doc_count":11},{"key_as_string":"2017-02-09T20:30:00.000Z","key":1486672200000,"doc_count":15},{"key_as_string":"2017-02-09T20:31:00.000Z","key":1486672260000,"doc_count":11},{"key_as_string":"2017-02-09T20:32:00.000Z","key":1486672320000,"doc_count":11},{"key_as_string":"2017-02-09T20:33:00.000Z","key":1486672380000,"doc_count":7},{"key_as_string":"2017-02-09T20:34:00.000Z","key":1486672440000,"doc_count":10},{"key_as_string":"2017-02-09T20:35:00.000Z","key":1486672500000,"doc_count":13},{"key_as_string":"2017-02-09T20:36:00.000Z","key":1486672560000,"doc_count":6},{"key_as_string":"2017-02-09T20:37:00.000Z","key":1486672620000,"doc_count":9},{"key_as_string":"2017-02-09T20:38:00.000Z","key":1486672680000,"doc_count":7},{"key_as_string":"2017-02-09T20:39:00.000Z","key":1486672740000,"doc_count":10},{"key_as_string":"2017-02-09T20:40:00.000Z","key":1486672800000,"doc_count":8},{"key_as_string":"2017-02-09T20:41:00.000Z","key":1486672860000,"doc_count":12},{"key_as_string":"2017-02-09T20:42:00.000Z","key":1486672920000,"doc_count":6},{"key_as_string":"2017-02-09T20:43:00.000Z","key":1486672980000,"doc_count":14},{"key_as_string":"2017-02-09T20:44:00.000Z","key":1486673040000,"doc_count":6},{"key_as_string":"2017-02-09T20:45:00.000Z","key":1486673100000,"doc_count":8},{"key_as_string":"2017-02-09T20:46:00.000Z","key":1486673160000,"doc_count":14},{"key_as_string":"2017-02-09T20:47:00.000Z","key":1486673220000,"doc_count":10},{"key_as_string":"2017-02-09T20:48:00.000Z","key":1486673280000,"doc_count":9},{"key_as_string":"2017-02-09T20:49:00.000Z","key":1486673340000,"doc_count":11},{"key_as_string":"2017-02-09T20:50:00.000Z","key":1486673400000,"doc_count":14},{"key_as_string":"2017-02-09T20:51:00.000Z","key":1486673460000,"doc_count":6},{"key_as_string":"2017-02-09T20:52:00.000Z","key":1486673520000,"doc_count":9},{"key_as_string":"2017-02-09T20:53:00.000Z","key":1486673580000,"doc_count":10},{"key_as_string":"2017-02-09T20:54:00.000Z","key":1486673640000,"doc_count":13},{"key_as_string":"2017-02-09T20:55:00.000Z","key":1486673700000,"doc_count":11},{"key_as_string":"2017-02-09T20:56:00.000Z","key":1486673760000,"doc_count":6},{"key_as_string":"2017-02-09T20:57:00.000Z","key":1486673820000,"doc_count":10},{"key_as_string":"2017-02-09T20:58:00.000Z","key":1486673880000,"doc_count":10},{"key_as_string":"2017-02-09T20:59:00.000Z","key":1486673940000,"doc_count":9},{"key_as_string":"2017-02-09T21:00:00.000Z","key":1486674000000,"doc_count":17},{"key_as_string":"2017-02-09T21:01:00.000Z","key":1486674060000,"doc_count":11},{"key_as_string":"2017-02-09T21:02:00.000Z","key":1486674120000,"doc_count":10},{"key_as_string":"2017-02-09T21:03:00.000Z","key":1486674180000,"doc_count":9},{"key_as_string":"2017-02-09T21:04:00.000Z","key":1486674240000,"doc_count":14},{"key_as_string":"2017-02-09T21:05:00.000Z","key":1486674300000,"doc_count":10},{"key_as_string":"2017-02-09T21:06:00.000Z","key":1486674360000,"doc_count":12},{"key_as_string":"2017-02-09T21:07:00.000Z","key":1486674420000,"doc_count":9},{"key_as_string":"2017-02-09T21:08:00.000Z","key":1486674480000,"doc_count":11},{"key_as_string":"2017-02-09T21:09:00.000Z","key":1486674540000,"doc_count":11},{"key_as_string":"2017-02-09T21:10:00.000Z","key":1486674600000,"doc_count":11},{"key_as_string":"2017-02-09T21:11:00.000Z","key":1486674660000,"doc_count":10},{"key_as_string":"2017-02-09T21:12:00.000Z","key":1486674720000,"doc_count":9},{"key_as_string":"2017-02-09T21:13:00.000Z","key":1486674780000,"doc_count":13},{"key_as_string":"2017-02-09T21:14:00.000Z","key":1486674840000,"doc_count":9},{"key_as_string":"2017-02-09T21:15:00.000Z","key":1486674900000,"doc_count":8},{"key_as_string":"2017-02-09T21:16:00.000Z","key":1486674960000,"doc_count":11},{"key_as_string":"2017-02-09T21:17:00.000Z","key":1486675020000,"doc_count":12},{"key_as_string":"2017-02-09T21:18:00.000Z","key":1486675080000,"doc_count":7},{"key_as_string":"2017-02-09T21:19:00.000Z","key":1486675140000,"doc_count":14},{"key_as_string":"2017-02-09T21:20:00.000Z","key":1486675200000,"doc_count":15},{"key_as_string":"2017-02-09T21:21:00.000Z","key":1486675260000,"doc_count":5},{"key_as_string":"2017-02-09T21:22:00.000Z","key":1486675320000,"doc_count":8},{"key_as_string":"2017-02-09T21:23:00.000Z","key":1486675380000,"doc_count":15},{"key_as_string":"2017-02-09T21:24:00.000Z","key":1486675440000,"doc_count":9},{"key_as_string":"2017-02-09T21:25:00.000Z","key":1486675500000,"doc_count":14},{"key_as_string":"2017-02-09T21:26:00.000Z","key":1486675560000,"doc_count":12},{"key_as_string":"2017-02-09T21:27:00.000Z","key":1486675620000,"doc_count":8},{"key_as_string":"2017-02-09T21:28:00.000Z","key":1486675680000,"doc_count":12},{"key_as_string":"2017-02-09T21:29:00.000Z","key":1486675740000,"doc_count":10},{"key_as_string":"2017-02-09T21:30:00.000Z","key":1486675800000,"doc_count":10},{"key_as_string":"2017-02-09T21:31:00.000Z","key":1486675860000,"doc_count":11},{"key_as_string":"2017-02-09T21:32:00.000Z","key":1486675920000,"doc_count":10},{"key_as_string":"2017-02-09T21:33:00.000Z","key":1486675980000,"doc_count":9},{"key_as_string":"2017-02-09T21:34:00.000Z","key":1486676040000,"doc_count":10},{"key_as_string":"2017-02-09T21:35:00.000Z","key":1486676100000,"doc_count":11},{"key_as_string":"2017-02-09T21:36:00.000Z","key":1486676160000,"doc_count":11},{"key_as_string":"2017-02-09T21:37:00.000Z","key":1486676220000,"doc_count":8},{"key_as_string":"2017-02-09T21:38:00.000Z","key":1486676280000,"doc_count":12},{"key_as_string":"2017-02-09T21:39:00.000Z","key":1486676340000,"doc_count":9},{"key_as_string":"2017-02-09T21:40:00.000Z","key":1486676400000,"doc_count":8},{"key_as_string":"2017-02-09T21:41:00.000Z","key":1486676460000,"doc_count":10},{"key_as_string":"2017-02-09T21:42:00.000Z","key":1486676520000,"doc_count":8},{"key_as_string":"2017-02-09T21:43:00.000Z","key":1486676580000,"doc_count":12},{"key_as_string":"2017-02-09T21:44:00.000Z","key":1486676640000,"doc_count":10},{"key_as_string":"2017-02-09T21:45:00.000Z","key":1486676700000,"doc_count":14},{"key_as_string":"2017-02-09T21:46:00.000Z","key":1486676760000,"doc_count":11},{"key_as_string":"2017-02-09T21:47:00.000Z","key":1486676820000,"doc_count":8},{"key_as_string":"2017-02-09T21:48:00.000Z","key":1486676880000,"doc_count":12},{"key_as_string":"2017-02-09T21:49:00.000Z","key":1486676940000,"doc_count":12},{"key_as_string":"2017-02-09T21:50:00.000Z","key":1486677000000,"doc_count":9},{"key_as_string":"2017-02-09T21:51:00.000Z","key":1486677060000,"doc_count":11},{"key_as_string":"2017-02-09T21:52:00.000Z","key":1486677120000,"doc_count":10},{"key_as_string":"2017-02-09T21:53:00.000Z","key":1486677180000,"doc_count":11},{"key_as_string":"2017-02-09T21:54:00.000Z","key":1486677240000,"doc_count":14},{"key_as_string":"2017-02-09T21:55:00.000Z","key":1486677300000,"doc_count":9},{"key_as_string":"2017-02-09T21:56:00.000Z","key":1486677360000,"doc_count":8},{"key_as_string":"2017-02-09T21:57:00.000Z","key":1486677420000,"doc_count":10},{"key_as_string":"2017-02-09T21:58:00.000Z","key":1486677480000,"doc_count":8},{"key_as_string":"2017-02-09T21:59:00.000Z","key":1486677540000,"doc_count":16},{"key_as_string":"2017-02-09T22:00:00.000Z","key":1486677600000,"doc_count":11},{"key_as_string":"2017-02-09T22:01:00.000Z","key":1486677660000,"doc_count":7},{"key_as_string":"2017-02-09T22:02:00.000Z","key":1486677720000,"doc_count":13},{"key_as_string":"2017-02-09T22:03:00.000Z","key":1486677780000,"doc_count":12},{"key_as_string":"2017-02-09T22:04:00.000Z","key":1486677840000,"doc_count":9},{"key_as_string":"2017-02-09T22:05:00.000Z","key":1486677900000,"doc_count":14},{"key_as_string":"2017-02-09T22:06:00.000Z","key":1486677960000,"doc_count":8},{"key_as_string":"2017-02-09T22:07:00.000Z","key":1486678020000,"doc_count":9},{"key_as_string":"2017-02-09T22:08:00.000Z","key":1486678080000,"doc_count":15},{"key_as_string":"2017-02-09T22:09:00.000Z","key":1486678140000,"doc_count":10},{"key_as_string":"2017-02-09T22:10:00.000Z","key":1486678200000,"doc_count":7},{"key_as_string":"2017-02-09T22:11:00.000Z","key":1486678260000,"doc_count":11},{"key_as_string":"2017-02-09T22:12:00.000Z","key":1486678320000,"doc_count":8},{"key_as_string":"2017-02-09T22:13:00.000Z","key":1486678380000,"doc_count":13},{"key_as_string":"2017-02-09T22:14:00.000Z","key":1486678440000,"doc_count":12},{"key_as_string":"2017-02-09T22:15:00.000Z","key":1486678500000,"doc_count":6},{"key_as_string":"2017-02-09T22:16:00.000Z","key":1486678560000,"doc_count":9},{"key_as_string":"2017-02-09T22:17:00.000Z","key":1486678620000,"doc_count":13},{"key_as_string":"2017-02-09T22:18:00.000Z","key":1486678680000,"doc_count":12},{"key_as_string":"2017-02-09T22:19:00.000Z","key":1486678740000,"doc_count":9},{"key_as_string":"2017-02-09T22:20:00.000Z","key":1486678800000,"doc_count":7},{"key_as_string":"2017-02-09T22:21:00.000Z","key":1486678860000,"doc_count":8},{"key_as_string":"2017-02-09T22:22:00.000Z","key":1486678920000,"doc_count":12},{"key_as_string":"2017-02-09T22:23:00.000Z","key":1486678980000,"doc_count":7},{"key_as_string":"2017-02-09T22:24:00.000Z","key":1486679040000,"doc_count":15},{"key_as_string":"2017-02-09T22:25:00.000Z","key":1486679100000,"doc_count":12},{"key_as_string":"2017-02-09T22:26:00.000Z","key":1486679160000,"doc_count":12},{"key_as_string":"2017-02-09T22:27:00.000Z","key":1486679220000,"doc_count":12},{"key_as_string":"2017-02-09T22:28:00.000Z","key":1486679280000,"doc_count":11},{"key_as_string":"2017-02-09T22:29:00.000Z","key":1486679340000,"doc_count":7},{"key_as_string":"2017-02-09T22:30:00.000Z","key":1486679400000,"doc_count":9},{"key_as_string":"2017-02-09T22:31:00.000Z","key":1486679460000,"doc_count":10},{"key_as_string":"2017-02-09T22:32:00.000Z","key":1486679520000,"doc_count":14},{"key_as_string":"2017-02-09T22:33:00.000Z","key":1486679580000,"doc_count":10},{"key_as_string":"2017-02-09T22:34:00.000Z","key":1486679640000,"doc_count":14},{"key_as_string":"2017-02-09T22:35:00.000Z","key":1486679700000,"doc_count":13},{"key_as_string":"2017-02-09T22:36:00.000Z","key":1486679760000,"doc_count":9},{"key_as_string":"2017-02-09T22:37:00.000Z","key":1486679820000,"doc_count":7},{"key_as_string":"2017-02-09T22:38:00.000Z","key":1486679880000,"doc_count":11},{"key_as_string":"2017-02-09T22:39:00.000Z","key":1486679940000,"doc_count":8},{"key_as_string":"2017-02-09T22:40:00.000Z","key":1486680000000,"doc_count":9},{"key_as_string":"2017-02-09T22:41:00.000Z","key":1486680060000,"doc_count":9},{"key_as_string":"2017-02-09T22:42:00.000Z","key":1486680120000,"doc_count":15},{"key_as_string":"2017-02-09T22:43:00.000Z","key":1486680180000,"doc_count":8},{"key_as_string":"2017-02-09T22:44:00.000Z","key":1486680240000,"doc_count":7},{"key_as_string":"2017-02-09T22:45:00.000Z","key":1486680300000,"doc_count":10},{"key_as_string":"2017-02-09T22:46:00.000Z","key":1486680360000,"doc_count":13},{"key_as_string":"2017-02-09T22:47:00.000Z","key":1486680420000,"doc_count":14},{"key_as_string":"2017-02-09T22:48:00.000Z","key":1486680480000,"doc_count":7},{"key_as_string":"2017-02-09T22:49:00.000Z","key":1486680540000,"doc_count":8},{"key_as_string":"2017-02-09T22:50:00.000Z","key":1486680600000,"doc_count":12},{"key_as_string":"2017-02-09T22:51:00.000Z","key":1486680660000,"doc_count":9},{"key_as_string":"2017-02-09T22:52:00.000Z","key":1486680720000,"doc_count":9},{"key_as_string":"2017-02-09T22:53:00.000Z","key":1486680780000,"doc_count":6},{"key_as_string":"2017-02-09T22:54:00.000Z","key":1486680840000,"doc_count":17},{"key_as_string":"2017-02-09T22:55:00.000Z","key":1486680900000,"doc_count":10},{"key_as_string":"2017-02-09T22:56:00.000Z","key":1486680960000,"doc_count":9},{"key_as_string":"2017-02-09T22:57:00.000Z","key":1486681020000,"doc_count":12},{"key_as_string":"2017-02-09T22:58:00.000Z","key":1486681080000,"doc_count":10},{"key_as_string":"2017-02-09T22:59:00.000Z","key":1486681140000,"doc_count":11},{"key_as_string":"2017-02-09T23:00:00.000Z","key":1486681200000,"doc_count":6},{"key_as_string":"2017-02-09T23:01:00.000Z","key":1486681260000,"doc_count":11},{"key_as_string":"2017-02-09T23:02:00.000Z","key":1486681320000,"doc_count":11},{"key_as_string":"2017-02-09T23:03:00.000Z","key":1486681380000,"doc_count":13},{"key_as_string":"2017-02-09T23:04:00.000Z","key":1486681440000,"doc_count":16},{"key_as_string":"2017-02-09T23:05:00.000Z","key":1486681500000,"doc_count":10},{"key_as_string":"2017-02-09T23:06:00.000Z","key":1486681560000,"doc_count":9},{"key_as_string":"2017-02-09T23:07:00.000Z","key":1486681620000,"doc_count":6},{"key_as_string":"2017-02-09T23:08:00.000Z","key":1486681680000,"doc_count":11},{"key_as_string":"2017-02-09T23:09:00.000Z","key":1486681740000,"doc_count":12},{"key_as_string":"2017-02-09T23:10:00.000Z","key":1486681800000,"doc_count":4},{"key_as_string":"2017-02-09T23:11:00.000Z","key":1486681860000,"doc_count":12},{"key_as_string":"2017-02-09T23:12:00.000Z","key":1486681920000,"doc_count":12},{"key_as_string":"2017-02-09T23:13:00.000Z","key":1486681980000,"doc_count":8},{"key_as_string":"2017-02-09T23:14:00.000Z","key":1486682040000,"doc_count":12},{"key_as_string":"2017-02-09T23:15:00.000Z","key":1486682100000,"doc_count":8},{"key_as_string":"2017-02-09T23:16:00.000Z","key":1486682160000,"doc_count":11},{"key_as_string":"2017-02-09T23:17:00.000Z","key":1486682220000,"doc_count":12},{"key_as_string":"2017-02-09T23:18:00.000Z","key":1486682280000,"doc_count":6},{"key_as_string":"2017-02-09T23:19:00.000Z","key":1486682340000,"doc_count":13},{"key_as_string":"2017-02-09T23:20:00.000Z","key":1486682400000,"doc_count":9},{"key_as_string":"2017-02-09T23:21:00.000Z","key":1486682460000,"doc_count":7},{"key_as_string":"2017-02-09T23:22:00.000Z","key":1486682520000,"doc_count":18},{"key_as_string":"2017-02-09T23:23:00.000Z","key":1486682580000,"doc_count":6},{"key_as_string":"2017-02-09T23:24:00.000Z","key":1486682640000,"doc_count":10},{"key_as_string":"2017-02-09T23:25:00.000Z","key":1486682700000,"doc_count":10},{"key_as_string":"2017-02-09T23:26:00.000Z","key":1486682760000,"doc_count":13},{"key_as_string":"2017-02-09T23:27:00.000Z","key":1486682820000,"doc_count":12},{"key_as_string":"2017-02-09T23:28:00.000Z","key":1486682880000,"doc_count":14},{"key_as_string":"2017-02-09T23:29:00.000Z","key":1486682940000,"doc_count":7},{"key_as_string":"2017-02-09T23:30:00.000Z","key":1486683000000,"doc_count":8},{"key_as_string":"2017-02-09T23:31:00.000Z","key":1486683060000,"doc_count":13},{"key_as_string":"2017-02-09T23:32:00.000Z","key":1486683120000,"doc_count":7},{"key_as_string":"2017-02-09T23:33:00.000Z","key":1486683180000,"doc_count":10},{"key_as_string":"2017-02-09T23:34:00.000Z","key":1486683240000,"doc_count":9},{"key_as_string":"2017-02-09T23:35:00.000Z","key":1486683300000,"doc_count":8},{"key_as_string":"2017-02-09T23:36:00.000Z","key":1486683360000,"doc_count":11},{"key_as_string":"2017-02-09T23:37:00.000Z","key":1486683420000,"doc_count":14},{"key_as_string":"2017-02-09T23:38:00.000Z","key":1486683480000,"doc_count":11},{"key_as_string":"2017-02-09T23:39:00.000Z","key":1486683540000,"doc_count":8},{"key_as_string":"2017-02-09T23:40:00.000Z","key":1486683600000,"doc_count":12},{"key_as_string":"2017-02-09T23:41:00.000Z","key":1486683660000,"doc_count":12},{"key_as_string":"2017-02-09T23:42:00.000Z","key":1486683720000,"doc_count":9},{"key_as_string":"2017-02-09T23:43:00.000Z","key":1486683780000,"doc_count":16},{"key_as_string":"2017-02-09T23:44:00.000Z","key":1486683840000,"doc_count":11},{"key_as_string":"2017-02-09T23:45:00.000Z","key":1486683900000,"doc_count":7},{"key_as_string":"2017-02-09T23:46:00.000Z","key":1486683960000,"doc_count":12},{"key_as_string":"2017-02-09T23:47:00.000Z","key":1486684020000,"doc_count":15},{"key_as_string":"2017-02-09T23:48:00.000Z","key":1486684080000,"doc_count":7},{"key_as_string":"2017-02-09T23:49:00.000Z","key":1486684140000,"doc_count":10},{"key_as_string":"2017-02-09T23:50:00.000Z","key":1486684200000,"doc_count":15},{"key_as_string":"2017-02-09T23:51:00.000Z","key":1486684260000,"doc_count":12},{"key_as_string":"2017-02-09T23:52:00.000Z","key":1486684320000,"doc_count":9},{"key_as_string":"2017-02-09T23:53:00.000Z","key":1486684380000,"doc_count":7},{"key_as_string":"2017-02-09T23:54:00.000Z","key":1486684440000,"doc_count":8},{"key_as_string":"2017-02-09T23:55:00.000Z","key":1486684500000,"doc_count":8},{"key_as_string":"2017-02-09T23:56:00.000Z","key":1486684560000,"doc_count":13},{"key_as_string":"2017-02-09T23:57:00.000Z","key":1486684620000,"doc_count":12},{"key_as_string":"2017-02-09T23:58:00.000Z","key":1486684680000,"doc_count":12},{"key_as_string":"2017-02-09T23:59:00.000Z","key":1486684740000,"doc_count":9},{"key_as_string":"2017-02-10T00:00:00.000Z","key":1486684800000,"doc_count":8},{"key_as_string":"2017-02-10T00:01:00.000Z","key":1486684860000,"doc_count":10},{"key_as_string":"2017-02-10T00:02:00.000Z","key":1486684920000,"doc_count":12},{"key_as_string":"2017-02-10T00:03:00.000Z","key":1486684980000,"doc_count":11},{"key_as_string":"2017-02-10T00:04:00.000Z","key":1486685040000,"doc_count":11},{"key_as_string":"2017-02-10T00:05:00.000Z","key":1486685100000,"doc_count":12},{"key_as_string":"2017-02-10T00:06:00.000Z","key":1486685160000,"doc_count":9},{"key_as_string":"2017-02-10T00:07:00.000Z","key":1486685220000,"doc_count":11},{"key_as_string":"2017-02-10T00:08:00.000Z","key":1486685280000,"doc_count":8},{"key_as_string":"2017-02-10T00:09:00.000Z","key":1486685340000,"doc_count":11},{"key_as_string":"2017-02-10T00:10:00.000Z","key":1486685400000,"doc_count":10},{"key_as_string":"2017-02-10T00:11:00.000Z","key":1486685460000,"doc_count":14},{"key_as_string":"2017-02-10T00:12:00.000Z","key":1486685520000,"doc_count":5},{"key_as_string":"2017-02-10T00:13:00.000Z","key":1486685580000,"doc_count":6},{"key_as_string":"2017-02-10T00:14:00.000Z","key":1486685640000,"doc_count":8},{"key_as_string":"2017-02-10T00:15:00.000Z","key":1486685700000,"doc_count":11},{"key_as_string":"2017-02-10T00:16:00.000Z","key":1486685760000,"doc_count":15},{"key_as_string":"2017-02-10T00:17:00.000Z","key":1486685820000,"doc_count":8},{"key_as_string":"2017-02-10T00:18:00.000Z","key":1486685880000,"doc_count":12},{"key_as_string":"2017-02-10T00:19:00.000Z","key":1486685940000,"doc_count":7},{"key_as_string":"2017-02-10T00:20:00.000Z","key":1486686000000,"doc_count":11},{"key_as_string":"2017-02-10T00:21:00.000Z","key":1486686060000,"doc_count":20},{"key_as_string":"2017-02-10T00:22:00.000Z","key":1486686120000,"doc_count":7},{"key_as_string":"2017-02-10T00:23:00.000Z","key":1486686180000,"doc_count":11},{"key_as_string":"2017-02-10T00:24:00.000Z","key":1486686240000,"doc_count":8},{"key_as_string":"2017-02-10T00:25:00.000Z","key":1486686300000,"doc_count":9},{"key_as_string":"2017-02-10T00:26:00.000Z","key":1486686360000,"doc_count":15},{"key_as_string":"2017-02-10T00:27:00.000Z","key":1486686420000,"doc_count":12},{"key_as_string":"2017-02-10T00:28:00.000Z","key":1486686480000,"doc_count":13},{"key_as_string":"2017-02-10T00:29:00.000Z","key":1486686540000,"doc_count":10},{"key_as_string":"2017-02-10T00:30:00.000Z","key":1486686600000,"doc_count":14},{"key_as_string":"2017-02-10T00:31:00.000Z","key":1486686660000,"doc_count":9},{"key_as_string":"2017-02-10T00:32:00.000Z","key":1486686720000,"doc_count":6},{"key_as_string":"2017-02-10T00:33:00.000Z","key":1486686780000,"doc_count":13},{"key_as_string":"2017-02-10T00:34:00.000Z","key":1486686840000,"doc_count":8},{"key_as_string":"2017-02-10T00:35:00.000Z","key":1486686900000,"doc_count":12},{"key_as_string":"2017-02-10T00:36:00.000Z","key":1486686960000,"doc_count":10},{"key_as_string":"2017-02-10T00:37:00.000Z","key":1486687020000,"doc_count":11},{"key_as_string":"2017-02-10T00:38:00.000Z","key":1486687080000,"doc_count":12},{"key_as_string":"2017-02-10T00:39:00.000Z","key":1486687140000,"doc_count":11},{"key_as_string":"2017-02-10T00:40:00.000Z","key":1486687200000,"doc_count":9},{"key_as_string":"2017-02-10T00:41:00.000Z","key":1486687260000,"doc_count":15},{"key_as_string":"2017-02-10T00:42:00.000Z","key":1486687320000,"doc_count":11},{"key_as_string":"2017-02-10T00:43:00.000Z","key":1486687380000,"doc_count":10},{"key_as_string":"2017-02-10T00:44:00.000Z","key":1486687440000,"doc_count":6},{"key_as_string":"2017-02-10T00:45:00.000Z","key":1486687500000,"doc_count":9},{"key_as_string":"2017-02-10T00:46:00.000Z","key":1486687560000,"doc_count":6},{"key_as_string":"2017-02-10T00:47:00.000Z","key":1486687620000,"doc_count":15},{"key_as_string":"2017-02-10T00:48:00.000Z","key":1486687680000,"doc_count":5},{"key_as_string":"2017-02-10T00:49:00.000Z","key":1486687740000,"doc_count":8},{"key_as_string":"2017-02-10T00:50:00.000Z","key":1486687800000,"doc_count":14},{"key_as_string":"2017-02-10T00:51:00.000Z","key":1486687860000,"doc_count":8},{"key_as_string":"2017-02-10T00:52:00.000Z","key":1486687920000,"doc_count":10},{"key_as_string":"2017-02-10T00:53:00.000Z","key":1486687980000,"doc_count":11},{"key_as_string":"2017-02-10T00:54:00.000Z","key":1486688040000,"doc_count":11},{"key_as_string":"2017-02-10T00:55:00.000Z","key":1486688100000,"doc_count":14},{"key_as_string":"2017-02-10T00:56:00.000Z","key":1486688160000,"doc_count":11},{"key_as_string":"2017-02-10T00:57:00.000Z","key":1486688220000,"doc_count":9},{"key_as_string":"2017-02-10T00:58:00.000Z","key":1486688280000,"doc_count":10},{"key_as_string":"2017-02-10T00:59:00.000Z","key":1486688340000,"doc_count":12},{"key_as_string":"2017-02-10T01:00:00.000Z","key":1486688400000,"doc_count":8},{"key_as_string":"2017-02-10T01:01:00.000Z","key":1486688460000,"doc_count":14},{"key_as_string":"2017-02-10T01:02:00.000Z","key":1486688520000,"doc_count":10},{"key_as_string":"2017-02-10T01:03:00.000Z","key":1486688580000,"doc_count":7},{"key_as_string":"2017-02-10T01:04:00.000Z","key":1486688640000,"doc_count":14},{"key_as_string":"2017-02-10T01:05:00.000Z","key":1486688700000,"doc_count":5},{"key_as_string":"2017-02-10T01:06:00.000Z","key":1486688760000,"doc_count":19},{"key_as_string":"2017-02-10T01:07:00.000Z","key":1486688820000,"doc_count":7},{"key_as_string":"2017-02-10T01:08:00.000Z","key":1486688880000,"doc_count":14},{"key_as_string":"2017-02-10T01:09:00.000Z","key":1486688940000,"doc_count":6},{"key_as_string":"2017-02-10T01:10:00.000Z","key":1486689000000,"doc_count":11},{"key_as_string":"2017-02-10T01:11:00.000Z","key":1486689060000,"doc_count":16},{"key_as_string":"2017-02-10T01:12:00.000Z","key":1486689120000,"doc_count":11},{"key_as_string":"2017-02-10T01:13:00.000Z","key":1486689180000,"doc_count":6},{"key_as_string":"2017-02-10T01:14:00.000Z","key":1486689240000,"doc_count":10},{"key_as_string":"2017-02-10T01:15:00.000Z","key":1486689300000,"doc_count":10},{"key_as_string":"2017-02-10T01:16:00.000Z","key":1486689360000,"doc_count":10},{"key_as_string":"2017-02-10T01:17:00.000Z","key":1486689420000,"doc_count":11},{"key_as_string":"2017-02-10T01:18:00.000Z","key":1486689480000,"doc_count":7},{"key_as_string":"2017-02-10T01:19:00.000Z","key":1486689540000,"doc_count":8},{"key_as_string":"2017-02-10T01:20:00.000Z","key":1486689600000,"doc_count":17},{"key_as_string":"2017-02-10T01:21:00.000Z","key":1486689660000,"doc_count":12},{"key_as_string":"2017-02-10T01:22:00.000Z","key":1486689720000,"doc_count":12},{"key_as_string":"2017-02-10T01:23:00.000Z","key":1486689780000,"doc_count":12},{"key_as_string":"2017-02-10T01:24:00.000Z","key":1486689840000,"doc_count":13},{"key_as_string":"2017-02-10T01:25:00.000Z","key":1486689900000,"doc_count":9},{"key_as_string":"2017-02-10T01:26:00.000Z","key":1486689960000,"doc_count":8},{"key_as_string":"2017-02-10T01:27:00.000Z","key":1486690020000,"doc_count":9},{"key_as_string":"2017-02-10T01:28:00.000Z","key":1486690080000,"doc_count":10},{"key_as_string":"2017-02-10T01:29:00.000Z","key":1486690140000,"doc_count":11},{"key_as_string":"2017-02-10T01:30:00.000Z","key":1486690200000,"doc_count":5},{"key_as_string":"2017-02-10T01:31:00.000Z","key":1486690260000,"doc_count":12},{"key_as_string":"2017-02-10T01:32:00.000Z","key":1486690320000,"doc_count":8},{"key_as_string":"2017-02-10T01:33:00.000Z","key":1486690380000,"doc_count":8},{"key_as_string":"2017-02-10T01:34:00.000Z","key":1486690440000,"doc_count":11},{"key_as_string":"2017-02-10T01:35:00.000Z","key":1486690500000,"doc_count":7},{"key_as_string":"2017-02-10T01:36:00.000Z","key":1486690560000,"doc_count":16},{"key_as_string":"2017-02-10T01:37:00.000Z","key":1486690620000,"doc_count":9},{"key_as_string":"2017-02-10T01:38:00.000Z","key":1486690680000,"doc_count":3},{"key_as_string":"2017-02-10T01:39:00.000Z","key":1486690740000,"doc_count":14},{"key_as_string":"2017-02-10T01:40:00.000Z","key":1486690800000,"doc_count":3},{"key_as_string":"2017-02-10T01:41:00.000Z","key":1486690860000,"doc_count":14},{"key_as_string":"2017-02-10T01:42:00.000Z","key":1486690920000,"doc_count":11},{"key_as_string":"2017-02-10T01:43:00.000Z","key":1486690980000,"doc_count":8},{"key_as_string":"2017-02-10T01:44:00.000Z","key":1486691040000,"doc_count":13},{"key_as_string":"2017-02-10T01:45:00.000Z","key":1486691100000,"doc_count":6},{"key_as_string":"2017-02-10T01:46:00.000Z","key":1486691160000,"doc_count":16},{"key_as_string":"2017-02-10T01:47:00.000Z","key":1486691220000,"doc_count":12},{"key_as_string":"2017-02-10T01:48:00.000Z","key":1486691280000,"doc_count":8},{"key_as_string":"2017-02-10T01:49:00.000Z","key":1486691340000,"doc_count":17},{"key_as_string":"2017-02-10T01:50:00.000Z","key":1486691400000,"doc_count":9},{"key_as_string":"2017-02-10T01:51:00.000Z","key":1486691460000,"doc_count":11},{"key_as_string":"2017-02-10T01:52:00.000Z","key":1486691520000,"doc_count":10},{"key_as_string":"2017-02-10T01:53:00.000Z","key":1486691580000,"doc_count":13},{"key_as_string":"2017-02-10T01:54:00.000Z","key":1486691640000,"doc_count":11},{"key_as_string":"2017-02-10T01:55:00.000Z","key":1486691700000,"doc_count":9},{"key_as_string":"2017-02-10T01:56:00.000Z","key":1486691760000,"doc_count":11},{"key_as_string":"2017-02-10T01:57:00.000Z","key":1486691820000,"doc_count":8},{"key_as_string":"2017-02-10T01:58:00.000Z","key":1486691880000,"doc_count":14},{"key_as_string":"2017-02-10T01:59:00.000Z","key":1486691940000,"doc_count":8},{"key_as_string":"2017-02-10T02:00:00.000Z","key":1486692000000,"doc_count":4},{"key_as_string":"2017-02-10T02:01:00.000Z","key":1486692060000,"doc_count":13},{"key_as_string":"2017-02-10T02:02:00.000Z","key":1486692120000,"doc_count":8},{"key_as_string":"2017-02-10T02:03:00.000Z","key":1486692180000,"doc_count":9},{"key_as_string":"2017-02-10T02:04:00.000Z","key":1486692240000,"doc_count":9},{"key_as_string":"2017-02-10T02:05:00.000Z","key":1486692300000,"doc_count":14},{"key_as_string":"2017-02-10T02:06:00.000Z","key":1486692360000,"doc_count":7},{"key_as_string":"2017-02-10T02:07:00.000Z","key":1486692420000,"doc_count":9},{"key_as_string":"2017-02-10T02:08:00.000Z","key":1486692480000,"doc_count":16},{"key_as_string":"2017-02-10T02:09:00.000Z","key":1486692540000,"doc_count":10},{"key_as_string":"2017-02-10T02:10:00.000Z","key":1486692600000,"doc_count":11},{"key_as_string":"2017-02-10T02:11:00.000Z","key":1486692660000,"doc_count":7},{"key_as_string":"2017-02-10T02:12:00.000Z","key":1486692720000,"doc_count":12},{"key_as_string":"2017-02-10T02:13:00.000Z","key":1486692780000,"doc_count":11},{"key_as_string":"2017-02-10T02:14:00.000Z","key":1486692840000,"doc_count":5},{"key_as_string":"2017-02-10T02:15:00.000Z","key":1486692900000,"doc_count":14},{"key_as_string":"2017-02-10T02:16:00.000Z","key":1486692960000,"doc_count":13},{"key_as_string":"2017-02-10T02:17:00.000Z","key":1486693020000,"doc_count":7},{"key_as_string":"2017-02-10T02:18:00.000Z","key":1486693080000,"doc_count":11},{"key_as_string":"2017-02-10T02:19:00.000Z","key":1486693140000,"doc_count":10},{"key_as_string":"2017-02-10T02:20:00.000Z","key":1486693200000,"doc_count":8},{"key_as_string":"2017-02-10T02:21:00.000Z","key":1486693260000,"doc_count":9},{"key_as_string":"2017-02-10T02:22:00.000Z","key":1486693320000,"doc_count":12},{"key_as_string":"2017-02-10T02:23:00.000Z","key":1486693380000,"doc_count":14},{"key_as_string":"2017-02-10T02:24:00.000Z","key":1486693440000,"doc_count":8},{"key_as_string":"2017-02-10T02:25:00.000Z","key":1486693500000,"doc_count":10},{"key_as_string":"2017-02-10T02:26:00.000Z","key":1486693560000,"doc_count":10},{"key_as_string":"2017-02-10T02:27:00.000Z","key":1486693620000,"doc_count":8},{"key_as_string":"2017-02-10T02:28:00.000Z","key":1486693680000,"doc_count":7},{"key_as_string":"2017-02-10T02:29:00.000Z","key":1486693740000,"doc_count":7},{"key_as_string":"2017-02-10T02:30:00.000Z","key":1486693800000,"doc_count":13},{"key_as_string":"2017-02-10T02:31:00.000Z","key":1486693860000,"doc_count":7},{"key_as_string":"2017-02-10T02:32:00.000Z","key":1486693920000,"doc_count":14},{"key_as_string":"2017-02-10T02:33:00.000Z","key":1486693980000,"doc_count":8},{"key_as_string":"2017-02-10T02:34:00.000Z","key":1486694040000,"doc_count":10},{"key_as_string":"2017-02-10T02:35:00.000Z","key":1486694100000,"doc_count":10},{"key_as_string":"2017-02-10T02:36:00.000Z","key":1486694160000,"doc_count":10},{"key_as_string":"2017-02-10T02:37:00.000Z","key":1486694220000,"doc_count":13},{"key_as_string":"2017-02-10T02:38:00.000Z","key":1486694280000,"doc_count":7},{"key_as_string":"2017-02-10T02:39:00.000Z","key":1486694340000,"doc_count":13},{"key_as_string":"2017-02-10T02:40:00.000Z","key":1486694400000,"doc_count":14},{"key_as_string":"2017-02-10T02:41:00.000Z","key":1486694460000,"doc_count":12},{"key_as_string":"2017-02-10T02:42:00.000Z","key":1486694520000,"doc_count":9},{"key_as_string":"2017-02-10T02:43:00.000Z","key":1486694580000,"doc_count":9},{"key_as_string":"2017-02-10T02:44:00.000Z","key":1486694640000,"doc_count":10},{"key_as_string":"2017-02-10T02:45:00.000Z","key":1486694700000,"doc_count":15},{"key_as_string":"2017-02-10T02:46:00.000Z","key":1486694760000,"doc_count":8},{"key_as_string":"2017-02-10T02:47:00.000Z","key":1486694820000,"doc_count":11},{"key_as_string":"2017-02-10T02:48:00.000Z","key":1486694880000,"doc_count":12},{"key_as_string":"2017-02-10T02:49:00.000Z","key":1486694940000,"doc_count":10},{"key_as_string":"2017-02-10T02:50:00.000Z","key":1486695000000,"doc_count":9},{"key_as_string":"2017-02-10T02:51:00.000Z","key":1486695060000,"doc_count":16},{"key_as_string":"2017-02-10T02:52:00.000Z","key":1486695120000,"doc_count":8},{"key_as_string":"2017-02-10T02:53:00.000Z","key":1486695180000,"doc_count":9},{"key_as_string":"2017-02-10T02:54:00.000Z","key":1486695240000,"doc_count":15},{"key_as_string":"2017-02-10T02:55:00.000Z","key":1486695300000,"doc_count":9},{"key_as_string":"2017-02-10T02:56:00.000Z","key":1486695360000,"doc_count":9},{"key_as_string":"2017-02-10T02:57:00.000Z","key":1486695420000,"doc_count":9},{"key_as_string":"2017-02-10T02:58:00.000Z","key":1486695480000,"doc_count":10},{"key_as_string":"2017-02-10T02:59:00.000Z","key":1486695540000,"doc_count":10},{"key_as_string":"2017-02-10T03:00:00.000Z","key":1486695600000,"doc_count":16},{"key_as_string":"2017-02-10T03:01:00.000Z","key":1486695660000,"doc_count":7},{"key_as_string":"2017-02-10T03:02:00.000Z","key":1486695720000,"doc_count":11},{"key_as_string":"2017-02-10T03:03:00.000Z","key":1486695780000,"doc_count":15},{"key_as_string":"2017-02-10T03:04:00.000Z","key":1486695840000,"doc_count":7},{"key_as_string":"2017-02-10T03:05:00.000Z","key":1486695900000,"doc_count":9},{"key_as_string":"2017-02-10T03:06:00.000Z","key":1486695960000,"doc_count":9},{"key_as_string":"2017-02-10T03:07:00.000Z","key":1486696020000,"doc_count":10},{"key_as_string":"2017-02-10T03:08:00.000Z","key":1486696080000,"doc_count":10},{"key_as_string":"2017-02-10T03:09:00.000Z","key":1486696140000,"doc_count":18},{"key_as_string":"2017-02-10T03:10:00.000Z","key":1486696200000,"doc_count":13},{"key_as_string":"2017-02-10T03:11:00.000Z","key":1486696260000,"doc_count":8},{"key_as_string":"2017-02-10T03:12:00.000Z","key":1486696320000,"doc_count":11},{"key_as_string":"2017-02-10T03:13:00.000Z","key":1486696380000,"doc_count":9},{"key_as_string":"2017-02-10T03:14:00.000Z","key":1486696440000,"doc_count":10},{"key_as_string":"2017-02-10T03:15:00.000Z","key":1486696500000,"doc_count":16},{"key_as_string":"2017-02-10T03:16:00.000Z","key":1486696560000,"doc_count":10},{"key_as_string":"2017-02-10T03:17:00.000Z","key":1486696620000,"doc_count":13},{"key_as_string":"2017-02-10T03:18:00.000Z","key":1486696680000,"doc_count":7},{"key_as_string":"2017-02-10T03:19:00.000Z","key":1486696740000,"doc_count":13},{"key_as_string":"2017-02-10T03:20:00.000Z","key":1486696800000,"doc_count":9},{"key_as_string":"2017-02-10T03:21:00.000Z","key":1486696860000,"doc_count":8},{"key_as_string":"2017-02-10T03:22:00.000Z","key":1486696920000,"doc_count":16},{"key_as_string":"2017-02-10T03:23:00.000Z","key":1486696980000,"doc_count":9},{"key_as_string":"2017-02-10T03:24:00.000Z","key":1486697040000,"doc_count":14},{"key_as_string":"2017-02-10T03:25:00.000Z","key":1486697100000,"doc_count":11},{"key_as_string":"2017-02-10T03:26:00.000Z","key":1486697160000,"doc_count":11},{"key_as_string":"2017-02-10T03:27:00.000Z","key":1486697220000,"doc_count":9},{"key_as_string":"2017-02-10T03:28:00.000Z","key":1486697280000,"doc_count":9},{"key_as_string":"2017-02-10T03:29:00.000Z","key":1486697340000,"doc_count":11},{"key_as_string":"2017-02-10T03:30:00.000Z","key":1486697400000,"doc_count":7},{"key_as_string":"2017-02-10T03:31:00.000Z","key":1486697460000,"doc_count":14},{"key_as_string":"2017-02-10T03:32:00.000Z","key":1486697520000,"doc_count":7},{"key_as_string":"2017-02-10T03:33:00.000Z","key":1486697580000,"doc_count":14},{"key_as_string":"2017-02-10T03:34:00.000Z","key":1486697640000,"doc_count":8},{"key_as_string":"2017-02-10T03:35:00.000Z","key":1486697700000,"doc_count":8},{"key_as_string":"2017-02-10T03:36:00.000Z","key":1486697760000,"doc_count":15},{"key_as_string":"2017-02-10T03:37:00.000Z","key":1486697820000,"doc_count":9},{"key_as_string":"2017-02-10T03:38:00.000Z","key":1486697880000,"doc_count":12},{"key_as_string":"2017-02-10T03:39:00.000Z","key":1486697940000,"doc_count":15},{"key_as_string":"2017-02-10T03:40:00.000Z","key":1486698000000,"doc_count":12},{"key_as_string":"2017-02-10T03:41:00.000Z","key":1486698060000,"doc_count":12},{"key_as_string":"2017-02-10T03:42:00.000Z","key":1486698120000,"doc_count":10},{"key_as_string":"2017-02-10T03:43:00.000Z","key":1486698180000,"doc_count":11},{"key_as_string":"2017-02-10T03:44:00.000Z","key":1486698240000,"doc_count":12},{"key_as_string":"2017-02-10T03:45:00.000Z","key":1486698300000,"doc_count":9},{"key_as_string":"2017-02-10T03:46:00.000Z","key":1486698360000,"doc_count":13},{"key_as_string":"2017-02-10T03:47:00.000Z","key":1486698420000,"doc_count":8},{"key_as_string":"2017-02-10T03:48:00.000Z","key":1486698480000,"doc_count":15},{"key_as_string":"2017-02-10T03:49:00.000Z","key":1486698540000,"doc_count":8},{"key_as_string":"2017-02-10T03:50:00.000Z","key":1486698600000,"doc_count":12},{"key_as_string":"2017-02-10T03:51:00.000Z","key":1486698660000,"doc_count":16},{"key_as_string":"2017-02-10T03:52:00.000Z","key":1486698720000,"doc_count":7},{"key_as_string":"2017-02-10T03:53:00.000Z","key":1486698780000,"doc_count":12},{"key_as_string":"2017-02-10T03:54:00.000Z","key":1486698840000,"doc_count":8},{"key_as_string":"2017-02-10T03:55:00.000Z","key":1486698900000,"doc_count":7},{"key_as_string":"2017-02-10T03:56:00.000Z","key":1486698960000,"doc_count":13},{"key_as_string":"2017-02-10T03:57:00.000Z","key":1486699020000,"doc_count":8},{"key_as_string":"2017-02-10T03:58:00.000Z","key":1486699080000,"doc_count":10},{"key_as_string":"2017-02-10T03:59:00.000Z","key":1486699140000,"doc_count":13},{"key_as_string":"2017-02-10T04:00:00.000Z","key":1486699200000,"doc_count":11},{"key_as_string":"2017-02-10T04:01:00.000Z","key":1486699260000,"doc_count":10},{"key_as_string":"2017-02-10T04:02:00.000Z","key":1486699320000,"doc_count":12},{"key_as_string":"2017-02-10T04:03:00.000Z","key":1486699380000,"doc_count":9},{"key_as_string":"2017-02-10T04:04:00.000Z","key":1486699440000,"doc_count":13},{"key_as_string":"2017-02-10T04:05:00.000Z","key":1486699500000,"doc_count":8},{"key_as_string":"2017-02-10T04:06:00.000Z","key":1486699560000,"doc_count":15},{"key_as_string":"2017-02-10T04:07:00.000Z","key":1486699620000,"doc_count":11},{"key_as_string":"2017-02-10T04:08:00.000Z","key":1486699680000,"doc_count":11},{"key_as_string":"2017-02-10T04:09:00.000Z","key":1486699740000,"doc_count":16},{"key_as_string":"2017-02-10T04:10:00.000Z","key":1486699800000,"doc_count":8},{"key_as_string":"2017-02-10T04:11:00.000Z","key":1486699860000,"doc_count":15},{"key_as_string":"2017-02-10T04:12:00.000Z","key":1486699920000,"doc_count":8},{"key_as_string":"2017-02-10T04:13:00.000Z","key":1486699980000,"doc_count":11},{"key_as_string":"2017-02-10T04:14:00.000Z","key":1486700040000,"doc_count":11},{"key_as_string":"2017-02-10T04:15:00.000Z","key":1486700100000,"doc_count":13},{"key_as_string":"2017-02-10T04:16:00.000Z","key":1486700160000,"doc_count":6},{"key_as_string":"2017-02-10T04:17:00.000Z","key":1486700220000,"doc_count":11},{"key_as_string":"2017-02-10T04:18:00.000Z","key":1486700280000,"doc_count":11},{"key_as_string":"2017-02-10T04:19:00.000Z","key":1486700340000,"doc_count":9},{"key_as_string":"2017-02-10T04:20:00.000Z","key":1486700400000,"doc_count":17},{"key_as_string":"2017-02-10T04:21:00.000Z","key":1486700460000,"doc_count":7},{"key_as_string":"2017-02-10T04:22:00.000Z","key":1486700520000,"doc_count":14},{"key_as_string":"2017-02-10T04:23:00.000Z","key":1486700580000,"doc_count":13},{"key_as_string":"2017-02-10T04:24:00.000Z","key":1486700640000,"doc_count":7},{"key_as_string":"2017-02-10T04:25:00.000Z","key":1486700700000,"doc_count":11},{"key_as_string":"2017-02-10T04:26:00.000Z","key":1486700760000,"doc_count":12},{"key_as_string":"2017-02-10T04:27:00.000Z","key":1486700820000,"doc_count":11},{"key_as_string":"2017-02-10T04:28:00.000Z","key":1486700880000,"doc_count":9},{"key_as_string":"2017-02-10T04:29:00.000Z","key":1486700940000,"doc_count":9},{"key_as_string":"2017-02-10T04:30:00.000Z","key":1486701000000,"doc_count":10},{"key_as_string":"2017-02-10T04:31:00.000Z","key":1486701060000,"doc_count":10},{"key_as_string":"2017-02-10T04:32:00.000Z","key":1486701120000,"doc_count":10},{"key_as_string":"2017-02-10T04:33:00.000Z","key":1486701180000,"doc_count":11},{"key_as_string":"2017-02-10T04:34:00.000Z","key":1486701240000,"doc_count":13},{"key_as_string":"2017-02-10T04:35:00.000Z","key":1486701300000,"doc_count":6},{"key_as_string":"2017-02-10T04:36:00.000Z","key":1486701360000,"doc_count":8},{"key_as_string":"2017-02-10T04:37:00.000Z","key":1486701420000,"doc_count":14},{"key_as_string":"2017-02-10T04:38:00.000Z","key":1486701480000,"doc_count":9},{"key_as_string":"2017-02-10T04:39:00.000Z","key":1486701540000,"doc_count":12},{"key_as_string":"2017-02-10T04:40:00.000Z","key":1486701600000,"doc_count":10},{"key_as_string":"2017-02-10T04:41:00.000Z","key":1486701660000,"doc_count":9},{"key_as_string":"2017-02-10T04:42:00.000Z","key":1486701720000,"doc_count":10},{"key_as_string":"2017-02-10T04:43:00.000Z","key":1486701780000,"doc_count":9},{"key_as_string":"2017-02-10T04:44:00.000Z","key":1486701840000,"doc_count":11},{"key_as_string":"2017-02-10T04:45:00.000Z","key":1486701900000,"doc_count":10},{"key_as_string":"2017-02-10T04:46:00.000Z","key":1486701960000,"doc_count":19},{"key_as_string":"2017-02-10T04:47:00.000Z","key":1486702020000,"doc_count":11},{"key_as_string":"2017-02-10T04:48:00.000Z","key":1486702080000,"doc_count":10},{"key_as_string":"2017-02-10T04:49:00.000Z","key":1486702140000,"doc_count":10},{"key_as_string":"2017-02-10T04:50:00.000Z","key":1486702200000,"doc_count":10},{"key_as_string":"2017-02-10T04:51:00.000Z","key":1486702260000,"doc_count":11},{"key_as_string":"2017-02-10T04:52:00.000Z","key":1486702320000,"doc_count":17},{"key_as_string":"2017-02-10T04:53:00.000Z","key":1486702380000,"doc_count":6},{"key_as_string":"2017-02-10T04:54:00.000Z","key":1486702440000,"doc_count":7},{"key_as_string":"2017-02-10T04:55:00.000Z","key":1486702500000,"doc_count":10},{"key_as_string":"2017-02-10T04:56:00.000Z","key":1486702560000,"doc_count":10},{"key_as_string":"2017-02-10T04:57:00.000Z","key":1486702620000,"doc_count":10},{"key_as_string":"2017-02-10T04:58:00.000Z","key":1486702680000,"doc_count":11},{"key_as_string":"2017-02-10T04:59:00.000Z","key":1486702740000,"doc_count":10},{"key_as_string":"2017-02-10T05:00:00.000Z","key":1486702800000,"doc_count":8},{"key_as_string":"2017-02-10T05:01:00.000Z","key":1486702860000,"doc_count":13},{"key_as_string":"2017-02-10T05:02:00.000Z","key":1486702920000,"doc_count":11},{"key_as_string":"2017-02-10T05:03:00.000Z","key":1486702980000,"doc_count":7},{"key_as_string":"2017-02-10T05:04:00.000Z","key":1486703040000,"doc_count":10},{"key_as_string":"2017-02-10T05:05:00.000Z","key":1486703100000,"doc_count":10},{"key_as_string":"2017-02-10T05:06:00.000Z","key":1486703160000,"doc_count":10},{"key_as_string":"2017-02-10T05:07:00.000Z","key":1486703220000,"doc_count":9},{"key_as_string":"2017-02-10T05:08:00.000Z","key":1486703280000,"doc_count":9},{"key_as_string":"2017-02-10T05:09:00.000Z","key":1486703340000,"doc_count":15},{"key_as_string":"2017-02-10T05:10:00.000Z","key":1486703400000,"doc_count":12},{"key_as_string":"2017-02-10T05:11:00.000Z","key":1486703460000,"doc_count":11},{"key_as_string":"2017-02-10T05:12:00.000Z","key":1486703520000,"doc_count":11},{"key_as_string":"2017-02-10T05:13:00.000Z","key":1486703580000,"doc_count":10},{"key_as_string":"2017-02-10T05:14:00.000Z","key":1486703640000,"doc_count":14},{"key_as_string":"2017-02-10T05:15:00.000Z","key":1486703700000,"doc_count":9},{"key_as_string":"2017-02-10T05:16:00.000Z","key":1486703760000,"doc_count":11},{"key_as_string":"2017-02-10T05:17:00.000Z","key":1486703820000,"doc_count":8},{"key_as_string":"2017-02-10T05:18:00.000Z","key":1486703880000,"doc_count":11},{"key_as_string":"2017-02-10T05:19:00.000Z","key":1486703940000,"doc_count":7},{"key_as_string":"2017-02-10T05:20:00.000Z","key":1486704000000,"doc_count":13},{"key_as_string":"2017-02-10T05:21:00.000Z","key":1486704060000,"doc_count":10},{"key_as_string":"2017-02-10T05:22:00.000Z","key":1486704120000,"doc_count":13},{"key_as_string":"2017-02-10T05:23:00.000Z","key":1486704180000,"doc_count":11},{"key_as_string":"2017-02-10T05:24:00.000Z","key":1486704240000,"doc_count":10},{"key_as_string":"2017-02-10T05:25:00.000Z","key":1486704300000,"doc_count":16},{"key_as_string":"2017-02-10T05:26:00.000Z","key":1486704360000,"doc_count":10},{"key_as_string":"2017-02-10T05:27:00.000Z","key":1486704420000,"doc_count":12},{"key_as_string":"2017-02-10T05:28:00.000Z","key":1486704480000,"doc_count":15},{"key_as_string":"2017-02-10T05:29:00.000Z","key":1486704540000,"doc_count":9},{"key_as_string":"2017-02-10T05:30:00.000Z","key":1486704600000,"doc_count":11},{"key_as_string":"2017-02-10T05:31:00.000Z","key":1486704660000,"doc_count":8},{"key_as_string":"2017-02-10T05:32:00.000Z","key":1486704720000,"doc_count":10},{"key_as_string":"2017-02-10T05:33:00.000Z","key":1486704780000,"doc_count":12},{"key_as_string":"2017-02-10T05:34:00.000Z","key":1486704840000,"doc_count":11},{"key_as_string":"2017-02-10T05:35:00.000Z","key":1486704900000,"doc_count":8},{"key_as_string":"2017-02-10T05:36:00.000Z","key":1486704960000,"doc_count":12},{"key_as_string":"2017-02-10T05:37:00.000Z","key":1486705020000,"doc_count":8},{"key_as_string":"2017-02-10T05:38:00.000Z","key":1486705080000,"doc_count":8},{"key_as_string":"2017-02-10T05:39:00.000Z","key":1486705140000,"doc_count":12},{"key_as_string":"2017-02-10T05:40:00.000Z","key":1486705200000,"doc_count":14},{"key_as_string":"2017-02-10T05:41:00.000Z","key":1486705260000,"doc_count":15},{"key_as_string":"2017-02-10T05:42:00.000Z","key":1486705320000,"doc_count":5},{"key_as_string":"2017-02-10T05:43:00.000Z","key":1486705380000,"doc_count":11},{"key_as_string":"2017-02-10T05:44:00.000Z","key":1486705440000,"doc_count":12},{"key_as_string":"2017-02-10T05:45:00.000Z","key":1486705500000,"doc_count":6},{"key_as_string":"2017-02-10T05:46:00.000Z","key":1486705560000,"doc_count":12},{"key_as_string":"2017-02-10T05:47:00.000Z","key":1486705620000,"doc_count":10},{"key_as_string":"2017-02-10T05:48:00.000Z","key":1486705680000,"doc_count":12},{"key_as_string":"2017-02-10T05:49:00.000Z","key":1486705740000,"doc_count":17},{"key_as_string":"2017-02-10T05:50:00.000Z","key":1486705800000,"doc_count":10},{"key_as_string":"2017-02-10T05:51:00.000Z","key":1486705860000,"doc_count":13},{"key_as_string":"2017-02-10T05:52:00.000Z","key":1486705920000,"doc_count":13},{"key_as_string":"2017-02-10T05:53:00.000Z","key":1486705980000,"doc_count":10},{"key_as_string":"2017-02-10T05:54:00.000Z","key":1486706040000,"doc_count":10},{"key_as_string":"2017-02-10T05:55:00.000Z","key":1486706100000,"doc_count":15},{"key_as_string":"2017-02-10T05:56:00.000Z","key":1486706160000,"doc_count":8},{"key_as_string":"2017-02-10T05:57:00.000Z","key":1486706220000,"doc_count":10},{"key_as_string":"2017-02-10T05:58:00.000Z","key":1486706280000,"doc_count":11},{"key_as_string":"2017-02-10T05:59:00.000Z","key":1486706340000,"doc_count":10},{"key_as_string":"2017-02-10T06:00:00.000Z","key":1486706400000,"doc_count":19},{"key_as_string":"2017-02-10T06:01:00.000Z","key":1486706460000,"doc_count":13},{"key_as_string":"2017-02-10T06:02:00.000Z","key":1486706520000,"doc_count":14},{"key_as_string":"2017-02-10T06:03:00.000Z","key":1486706580000,"doc_count":11},{"key_as_string":"2017-02-10T06:04:00.000Z","key":1486706640000,"doc_count":13},{"key_as_string":"2017-02-10T06:05:00.000Z","key":1486706700000,"doc_count":6},{"key_as_string":"2017-02-10T06:06:00.000Z","key":1486706760000,"doc_count":12},{"key_as_string":"2017-02-10T06:07:00.000Z","key":1486706820000,"doc_count":14},{"key_as_string":"2017-02-10T06:08:00.000Z","key":1486706880000,"doc_count":12},{"key_as_string":"2017-02-10T06:09:00.000Z","key":1486706940000,"doc_count":14},{"key_as_string":"2017-02-10T06:10:00.000Z","key":1486707000000,"doc_count":8},{"key_as_string":"2017-02-10T06:11:00.000Z","key":1486707060000,"doc_count":6},{"key_as_string":"2017-02-10T06:12:00.000Z","key":1486707120000,"doc_count":17},{"key_as_string":"2017-02-10T06:13:00.000Z","key":1486707180000,"doc_count":14},{"key_as_string":"2017-02-10T06:14:00.000Z","key":1486707240000,"doc_count":5},{"key_as_string":"2017-02-10T06:15:00.000Z","key":1486707300000,"doc_count":17},{"key_as_string":"2017-02-10T06:16:00.000Z","key":1486707360000,"doc_count":9},{"key_as_string":"2017-02-10T06:17:00.000Z","key":1486707420000,"doc_count":13},{"key_as_string":"2017-02-10T06:18:00.000Z","key":1486707480000,"doc_count":8},{"key_as_string":"2017-02-10T06:19:00.000Z","key":1486707540000,"doc_count":14},{"key_as_string":"2017-02-10T06:20:00.000Z","key":1486707600000,"doc_count":8},{"key_as_string":"2017-02-10T06:21:00.000Z","key":1486707660000,"doc_count":11},{"key_as_string":"2017-02-10T06:22:00.000Z","key":1486707720000,"doc_count":15},{"key_as_string":"2017-02-10T06:23:00.000Z","key":1486707780000,"doc_count":16},{"key_as_string":"2017-02-10T06:24:00.000Z","key":1486707840000,"doc_count":11},{"key_as_string":"2017-02-10T06:25:00.000Z","key":1486707900000,"doc_count":10},{"key_as_string":"2017-02-10T06:26:00.000Z","key":1486707960000,"doc_count":8},{"key_as_string":"2017-02-10T06:27:00.000Z","key":1486708020000,"doc_count":18},{"key_as_string":"2017-02-10T06:28:00.000Z","key":1486708080000,"doc_count":11},{"key_as_string":"2017-02-10T06:29:00.000Z","key":1486708140000,"doc_count":10},{"key_as_string":"2017-02-10T06:30:00.000Z","key":1486708200000,"doc_count":9},{"key_as_string":"2017-02-10T06:31:00.000Z","key":1486708260000,"doc_count":13},{"key_as_string":"2017-02-10T06:32:00.000Z","key":1486708320000,"doc_count":10},{"key_as_string":"2017-02-10T06:33:00.000Z","key":1486708380000,"doc_count":12},{"key_as_string":"2017-02-10T06:34:00.000Z","key":1486708440000,"doc_count":10},{"key_as_string":"2017-02-10T06:35:00.000Z","key":1486708500000,"doc_count":9},{"key_as_string":"2017-02-10T06:36:00.000Z","key":1486708560000,"doc_count":17},{"key_as_string":"2017-02-10T06:37:00.000Z","key":1486708620000,"doc_count":15},{"key_as_string":"2017-02-10T06:38:00.000Z","key":1486708680000,"doc_count":11},{"key_as_string":"2017-02-10T06:39:00.000Z","key":1486708740000,"doc_count":12},{"key_as_string":"2017-02-10T06:40:00.000Z","key":1486708800000,"doc_count":11},{"key_as_string":"2017-02-10T06:41:00.000Z","key":1486708860000,"doc_count":13},{"key_as_string":"2017-02-10T06:42:00.000Z","key":1486708920000,"doc_count":10},{"key_as_string":"2017-02-10T06:43:00.000Z","key":1486708980000,"doc_count":12},{"key_as_string":"2017-02-10T06:44:00.000Z","key":1486709040000,"doc_count":14},{"key_as_string":"2017-02-10T06:45:00.000Z","key":1486709100000,"doc_count":7},{"key_as_string":"2017-02-10T06:46:00.000Z","key":1486709160000,"doc_count":17},{"key_as_string":"2017-02-10T06:47:00.000Z","key":1486709220000,"doc_count":9},{"key_as_string":"2017-02-10T06:48:00.000Z","key":1486709280000,"doc_count":12},{"key_as_string":"2017-02-10T06:49:00.000Z","key":1486709340000,"doc_count":14},{"key_as_string":"2017-02-10T06:50:00.000Z","key":1486709400000,"doc_count":15},{"key_as_string":"2017-02-10T06:51:00.000Z","key":1486709460000,"doc_count":12},{"key_as_string":"2017-02-10T06:52:00.000Z","key":1486709520000,"doc_count":8},{"key_as_string":"2017-02-10T06:53:00.000Z","key":1486709580000,"doc_count":13},{"key_as_string":"2017-02-10T06:54:00.000Z","key":1486709640000,"doc_count":15},{"key_as_string":"2017-02-10T06:55:00.000Z","key":1486709700000,"doc_count":8},{"key_as_string":"2017-02-10T06:56:00.000Z","key":1486709760000,"doc_count":7},{"key_as_string":"2017-02-10T06:57:00.000Z","key":1486709820000,"doc_count":18},{"key_as_string":"2017-02-10T06:58:00.000Z","key":1486709880000,"doc_count":11},{"key_as_string":"2017-02-10T06:59:00.000Z","key":1486709940000,"doc_count":14},{"key_as_string":"2017-02-10T07:00:00.000Z","key":1486710000000,"doc_count":8},{"key_as_string":"2017-02-10T07:01:00.000Z","key":1486710060000,"doc_count":12},{"key_as_string":"2017-02-10T07:02:00.000Z","key":1486710120000,"doc_count":14},{"key_as_string":"2017-02-10T07:03:00.000Z","key":1486710180000,"doc_count":9},{"key_as_string":"2017-02-10T07:04:00.000Z","key":1486710240000,"doc_count":13},{"key_as_string":"2017-02-10T07:05:00.000Z","key":1486710300000,"doc_count":12},{"key_as_string":"2017-02-10T07:06:00.000Z","key":1486710360000,"doc_count":15},{"key_as_string":"2017-02-10T07:07:00.000Z","key":1486710420000,"doc_count":19},{"key_as_string":"2017-02-10T07:08:00.000Z","key":1486710480000,"doc_count":11},{"key_as_string":"2017-02-10T07:09:00.000Z","key":1486710540000,"doc_count":16},{"key_as_string":"2017-02-10T07:10:00.000Z","key":1486710600000,"doc_count":11},{"key_as_string":"2017-02-10T07:11:00.000Z","key":1486710660000,"doc_count":13},{"key_as_string":"2017-02-10T07:12:00.000Z","key":1486710720000,"doc_count":12},{"key_as_string":"2017-02-10T07:13:00.000Z","key":1486710780000,"doc_count":9},{"key_as_string":"2017-02-10T07:14:00.000Z","key":1486710840000,"doc_count":21},{"key_as_string":"2017-02-10T07:15:00.000Z","key":1486710900000,"doc_count":11},{"key_as_string":"2017-02-10T07:16:00.000Z","key":1486710960000,"doc_count":6},{"key_as_string":"2017-02-10T07:17:00.000Z","key":1486711020000,"doc_count":16},{"key_as_string":"2017-02-10T07:18:00.000Z","key":1486711080000,"doc_count":15},{"key_as_string":"2017-02-10T07:19:00.000Z","key":1486711140000,"doc_count":13},{"key_as_string":"2017-02-10T07:20:00.000Z","key":1486711200000,"doc_count":15},{"key_as_string":"2017-02-10T07:21:00.000Z","key":1486711260000,"doc_count":14},{"key_as_string":"2017-02-10T07:22:00.000Z","key":1486711320000,"doc_count":14},{"key_as_string":"2017-02-10T07:23:00.000Z","key":1486711380000,"doc_count":13},{"key_as_string":"2017-02-10T07:24:00.000Z","key":1486711440000,"doc_count":10},{"key_as_string":"2017-02-10T07:25:00.000Z","key":1486711500000,"doc_count":11},{"key_as_string":"2017-02-10T07:26:00.000Z","key":1486711560000,"doc_count":12},{"key_as_string":"2017-02-10T07:27:00.000Z","key":1486711620000,"doc_count":15},{"key_as_string":"2017-02-10T07:28:00.000Z","key":1486711680000,"doc_count":7},{"key_as_string":"2017-02-10T07:29:00.000Z","key":1486711740000,"doc_count":18},{"key_as_string":"2017-02-10T07:30:00.000Z","key":1486711800000,"doc_count":14},{"key_as_string":"2017-02-10T07:31:00.000Z","key":1486711860000,"doc_count":12},{"key_as_string":"2017-02-10T07:32:00.000Z","key":1486711920000,"doc_count":16},{"key_as_string":"2017-02-10T07:33:00.000Z","key":1486711980000,"doc_count":12},{"key_as_string":"2017-02-10T07:34:00.000Z","key":1486712040000,"doc_count":15},{"key_as_string":"2017-02-10T07:35:00.000Z","key":1486712100000,"doc_count":12},{"key_as_string":"2017-02-10T07:36:00.000Z","key":1486712160000,"doc_count":10},{"key_as_string":"2017-02-10T07:37:00.000Z","key":1486712220000,"doc_count":12},{"key_as_string":"2017-02-10T07:38:00.000Z","key":1486712280000,"doc_count":13},{"key_as_string":"2017-02-10T07:39:00.000Z","key":1486712340000,"doc_count":8},{"key_as_string":"2017-02-10T07:40:00.000Z","key":1486712400000,"doc_count":11},{"key_as_string":"2017-02-10T07:41:00.000Z","key":1486712460000,"doc_count":13},{"key_as_string":"2017-02-10T07:42:00.000Z","key":1486712520000,"doc_count":19},{"key_as_string":"2017-02-10T07:43:00.000Z","key":1486712580000,"doc_count":10},{"key_as_string":"2017-02-10T07:44:00.000Z","key":1486712640000,"doc_count":19},{"key_as_string":"2017-02-10T07:45:00.000Z","key":1486712700000,"doc_count":8},{"key_as_string":"2017-02-10T07:46:00.000Z","key":1486712760000,"doc_count":13},{"key_as_string":"2017-02-10T07:47:00.000Z","key":1486712820000,"doc_count":17},{"key_as_string":"2017-02-10T07:48:00.000Z","key":1486712880000,"doc_count":14},{"key_as_string":"2017-02-10T07:49:00.000Z","key":1486712940000,"doc_count":13},{"key_as_string":"2017-02-10T07:50:00.000Z","key":1486713000000,"doc_count":14},{"key_as_string":"2017-02-10T07:51:00.000Z","key":1486713060000,"doc_count":11},{"key_as_string":"2017-02-10T07:52:00.000Z","key":1486713120000,"doc_count":16},{"key_as_string":"2017-02-10T07:53:00.000Z","key":1486713180000,"doc_count":13},{"key_as_string":"2017-02-10T07:54:00.000Z","key":1486713240000,"doc_count":12},{"key_as_string":"2017-02-10T07:55:00.000Z","key":1486713300000,"doc_count":13},{"key_as_string":"2017-02-10T07:56:00.000Z","key":1486713360000,"doc_count":11},{"key_as_string":"2017-02-10T07:57:00.000Z","key":1486713420000,"doc_count":9},{"key_as_string":"2017-02-10T07:58:00.000Z","key":1486713480000,"doc_count":11},{"key_as_string":"2017-02-10T07:59:00.000Z","key":1486713540000,"doc_count":14},{"key_as_string":"2017-02-10T08:00:00.000Z","key":1486713600000,"doc_count":9},{"key_as_string":"2017-02-10T08:01:00.000Z","key":1486713660000,"doc_count":13},{"key_as_string":"2017-02-10T08:02:00.000Z","key":1486713720000,"doc_count":13},{"key_as_string":"2017-02-10T08:03:00.000Z","key":1486713780000,"doc_count":12},{"key_as_string":"2017-02-10T08:04:00.000Z","key":1486713840000,"doc_count":19},{"key_as_string":"2017-02-10T08:05:00.000Z","key":1486713900000,"doc_count":9},{"key_as_string":"2017-02-10T08:06:00.000Z","key":1486713960000,"doc_count":15},{"key_as_string":"2017-02-10T08:07:00.000Z","key":1486714020000,"doc_count":9},{"key_as_string":"2017-02-10T08:08:00.000Z","key":1486714080000,"doc_count":15},{"key_as_string":"2017-02-10T08:09:00.000Z","key":1486714140000,"doc_count":11},{"key_as_string":"2017-02-10T08:10:00.000Z","key":1486714200000,"doc_count":10},{"key_as_string":"2017-02-10T08:11:00.000Z","key":1486714260000,"doc_count":13},{"key_as_string":"2017-02-10T08:12:00.000Z","key":1486714320000,"doc_count":8},{"key_as_string":"2017-02-10T08:13:00.000Z","key":1486714380000,"doc_count":13},{"key_as_string":"2017-02-10T08:14:00.000Z","key":1486714440000,"doc_count":15},{"key_as_string":"2017-02-10T08:15:00.000Z","key":1486714500000,"doc_count":17},{"key_as_string":"2017-02-10T08:16:00.000Z","key":1486714560000,"doc_count":13},{"key_as_string":"2017-02-10T08:17:00.000Z","key":1486714620000,"doc_count":17},{"key_as_string":"2017-02-10T08:18:00.000Z","key":1486714680000,"doc_count":12},{"key_as_string":"2017-02-10T08:19:00.000Z","key":1486714740000,"doc_count":17},{"key_as_string":"2017-02-10T08:20:00.000Z","key":1486714800000,"doc_count":15},{"key_as_string":"2017-02-10T08:21:00.000Z","key":1486714860000,"doc_count":12},{"key_as_string":"2017-02-10T08:22:00.000Z","key":1486714920000,"doc_count":12},{"key_as_string":"2017-02-10T08:23:00.000Z","key":1486714980000,"doc_count":14},{"key_as_string":"2017-02-10T08:24:00.000Z","key":1486715040000,"doc_count":15},{"key_as_string":"2017-02-10T08:25:00.000Z","key":1486715100000,"doc_count":13},{"key_as_string":"2017-02-10T08:26:00.000Z","key":1486715160000,"doc_count":11},{"key_as_string":"2017-02-10T08:27:00.000Z","key":1486715220000,"doc_count":15},{"key_as_string":"2017-02-10T08:28:00.000Z","key":1486715280000,"doc_count":11},{"key_as_string":"2017-02-10T08:29:00.000Z","key":1486715340000,"doc_count":13},{"key_as_string":"2017-02-10T08:30:00.000Z","key":1486715400000,"doc_count":12},{"key_as_string":"2017-02-10T08:31:00.000Z","key":1486715460000,"doc_count":12},{"key_as_string":"2017-02-10T08:32:00.000Z","key":1486715520000,"doc_count":16},{"key_as_string":"2017-02-10T08:33:00.000Z","key":1486715580000,"doc_count":12},{"key_as_string":"2017-02-10T08:34:00.000Z","key":1486715640000,"doc_count":12},{"key_as_string":"2017-02-10T08:35:00.000Z","key":1486715700000,"doc_count":13},{"key_as_string":"2017-02-10T08:36:00.000Z","key":1486715760000,"doc_count":11},{"key_as_string":"2017-02-10T08:37:00.000Z","key":1486715820000,"doc_count":17},{"key_as_string":"2017-02-10T08:38:00.000Z","key":1486715880000,"doc_count":14},{"key_as_string":"2017-02-10T08:39:00.000Z","key":1486715940000,"doc_count":7},{"key_as_string":"2017-02-10T08:40:00.000Z","key":1486716000000,"doc_count":22},{"key_as_string":"2017-02-10T08:41:00.000Z","key":1486716060000,"doc_count":12},{"key_as_string":"2017-02-10T08:42:00.000Z","key":1486716120000,"doc_count":17},{"key_as_string":"2017-02-10T08:43:00.000Z","key":1486716180000,"doc_count":13},{"key_as_string":"2017-02-10T08:44:00.000Z","key":1486716240000,"doc_count":17},{"key_as_string":"2017-02-10T08:45:00.000Z","key":1486716300000,"doc_count":14},{"key_as_string":"2017-02-10T08:46:00.000Z","key":1486716360000,"doc_count":11},{"key_as_string":"2017-02-10T08:47:00.000Z","key":1486716420000,"doc_count":18},{"key_as_string":"2017-02-10T08:48:00.000Z","key":1486716480000,"doc_count":11},{"key_as_string":"2017-02-10T08:49:00.000Z","key":1486716540000,"doc_count":18},{"key_as_string":"2017-02-10T08:50:00.000Z","key":1486716600000,"doc_count":13},{"key_as_string":"2017-02-10T08:51:00.000Z","key":1486716660000,"doc_count":12},{"key_as_string":"2017-02-10T08:52:00.000Z","key":1486716720000,"doc_count":13},{"key_as_string":"2017-02-10T08:53:00.000Z","key":1486716780000,"doc_count":14},{"key_as_string":"2017-02-10T08:54:00.000Z","key":1486716840000,"doc_count":18},{"key_as_string":"2017-02-10T08:55:00.000Z","key":1486716900000,"doc_count":10},{"key_as_string":"2017-02-10T08:56:00.000Z","key":1486716960000,"doc_count":17},{"key_as_string":"2017-02-10T08:57:00.000Z","key":1486717020000,"doc_count":13},{"key_as_string":"2017-02-10T08:58:00.000Z","key":1486717080000,"doc_count":14},{"key_as_string":"2017-02-10T08:59:00.000Z","key":1486717140000,"doc_count":11},{"key_as_string":"2017-02-10T09:00:00.000Z","key":1486717200000,"doc_count":11},{"key_as_string":"2017-02-10T09:01:00.000Z","key":1486717260000,"doc_count":12},{"key_as_string":"2017-02-10T09:02:00.000Z","key":1486717320000,"doc_count":10},{"key_as_string":"2017-02-10T09:03:00.000Z","key":1486717380000,"doc_count":16},{"key_as_string":"2017-02-10T09:04:00.000Z","key":1486717440000,"doc_count":9},{"key_as_string":"2017-02-10T09:05:00.000Z","key":1486717500000,"doc_count":15},{"key_as_string":"2017-02-10T09:06:00.000Z","key":1486717560000,"doc_count":15},{"key_as_string":"2017-02-10T09:07:00.000Z","key":1486717620000,"doc_count":17},{"key_as_string":"2017-02-10T09:08:00.000Z","key":1486717680000,"doc_count":10},{"key_as_string":"2017-02-10T09:09:00.000Z","key":1486717740000,"doc_count":14},{"key_as_string":"2017-02-10T09:10:00.000Z","key":1486717800000,"doc_count":12},{"key_as_string":"2017-02-10T09:11:00.000Z","key":1486717860000,"doc_count":16},{"key_as_string":"2017-02-10T09:12:00.000Z","key":1486717920000,"doc_count":9},{"key_as_string":"2017-02-10T09:13:00.000Z","key":1486717980000,"doc_count":14},{"key_as_string":"2017-02-10T09:14:00.000Z","key":1486718040000,"doc_count":14},{"key_as_string":"2017-02-10T09:15:00.000Z","key":1486718100000,"doc_count":18},{"key_as_string":"2017-02-10T09:16:00.000Z","key":1486718160000,"doc_count":16},{"key_as_string":"2017-02-10T09:17:00.000Z","key":1486718220000,"doc_count":16},{"key_as_string":"2017-02-10T09:18:00.000Z","key":1486718280000,"doc_count":13},{"key_as_string":"2017-02-10T09:19:00.000Z","key":1486718340000,"doc_count":11},{"key_as_string":"2017-02-10T09:20:00.000Z","key":1486718400000,"doc_count":14},{"key_as_string":"2017-02-10T09:21:00.000Z","key":1486718460000,"doc_count":12},{"key_as_string":"2017-02-10T09:22:00.000Z","key":1486718520000,"doc_count":10},{"key_as_string":"2017-02-10T09:23:00.000Z","key":1486718580000,"doc_count":12},{"key_as_string":"2017-02-10T09:24:00.000Z","key":1486718640000,"doc_count":15},{"key_as_string":"2017-02-10T09:25:00.000Z","key":1486718700000,"doc_count":9},{"key_as_string":"2017-02-10T09:26:00.000Z","key":1486718760000,"doc_count":16},{"key_as_string":"2017-02-10T09:27:00.000Z","key":1486718820000,"doc_count":17},{"key_as_string":"2017-02-10T09:28:00.000Z","key":1486718880000,"doc_count":12},{"key_as_string":"2017-02-10T09:29:00.000Z","key":1486718940000,"doc_count":10},{"key_as_string":"2017-02-10T09:30:00.000Z","key":1486719000000,"doc_count":12},{"key_as_string":"2017-02-10T09:31:00.000Z","key":1486719060000,"doc_count":13},{"key_as_string":"2017-02-10T09:32:00.000Z","key":1486719120000,"doc_count":13},{"key_as_string":"2017-02-10T09:33:00.000Z","key":1486719180000,"doc_count":11},{"key_as_string":"2017-02-10T09:34:00.000Z","key":1486719240000,"doc_count":11},{"key_as_string":"2017-02-10T09:35:00.000Z","key":1486719300000,"doc_count":15},{"key_as_string":"2017-02-10T09:36:00.000Z","key":1486719360000,"doc_count":10},{"key_as_string":"2017-02-10T09:37:00.000Z","key":1486719420000,"doc_count":12},{"key_as_string":"2017-02-10T09:38:00.000Z","key":1486719480000,"doc_count":19},{"key_as_string":"2017-02-10T09:39:00.000Z","key":1486719540000,"doc_count":12},{"key_as_string":"2017-02-10T09:40:00.000Z","key":1486719600000,"doc_count":19},{"key_as_string":"2017-02-10T09:41:00.000Z","key":1486719660000,"doc_count":16},{"key_as_string":"2017-02-10T09:42:00.000Z","key":1486719720000,"doc_count":19},{"key_as_string":"2017-02-10T09:43:00.000Z","key":1486719780000,"doc_count":7},{"key_as_string":"2017-02-10T09:44:00.000Z","key":1486719840000,"doc_count":18},{"key_as_string":"2017-02-10T09:45:00.000Z","key":1486719900000,"doc_count":12},{"key_as_string":"2017-02-10T09:46:00.000Z","key":1486719960000,"doc_count":14},{"key_as_string":"2017-02-10T09:47:00.000Z","key":1486720020000,"doc_count":14},{"key_as_string":"2017-02-10T09:48:00.000Z","key":1486720080000,"doc_count":11},{"key_as_string":"2017-02-10T09:49:00.000Z","key":1486720140000,"doc_count":14},{"key_as_string":"2017-02-10T09:50:00.000Z","key":1486720200000,"doc_count":9},{"key_as_string":"2017-02-10T09:51:00.000Z","key":1486720260000,"doc_count":17},{"key_as_string":"2017-02-10T09:52:00.000Z","key":1486720320000,"doc_count":20},{"key_as_string":"2017-02-10T09:53:00.000Z","key":1486720380000,"doc_count":13},{"key_as_string":"2017-02-10T09:54:00.000Z","key":1486720440000,"doc_count":7},{"key_as_string":"2017-02-10T09:55:00.000Z","key":1486720500000,"doc_count":16},{"key_as_string":"2017-02-10T09:56:00.000Z","key":1486720560000,"doc_count":13},{"key_as_string":"2017-02-10T09:57:00.000Z","key":1486720620000,"doc_count":12},{"key_as_string":"2017-02-10T09:58:00.000Z","key":1486720680000,"doc_count":13},{"key_as_string":"2017-02-10T09:59:00.000Z","key":1486720740000,"doc_count":10},{"key_as_string":"2017-02-10T10:00:00.000Z","key":1486720800000,"doc_count":15},{"key_as_string":"2017-02-10T10:01:00.000Z","key":1486720860000,"doc_count":13},{"key_as_string":"2017-02-10T10:02:00.000Z","key":1486720920000,"doc_count":23},{"key_as_string":"2017-02-10T10:03:00.000Z","key":1486720980000,"doc_count":11},{"key_as_string":"2017-02-10T10:04:00.000Z","key":1486721040000,"doc_count":11},{"key_as_string":"2017-02-10T10:05:00.000Z","key":1486721100000,"doc_count":17},{"key_as_string":"2017-02-10T10:06:00.000Z","key":1486721160000,"doc_count":10},{"key_as_string":"2017-02-10T10:07:00.000Z","key":1486721220000,"doc_count":15},{"key_as_string":"2017-02-10T10:08:00.000Z","key":1486721280000,"doc_count":13},{"key_as_string":"2017-02-10T10:09:00.000Z","key":1486721340000,"doc_count":15},{"key_as_string":"2017-02-10T10:10:00.000Z","key":1486721400000,"doc_count":17},{"key_as_string":"2017-02-10T10:11:00.000Z","key":1486721460000,"doc_count":12},{"key_as_string":"2017-02-10T10:12:00.000Z","key":1486721520000,"doc_count":11},{"key_as_string":"2017-02-10T10:13:00.000Z","key":1486721580000,"doc_count":19},{"key_as_string":"2017-02-10T10:14:00.000Z","key":1486721640000,"doc_count":12},{"key_as_string":"2017-02-10T10:15:00.000Z","key":1486721700000,"doc_count":17},{"key_as_string":"2017-02-10T10:16:00.000Z","key":1486721760000,"doc_count":13},{"key_as_string":"2017-02-10T10:17:00.000Z","key":1486721820000,"doc_count":15},{"key_as_string":"2017-02-10T10:18:00.000Z","key":1486721880000,"doc_count":13},{"key_as_string":"2017-02-10T10:19:00.000Z","key":1486721940000,"doc_count":15},{"key_as_string":"2017-02-10T10:20:00.000Z","key":1486722000000,"doc_count":15},{"key_as_string":"2017-02-10T10:21:00.000Z","key":1486722060000,"doc_count":15},{"key_as_string":"2017-02-10T10:22:00.000Z","key":1486722120000,"doc_count":14},{"key_as_string":"2017-02-10T10:23:00.000Z","key":1486722180000,"doc_count":18},{"key_as_string":"2017-02-10T10:24:00.000Z","key":1486722240000,"doc_count":16},{"key_as_string":"2017-02-10T10:25:00.000Z","key":1486722300000,"doc_count":11},{"key_as_string":"2017-02-10T10:26:00.000Z","key":1486722360000,"doc_count":12},{"key_as_string":"2017-02-10T10:27:00.000Z","key":1486722420000,"doc_count":21},{"key_as_string":"2017-02-10T10:28:00.000Z","key":1486722480000,"doc_count":12},{"key_as_string":"2017-02-10T10:29:00.000Z","key":1486722540000,"doc_count":10},{"key_as_string":"2017-02-10T10:30:00.000Z","key":1486722600000,"doc_count":13},{"key_as_string":"2017-02-10T10:31:00.000Z","key":1486722660000,"doc_count":12},{"key_as_string":"2017-02-10T10:32:00.000Z","key":1486722720000,"doc_count":14},{"key_as_string":"2017-02-10T10:33:00.000Z","key":1486722780000,"doc_count":10},{"key_as_string":"2017-02-10T10:34:00.000Z","key":1486722840000,"doc_count":16},{"key_as_string":"2017-02-10T10:35:00.000Z","key":1486722900000,"doc_count":10},{"key_as_string":"2017-02-10T10:36:00.000Z","key":1486722960000,"doc_count":19},{"key_as_string":"2017-02-10T10:37:00.000Z","key":1486723020000,"doc_count":6},{"key_as_string":"2017-02-10T10:38:00.000Z","key":1486723080000,"doc_count":21},{"key_as_string":"2017-02-10T10:39:00.000Z","key":1486723140000,"doc_count":13},{"key_as_string":"2017-02-10T10:40:00.000Z","key":1486723200000,"doc_count":18},{"key_as_string":"2017-02-10T10:41:00.000Z","key":1486723260000,"doc_count":12},{"key_as_string":"2017-02-10T10:42:00.000Z","key":1486723320000,"doc_count":16},{"key_as_string":"2017-02-10T10:43:00.000Z","key":1486723380000,"doc_count":6},{"key_as_string":"2017-02-10T10:44:00.000Z","key":1486723440000,"doc_count":16},{"key_as_string":"2017-02-10T10:45:00.000Z","key":1486723500000,"doc_count":10},{"key_as_string":"2017-02-10T10:46:00.000Z","key":1486723560000,"doc_count":17},{"key_as_string":"2017-02-10T10:47:00.000Z","key":1486723620000,"doc_count":11},{"key_as_string":"2017-02-10T10:48:00.000Z","key":1486723680000,"doc_count":17},{"key_as_string":"2017-02-10T10:49:00.000Z","key":1486723740000,"doc_count":11},{"key_as_string":"2017-02-10T10:50:00.000Z","key":1486723800000,"doc_count":12},{"key_as_string":"2017-02-10T10:51:00.000Z","key":1486723860000,"doc_count":12},{"key_as_string":"2017-02-10T10:52:00.000Z","key":1486723920000,"doc_count":18},{"key_as_string":"2017-02-10T10:53:00.000Z","key":1486723980000,"doc_count":15},{"key_as_string":"2017-02-10T10:54:00.000Z","key":1486724040000,"doc_count":15},{"key_as_string":"2017-02-10T10:55:00.000Z","key":1486724100000,"doc_count":16},{"key_as_string":"2017-02-10T10:56:00.000Z","key":1486724160000,"doc_count":8},{"key_as_string":"2017-02-10T10:57:00.000Z","key":1486724220000,"doc_count":19},{"key_as_string":"2017-02-10T10:58:00.000Z","key":1486724280000,"doc_count":12},{"key_as_string":"2017-02-10T10:59:00.000Z","key":1486724340000,"doc_count":16},{"key_as_string":"2017-02-10T11:00:00.000Z","key":1486724400000,"doc_count":16},{"key_as_string":"2017-02-10T11:01:00.000Z","key":1486724460000,"doc_count":11},{"key_as_string":"2017-02-10T11:02:00.000Z","key":1486724520000,"doc_count":11},{"key_as_string":"2017-02-10T11:03:00.000Z","key":1486724580000,"doc_count":16},{"key_as_string":"2017-02-10T11:04:00.000Z","key":1486724640000,"doc_count":15},{"key_as_string":"2017-02-10T11:05:00.000Z","key":1486724700000,"doc_count":12},{"key_as_string":"2017-02-10T11:06:00.000Z","key":1486724760000,"doc_count":20},{"key_as_string":"2017-02-10T11:07:00.000Z","key":1486724820000,"doc_count":14},{"key_as_string":"2017-02-10T11:08:00.000Z","key":1486724880000,"doc_count":11},{"key_as_string":"2017-02-10T11:09:00.000Z","key":1486724940000,"doc_count":11},{"key_as_string":"2017-02-10T11:10:00.000Z","key":1486725000000,"doc_count":11},{"key_as_string":"2017-02-10T11:11:00.000Z","key":1486725060000,"doc_count":16},{"key_as_string":"2017-02-10T11:12:00.000Z","key":1486725120000,"doc_count":12},{"key_as_string":"2017-02-10T11:13:00.000Z","key":1486725180000,"doc_count":15},{"key_as_string":"2017-02-10T11:14:00.000Z","key":1486725240000,"doc_count":12},{"key_as_string":"2017-02-10T11:15:00.000Z","key":1486725300000,"doc_count":12},{"key_as_string":"2017-02-10T11:16:00.000Z","key":1486725360000,"doc_count":17},{"key_as_string":"2017-02-10T11:17:00.000Z","key":1486725420000,"doc_count":15},{"key_as_string":"2017-02-10T11:18:00.000Z","key":1486725480000,"doc_count":12},{"key_as_string":"2017-02-10T11:19:00.000Z","key":1486725540000,"doc_count":13},{"key_as_string":"2017-02-10T11:20:00.000Z","key":1486725600000,"doc_count":12},{"key_as_string":"2017-02-10T11:21:00.000Z","key":1486725660000,"doc_count":12},{"key_as_string":"2017-02-10T11:22:00.000Z","key":1486725720000,"doc_count":9},{"key_as_string":"2017-02-10T11:23:00.000Z","key":1486725780000,"doc_count":15},{"key_as_string":"2017-02-10T11:24:00.000Z","key":1486725840000,"doc_count":21},{"key_as_string":"2017-02-10T11:25:00.000Z","key":1486725900000,"doc_count":13},{"key_as_string":"2017-02-10T11:26:00.000Z","key":1486725960000,"doc_count":15},{"key_as_string":"2017-02-10T11:27:00.000Z","key":1486726020000,"doc_count":14},{"key_as_string":"2017-02-10T11:28:00.000Z","key":1486726080000,"doc_count":10},{"key_as_string":"2017-02-10T11:29:00.000Z","key":1486726140000,"doc_count":16},{"key_as_string":"2017-02-10T11:30:00.000Z","key":1486726200000,"doc_count":12},{"key_as_string":"2017-02-10T11:31:00.000Z","key":1486726260000,"doc_count":18},{"key_as_string":"2017-02-10T11:32:00.000Z","key":1486726320000,"doc_count":11},{"key_as_string":"2017-02-10T11:33:00.000Z","key":1486726380000,"doc_count":15},{"key_as_string":"2017-02-10T11:34:00.000Z","key":1486726440000,"doc_count":16},{"key_as_string":"2017-02-10T11:35:00.000Z","key":1486726500000,"doc_count":19},{"key_as_string":"2017-02-10T11:36:00.000Z","key":1486726560000,"doc_count":9},{"key_as_string":"2017-02-10T11:37:00.000Z","key":1486726620000,"doc_count":14},{"key_as_string":"2017-02-10T11:38:00.000Z","key":1486726680000,"doc_count":12},{"key_as_string":"2017-02-10T11:39:00.000Z","key":1486726740000,"doc_count":15},{"key_as_string":"2017-02-10T11:40:00.000Z","key":1486726800000,"doc_count":13},{"key_as_string":"2017-02-10T11:41:00.000Z","key":1486726860000,"doc_count":14},{"key_as_string":"2017-02-10T11:42:00.000Z","key":1486726920000,"doc_count":11},{"key_as_string":"2017-02-10T11:43:00.000Z","key":1486726980000,"doc_count":16},{"key_as_string":"2017-02-10T11:44:00.000Z","key":1486727040000,"doc_count":10},{"key_as_string":"2017-02-10T11:45:00.000Z","key":1486727100000,"doc_count":12},{"key_as_string":"2017-02-10T11:46:00.000Z","key":1486727160000,"doc_count":9},{"key_as_string":"2017-02-10T11:47:00.000Z","key":1486727220000,"doc_count":16},{"key_as_string":"2017-02-10T11:48:00.000Z","key":1486727280000,"doc_count":8},{"key_as_string":"2017-02-10T11:49:00.000Z","key":1486727340000,"doc_count":13},{"key_as_string":"2017-02-10T11:50:00.000Z","key":1486727400000,"doc_count":16},{"key_as_string":"2017-02-10T11:51:00.000Z","key":1486727460000,"doc_count":13},{"key_as_string":"2017-02-10T11:52:00.000Z","key":1486727520000,"doc_count":12},{"key_as_string":"2017-02-10T11:53:00.000Z","key":1486727580000,"doc_count":23},{"key_as_string":"2017-02-10T11:54:00.000Z","key":1486727640000,"doc_count":11},{"key_as_string":"2017-02-10T11:55:00.000Z","key":1486727700000,"doc_count":12},{"key_as_string":"2017-02-10T11:56:00.000Z","key":1486727760000,"doc_count":14},{"key_as_string":"2017-02-10T11:57:00.000Z","key":1486727820000,"doc_count":15},{"key_as_string":"2017-02-10T11:58:00.000Z","key":1486727880000,"doc_count":14},{"key_as_string":"2017-02-10T11:59:00.000Z","key":1486727940000,"doc_count":14},{"key_as_string":"2017-02-10T12:00:00.000Z","key":1486728000000,"doc_count":13},{"key_as_string":"2017-02-10T12:01:00.000Z","key":1486728060000,"doc_count":15},{"key_as_string":"2017-02-10T12:02:00.000Z","key":1486728120000,"doc_count":10},{"key_as_string":"2017-02-10T12:03:00.000Z","key":1486728180000,"doc_count":14},{"key_as_string":"2017-02-10T12:04:00.000Z","key":1486728240000,"doc_count":14},{"key_as_string":"2017-02-10T12:05:00.000Z","key":1486728300000,"doc_count":15},{"key_as_string":"2017-02-10T12:06:00.000Z","key":1486728360000,"doc_count":10},{"key_as_string":"2017-02-10T12:07:00.000Z","key":1486728420000,"doc_count":16},{"key_as_string":"2017-02-10T12:08:00.000Z","key":1486728480000,"doc_count":17},{"key_as_string":"2017-02-10T12:09:00.000Z","key":1486728540000,"doc_count":11},{"key_as_string":"2017-02-10T12:10:00.000Z","key":1486728600000,"doc_count":16},{"key_as_string":"2017-02-10T12:11:00.000Z","key":1486728660000,"doc_count":16},{"key_as_string":"2017-02-10T12:12:00.000Z","key":1486728720000,"doc_count":8},{"key_as_string":"2017-02-10T12:13:00.000Z","key":1486728780000,"doc_count":19},{"key_as_string":"2017-02-10T12:14:00.000Z","key":1486728840000,"doc_count":21},{"key_as_string":"2017-02-10T12:15:00.000Z","key":1486728900000,"doc_count":15},{"key_as_string":"2017-02-10T12:16:00.000Z","key":1486728960000,"doc_count":12},{"key_as_string":"2017-02-10T12:17:00.000Z","key":1486729020000,"doc_count":16},{"key_as_string":"2017-02-10T12:18:00.000Z","key":1486729080000,"doc_count":14},{"key_as_string":"2017-02-10T12:19:00.000Z","key":1486729140000,"doc_count":17},{"key_as_string":"2017-02-10T12:20:00.000Z","key":1486729200000,"doc_count":16},{"key_as_string":"2017-02-10T12:21:00.000Z","key":1486729260000,"doc_count":13},{"key_as_string":"2017-02-10T12:22:00.000Z","key":1486729320000,"doc_count":9},{"key_as_string":"2017-02-10T12:23:00.000Z","key":1486729380000,"doc_count":14},{"key_as_string":"2017-02-10T12:24:00.000Z","key":1486729440000,"doc_count":14},{"key_as_string":"2017-02-10T12:25:00.000Z","key":1486729500000,"doc_count":12},{"key_as_string":"2017-02-10T12:26:00.000Z","key":1486729560000,"doc_count":15},{"key_as_string":"2017-02-10T12:27:00.000Z","key":1486729620000,"doc_count":11},{"key_as_string":"2017-02-10T12:28:00.000Z","key":1486729680000,"doc_count":11},{"key_as_string":"2017-02-10T12:29:00.000Z","key":1486729740000,"doc_count":13},{"key_as_string":"2017-02-10T12:30:00.000Z","key":1486729800000,"doc_count":9},{"key_as_string":"2017-02-10T12:31:00.000Z","key":1486729860000,"doc_count":16},{"key_as_string":"2017-02-10T12:32:00.000Z","key":1486729920000,"doc_count":7},{"key_as_string":"2017-02-10T12:33:00.000Z","key":1486729980000,"doc_count":15},{"key_as_string":"2017-02-10T12:34:00.000Z","key":1486730040000,"doc_count":10},{"key_as_string":"2017-02-10T12:35:00.000Z","key":1486730100000,"doc_count":15},{"key_as_string":"2017-02-10T12:36:00.000Z","key":1486730160000,"doc_count":15},{"key_as_string":"2017-02-10T12:37:00.000Z","key":1486730220000,"doc_count":17},{"key_as_string":"2017-02-10T12:38:00.000Z","key":1486730280000,"doc_count":15},{"key_as_string":"2017-02-10T12:39:00.000Z","key":1486730340000,"doc_count":13},{"key_as_string":"2017-02-10T12:40:00.000Z","key":1486730400000,"doc_count":12},{"key_as_string":"2017-02-10T12:41:00.000Z","key":1486730460000,"doc_count":10},{"key_as_string":"2017-02-10T12:42:00.000Z","key":1486730520000,"doc_count":11},{"key_as_string":"2017-02-10T12:43:00.000Z","key":1486730580000,"doc_count":15},{"key_as_string":"2017-02-10T12:44:00.000Z","key":1486730640000,"doc_count":14},{"key_as_string":"2017-02-10T12:45:00.000Z","key":1486730700000,"doc_count":15},{"key_as_string":"2017-02-10T12:46:00.000Z","key":1486730760000,"doc_count":12},{"key_as_string":"2017-02-10T12:47:00.000Z","key":1486730820000,"doc_count":14},{"key_as_string":"2017-02-10T12:48:00.000Z","key":1486730880000,"doc_count":15},{"key_as_string":"2017-02-10T12:49:00.000Z","key":1486730940000,"doc_count":14},{"key_as_string":"2017-02-10T12:50:00.000Z","key":1486731000000,"doc_count":14},{"key_as_string":"2017-02-10T12:51:00.000Z","key":1486731060000,"doc_count":13},{"key_as_string":"2017-02-10T12:52:00.000Z","key":1486731120000,"doc_count":17},{"key_as_string":"2017-02-10T12:53:00.000Z","key":1486731180000,"doc_count":10},{"key_as_string":"2017-02-10T12:54:00.000Z","key":1486731240000,"doc_count":12},{"key_as_string":"2017-02-10T12:55:00.000Z","key":1486731300000,"doc_count":16},{"key_as_string":"2017-02-10T12:56:00.000Z","key":1486731360000,"doc_count":8},{"key_as_string":"2017-02-10T12:57:00.000Z","key":1486731420000,"doc_count":12},{"key_as_string":"2017-02-10T12:58:00.000Z","key":1486731480000,"doc_count":22},{"key_as_string":"2017-02-10T12:59:00.000Z","key":1486731540000,"doc_count":13},{"key_as_string":"2017-02-10T13:00:00.000Z","key":1486731600000,"doc_count":15},{"key_as_string":"2017-02-10T13:01:00.000Z","key":1486731660000,"doc_count":8},{"key_as_string":"2017-02-10T13:02:00.000Z","key":1486731720000,"doc_count":19},{"key_as_string":"2017-02-10T13:03:00.000Z","key":1486731780000,"doc_count":9},{"key_as_string":"2017-02-10T13:04:00.000Z","key":1486731840000,"doc_count":12},{"key_as_string":"2017-02-10T13:05:00.000Z","key":1486731900000,"doc_count":15},{"key_as_string":"2017-02-10T13:06:00.000Z","key":1486731960000,"doc_count":15},{"key_as_string":"2017-02-10T13:07:00.000Z","key":1486732020000,"doc_count":16},{"key_as_string":"2017-02-10T13:08:00.000Z","key":1486732080000,"doc_count":14},{"key_as_string":"2017-02-10T13:09:00.000Z","key":1486732140000,"doc_count":15},{"key_as_string":"2017-02-10T13:10:00.000Z","key":1486732200000,"doc_count":9},{"key_as_string":"2017-02-10T13:11:00.000Z","key":1486732260000,"doc_count":14},{"key_as_string":"2017-02-10T13:12:00.000Z","key":1486732320000,"doc_count":15},{"key_as_string":"2017-02-10T13:13:00.000Z","key":1486732380000,"doc_count":12},{"key_as_string":"2017-02-10T13:14:00.000Z","key":1486732440000,"doc_count":13},{"key_as_string":"2017-02-10T13:15:00.000Z","key":1486732500000,"doc_count":22},{"key_as_string":"2017-02-10T13:16:00.000Z","key":1486732560000,"doc_count":9},{"key_as_string":"2017-02-10T13:17:00.000Z","key":1486732620000,"doc_count":13},{"key_as_string":"2017-02-10T13:18:00.000Z","key":1486732680000,"doc_count":14},{"key_as_string":"2017-02-10T13:19:00.000Z","key":1486732740000,"doc_count":9},{"key_as_string":"2017-02-10T13:20:00.000Z","key":1486732800000,"doc_count":11},{"key_as_string":"2017-02-10T13:21:00.000Z","key":1486732860000,"doc_count":12},{"key_as_string":"2017-02-10T13:22:00.000Z","key":1486732920000,"doc_count":16},{"key_as_string":"2017-02-10T13:23:00.000Z","key":1486732980000,"doc_count":15},{"key_as_string":"2017-02-10T13:24:00.000Z","key":1486733040000,"doc_count":9},{"key_as_string":"2017-02-10T13:25:00.000Z","key":1486733100000,"doc_count":12},{"key_as_string":"2017-02-10T13:26:00.000Z","key":1486733160000,"doc_count":14},{"key_as_string":"2017-02-10T13:27:00.000Z","key":1486733220000,"doc_count":17},{"key_as_string":"2017-02-10T13:28:00.000Z","key":1486733280000,"doc_count":5},{"key_as_string":"2017-02-10T13:29:00.000Z","key":1486733340000,"doc_count":16},{"key_as_string":"2017-02-10T13:30:00.000Z","key":1486733400000,"doc_count":9},{"key_as_string":"2017-02-10T13:31:00.000Z","key":1486733460000,"doc_count":17},{"key_as_string":"2017-02-10T13:32:00.000Z","key":1486733520000,"doc_count":12},{"key_as_string":"2017-02-10T13:33:00.000Z","key":1486733580000,"doc_count":10},{"key_as_string":"2017-02-10T13:34:00.000Z","key":1486733640000,"doc_count":18},{"key_as_string":"2017-02-10T13:35:00.000Z","key":1486733700000,"doc_count":5},{"key_as_string":"2017-02-10T13:36:00.000Z","key":1486733760000,"doc_count":13},{"key_as_string":"2017-02-10T13:37:00.000Z","key":1486733820000,"doc_count":18},{"key_as_string":"2017-02-10T13:38:00.000Z","key":1486733880000,"doc_count":17},{"key_as_string":"2017-02-10T13:39:00.000Z","key":1486733940000,"doc_count":15},{"key_as_string":"2017-02-10T13:40:00.000Z","key":1486734000000,"doc_count":16},{"key_as_string":"2017-02-10T13:41:00.000Z","key":1486734060000,"doc_count":8},{"key_as_string":"2017-02-10T13:42:00.000Z","key":1486734120000,"doc_count":17},{"key_as_string":"2017-02-10T13:43:00.000Z","key":1486734180000,"doc_count":14},{"key_as_string":"2017-02-10T13:44:00.000Z","key":1486734240000,"doc_count":14},{"key_as_string":"2017-02-10T13:45:00.000Z","key":1486734300000,"doc_count":17},{"key_as_string":"2017-02-10T13:46:00.000Z","key":1486734360000,"doc_count":14},{"key_as_string":"2017-02-10T13:47:00.000Z","key":1486734420000,"doc_count":16},{"key_as_string":"2017-02-10T13:48:00.000Z","key":1486734480000,"doc_count":16},{"key_as_string":"2017-02-10T13:49:00.000Z","key":1486734540000,"doc_count":21},{"key_as_string":"2017-02-10T13:50:00.000Z","key":1486734600000,"doc_count":12},{"key_as_string":"2017-02-10T13:51:00.000Z","key":1486734660000,"doc_count":12},{"key_as_string":"2017-02-10T13:52:00.000Z","key":1486734720000,"doc_count":8},{"key_as_string":"2017-02-10T13:53:00.000Z","key":1486734780000,"doc_count":15},{"key_as_string":"2017-02-10T13:54:00.000Z","key":1486734840000,"doc_count":13},{"key_as_string":"2017-02-10T13:55:00.000Z","key":1486734900000,"doc_count":14},{"key_as_string":"2017-02-10T13:56:00.000Z","key":1486734960000,"doc_count":13},{"key_as_string":"2017-02-10T13:57:00.000Z","key":1486735020000,"doc_count":12},{"key_as_string":"2017-02-10T13:58:00.000Z","key":1486735080000,"doc_count":14},{"key_as_string":"2017-02-10T13:59:00.000Z","key":1486735140000,"doc_count":13},{"key_as_string":"2017-02-10T14:00:00.000Z","key":1486735200000,"doc_count":16},{"key_as_string":"2017-02-10T14:01:00.000Z","key":1486735260000,"doc_count":9},{"key_as_string":"2017-02-10T14:02:00.000Z","key":1486735320000,"doc_count":20},{"key_as_string":"2017-02-10T14:03:00.000Z","key":1486735380000,"doc_count":15},{"key_as_string":"2017-02-10T14:04:00.000Z","key":1486735440000,"doc_count":13},{"key_as_string":"2017-02-10T14:05:00.000Z","key":1486735500000,"doc_count":10},{"key_as_string":"2017-02-10T14:06:00.000Z","key":1486735560000,"doc_count":11},{"key_as_string":"2017-02-10T14:07:00.000Z","key":1486735620000,"doc_count":9},{"key_as_string":"2017-02-10T14:08:00.000Z","key":1486735680000,"doc_count":15},{"key_as_string":"2017-02-10T14:09:00.000Z","key":1486735740000,"doc_count":17},{"key_as_string":"2017-02-10T14:10:00.000Z","key":1486735800000,"doc_count":13},{"key_as_string":"2017-02-10T14:11:00.000Z","key":1486735860000,"doc_count":15},{"key_as_string":"2017-02-10T14:12:00.000Z","key":1486735920000,"doc_count":10},{"key_as_string":"2017-02-10T14:13:00.000Z","key":1486735980000,"doc_count":12},{"key_as_string":"2017-02-10T14:14:00.000Z","key":1486736040000,"doc_count":15},{"key_as_string":"2017-02-10T14:15:00.000Z","key":1486736100000,"doc_count":13},{"key_as_string":"2017-02-10T14:16:00.000Z","key":1486736160000,"doc_count":13},{"key_as_string":"2017-02-10T14:17:00.000Z","key":1486736220000,"doc_count":15},{"key_as_string":"2017-02-10T14:18:00.000Z","key":1486736280000,"doc_count":14},{"key_as_string":"2017-02-10T14:19:00.000Z","key":1486736340000,"doc_count":9},{"key_as_string":"2017-02-10T14:20:00.000Z","key":1486736400000,"doc_count":15},{"key_as_string":"2017-02-10T14:21:00.000Z","key":1486736460000,"doc_count":12},{"key_as_string":"2017-02-10T14:22:00.000Z","key":1486736520000,"doc_count":12},{"key_as_string":"2017-02-10T14:23:00.000Z","key":1486736580000,"doc_count":16},{"key_as_string":"2017-02-10T14:24:00.000Z","key":1486736640000,"doc_count":4},{"key_as_string":"2017-02-10T14:25:00.000Z","key":1486736700000,"doc_count":12},{"key_as_string":"2017-02-10T14:26:00.000Z","key":1486736760000,"doc_count":15},{"key_as_string":"2017-02-10T14:27:00.000Z","key":1486736820000,"doc_count":17},{"key_as_string":"2017-02-10T14:28:00.000Z","key":1486736880000,"doc_count":15},{"key_as_string":"2017-02-10T14:29:00.000Z","key":1486736940000,"doc_count":13},{"key_as_string":"2017-02-10T14:30:00.000Z","key":1486737000000,"doc_count":9},{"key_as_string":"2017-02-10T14:31:00.000Z","key":1486737060000,"doc_count":17},{"key_as_string":"2017-02-10T14:32:00.000Z","key":1486737120000,"doc_count":14},{"key_as_string":"2017-02-10T14:33:00.000Z","key":1486737180000,"doc_count":11},{"key_as_string":"2017-02-10T14:34:00.000Z","key":1486737240000,"doc_count":10},{"key_as_string":"2017-02-10T14:35:00.000Z","key":1486737300000,"doc_count":10},{"key_as_string":"2017-02-10T14:36:00.000Z","key":1486737360000,"doc_count":12},{"key_as_string":"2017-02-10T14:37:00.000Z","key":1486737420000,"doc_count":14},{"key_as_string":"2017-02-10T14:38:00.000Z","key":1486737480000,"doc_count":15},{"key_as_string":"2017-02-10T14:39:00.000Z","key":1486737540000,"doc_count":8},{"key_as_string":"2017-02-10T14:40:00.000Z","key":1486737600000,"doc_count":15},{"key_as_string":"2017-02-10T14:41:00.000Z","key":1486737660000,"doc_count":10},{"key_as_string":"2017-02-10T14:42:00.000Z","key":1486737720000,"doc_count":14},{"key_as_string":"2017-02-10T14:43:00.000Z","key":1486737780000,"doc_count":11},{"key_as_string":"2017-02-10T14:44:00.000Z","key":1486737840000,"doc_count":12},{"key_as_string":"2017-02-10T14:45:00.000Z","key":1486737900000,"doc_count":18},{"key_as_string":"2017-02-10T14:46:00.000Z","key":1486737960000,"doc_count":12},{"key_as_string":"2017-02-10T14:47:00.000Z","key":1486738020000,"doc_count":14},{"key_as_string":"2017-02-10T14:48:00.000Z","key":1486738080000,"doc_count":13},{"key_as_string":"2017-02-10T14:49:00.000Z","key":1486738140000,"doc_count":9},{"key_as_string":"2017-02-10T14:50:00.000Z","key":1486738200000,"doc_count":20},{"key_as_string":"2017-02-10T14:51:00.000Z","key":1486738260000,"doc_count":11},{"key_as_string":"2017-02-10T14:52:00.000Z","key":1486738320000,"doc_count":9},{"key_as_string":"2017-02-10T14:53:00.000Z","key":1486738380000,"doc_count":14},{"key_as_string":"2017-02-10T14:54:00.000Z","key":1486738440000,"doc_count":15},{"key_as_string":"2017-02-10T14:55:00.000Z","key":1486738500000,"doc_count":13},{"key_as_string":"2017-02-10T14:56:00.000Z","key":1486738560000,"doc_count":12},{"key_as_string":"2017-02-10T14:57:00.000Z","key":1486738620000,"doc_count":15},{"key_as_string":"2017-02-10T14:58:00.000Z","key":1486738680000,"doc_count":9},{"key_as_string":"2017-02-10T14:59:00.000Z","key":1486738740000,"doc_count":12},{"key_as_string":"2017-02-10T15:00:00.000Z","key":1486738800000,"doc_count":15},{"key_as_string":"2017-02-10T15:01:00.000Z","key":1486738860000,"doc_count":11},{"key_as_string":"2017-02-10T15:02:00.000Z","key":1486738920000,"doc_count":14},{"key_as_string":"2017-02-10T15:03:00.000Z","key":1486738980000,"doc_count":11},{"key_as_string":"2017-02-10T15:04:00.000Z","key":1486739040000,"doc_count":12},{"key_as_string":"2017-02-10T15:05:00.000Z","key":1486739100000,"doc_count":11},{"key_as_string":"2017-02-10T15:06:00.000Z","key":1486739160000,"doc_count":17},{"key_as_string":"2017-02-10T15:07:00.000Z","key":1486739220000,"doc_count":12},{"key_as_string":"2017-02-10T15:08:00.000Z","key":1486739280000,"doc_count":10},{"key_as_string":"2017-02-10T15:09:00.000Z","key":1486739340000,"doc_count":19},{"key_as_string":"2017-02-10T15:10:00.000Z","key":1486739400000,"doc_count":10},{"key_as_string":"2017-02-10T15:11:00.000Z","key":1486739460000,"doc_count":10},{"key_as_string":"2017-02-10T15:12:00.000Z","key":1486739520000,"doc_count":18},{"key_as_string":"2017-02-10T15:13:00.000Z","key":1486739580000,"doc_count":14},{"key_as_string":"2017-02-10T15:14:00.000Z","key":1486739640000,"doc_count":11},{"key_as_string":"2017-02-10T15:15:00.000Z","key":1486739700000,"doc_count":16},{"key_as_string":"2017-02-10T15:16:00.000Z","key":1486739760000,"doc_count":13},{"key_as_string":"2017-02-10T15:17:00.000Z","key":1486739820000,"doc_count":20},{"key_as_string":"2017-02-10T15:18:00.000Z","key":1486739880000,"doc_count":10},{"key_as_string":"2017-02-10T15:19:00.000Z","key":1486739940000,"doc_count":16},{"key_as_string":"2017-02-10T15:20:00.000Z","key":1486740000000,"doc_count":15},{"key_as_string":"2017-02-10T15:21:00.000Z","key":1486740060000,"doc_count":16},{"key_as_string":"2017-02-10T15:22:00.000Z","key":1486740120000,"doc_count":20},{"key_as_string":"2017-02-10T15:23:00.000Z","key":1486740180000,"doc_count":12},{"key_as_string":"2017-02-10T15:24:00.000Z","key":1486740240000,"doc_count":15},{"key_as_string":"2017-02-10T15:25:00.000Z","key":1486740300000,"doc_count":12},{"key_as_string":"2017-02-10T15:26:00.000Z","key":1486740360000,"doc_count":16},{"key_as_string":"2017-02-10T15:27:00.000Z","key":1486740420000,"doc_count":14},{"key_as_string":"2017-02-10T15:28:00.000Z","key":1486740480000,"doc_count":12},{"key_as_string":"2017-02-10T15:29:00.000Z","key":1486740540000,"doc_count":10},{"key_as_string":"2017-02-10T15:30:00.000Z","key":1486740600000,"doc_count":16},{"key_as_string":"2017-02-10T15:31:00.000Z","key":1486740660000,"doc_count":13},{"key_as_string":"2017-02-10T15:32:00.000Z","key":1486740720000,"doc_count":14},{"key_as_string":"2017-02-10T15:33:00.000Z","key":1486740780000,"doc_count":11},{"key_as_string":"2017-02-10T15:34:00.000Z","key":1486740840000,"doc_count":12},{"key_as_string":"2017-02-10T15:35:00.000Z","key":1486740900000,"doc_count":12},{"key_as_string":"2017-02-10T15:36:00.000Z","key":1486740960000,"doc_count":11},{"key_as_string":"2017-02-10T15:37:00.000Z","key":1486741020000,"doc_count":13},{"key_as_string":"2017-02-10T15:38:00.000Z","key":1486741080000,"doc_count":11},{"key_as_string":"2017-02-10T15:39:00.000Z","key":1486741140000,"doc_count":11},{"key_as_string":"2017-02-10T15:40:00.000Z","key":1486741200000,"doc_count":12},{"key_as_string":"2017-02-10T15:41:00.000Z","key":1486741260000,"doc_count":16},{"key_as_string":"2017-02-10T15:42:00.000Z","key":1486741320000,"doc_count":15},{"key_as_string":"2017-02-10T15:43:00.000Z","key":1486741380000,"doc_count":15},{"key_as_string":"2017-02-10T15:44:00.000Z","key":1486741440000,"doc_count":15},{"key_as_string":"2017-02-10T15:45:00.000Z","key":1486741500000,"doc_count":15},{"key_as_string":"2017-02-10T15:46:00.000Z","key":1486741560000,"doc_count":11},{"key_as_string":"2017-02-10T15:47:00.000Z","key":1486741620000,"doc_count":14},{"key_as_string":"2017-02-10T15:48:00.000Z","key":1486741680000,"doc_count":15},{"key_as_string":"2017-02-10T15:49:00.000Z","key":1486741740000,"doc_count":12},{"key_as_string":"2017-02-10T15:50:00.000Z","key":1486741800000,"doc_count":12},{"key_as_string":"2017-02-10T15:51:00.000Z","key":1486741860000,"doc_count":12},{"key_as_string":"2017-02-10T15:52:00.000Z","key":1486741920000,"doc_count":19},{"key_as_string":"2017-02-10T15:53:00.000Z","key":1486741980000,"doc_count":14},{"key_as_string":"2017-02-10T15:54:00.000Z","key":1486742040000,"doc_count":18},{"key_as_string":"2017-02-10T15:55:00.000Z","key":1486742100000,"doc_count":11},{"key_as_string":"2017-02-10T15:56:00.000Z","key":1486742160000,"doc_count":16},{"key_as_string":"2017-02-10T15:57:00.000Z","key":1486742220000,"doc_count":11},{"key_as_string":"2017-02-10T15:58:00.000Z","key":1486742280000,"doc_count":10},{"key_as_string":"2017-02-10T15:59:00.000Z","key":1486742340000,"doc_count":12},{"key_as_string":"2017-02-10T16:00:00.000Z","key":1486742400000,"doc_count":16},{"key_as_string":"2017-02-10T16:01:00.000Z","key":1486742460000,"doc_count":12},{"key_as_string":"2017-02-10T16:02:00.000Z","key":1486742520000,"doc_count":16},{"key_as_string":"2017-02-10T16:03:00.000Z","key":1486742580000,"doc_count":11},{"key_as_string":"2017-02-10T16:04:00.000Z","key":1486742640000,"doc_count":13},{"key_as_string":"2017-02-10T16:05:00.000Z","key":1486742700000,"doc_count":15},{"key_as_string":"2017-02-10T16:06:00.000Z","key":1486742760000,"doc_count":13},{"key_as_string":"2017-02-10T16:07:00.000Z","key":1486742820000,"doc_count":14},{"key_as_string":"2017-02-10T16:08:00.000Z","key":1486742880000,"doc_count":16},{"key_as_string":"2017-02-10T16:09:00.000Z","key":1486742940000,"doc_count":8},{"key_as_string":"2017-02-10T16:10:00.000Z","key":1486743000000,"doc_count":13},{"key_as_string":"2017-02-10T16:11:00.000Z","key":1486743060000,"doc_count":13},{"key_as_string":"2017-02-10T16:12:00.000Z","key":1486743120000,"doc_count":15},{"key_as_string":"2017-02-10T16:13:00.000Z","key":1486743180000,"doc_count":20},{"key_as_string":"2017-02-10T16:14:00.000Z","key":1486743240000,"doc_count":12},{"key_as_string":"2017-02-10T16:15:00.000Z","key":1486743300000,"doc_count":10},{"key_as_string":"2017-02-10T16:16:00.000Z","key":1486743360000,"doc_count":12},{"key_as_string":"2017-02-10T16:17:00.000Z","key":1486743420000,"doc_count":12},{"key_as_string":"2017-02-10T16:18:00.000Z","key":1486743480000,"doc_count":15},{"key_as_string":"2017-02-10T16:19:00.000Z","key":1486743540000,"doc_count":11},{"key_as_string":"2017-02-10T16:20:00.000Z","key":1486743600000,"doc_count":10},{"key_as_string":"2017-02-10T16:21:00.000Z","key":1486743660000,"doc_count":13},{"key_as_string":"2017-02-10T16:22:00.000Z","key":1486743720000,"doc_count":12},{"key_as_string":"2017-02-10T16:23:00.000Z","key":1486743780000,"doc_count":15},{"key_as_string":"2017-02-10T16:24:00.000Z","key":1486743840000,"doc_count":17},{"key_as_string":"2017-02-10T16:25:00.000Z","key":1486743900000,"doc_count":12},{"key_as_string":"2017-02-10T16:26:00.000Z","key":1486743960000,"doc_count":13},{"key_as_string":"2017-02-10T16:27:00.000Z","key":1486744020000,"doc_count":11},{"key_as_string":"2017-02-10T16:28:00.000Z","key":1486744080000,"doc_count":8},{"key_as_string":"2017-02-10T16:29:00.000Z","key":1486744140000,"doc_count":14},{"key_as_string":"2017-02-10T16:30:00.000Z","key":1486744200000,"doc_count":12},{"key_as_string":"2017-02-10T16:31:00.000Z","key":1486744260000,"doc_count":12},{"key_as_string":"2017-02-10T16:32:00.000Z","key":1486744320000,"doc_count":12},{"key_as_string":"2017-02-10T16:33:00.000Z","key":1486744380000,"doc_count":8},{"key_as_string":"2017-02-10T16:34:00.000Z","key":1486744440000,"doc_count":18},{"key_as_string":"2017-02-10T16:35:00.000Z","key":1486744500000,"doc_count":14},{"key_as_string":"2017-02-10T16:36:00.000Z","key":1486744560000,"doc_count":12},{"key_as_string":"2017-02-10T16:37:00.000Z","key":1486744620000,"doc_count":10},{"key_as_string":"2017-02-10T16:38:00.000Z","key":1486744680000,"doc_count":19},{"key_as_string":"2017-02-10T16:39:00.000Z","key":1486744740000,"doc_count":8},{"key_as_string":"2017-02-10T16:40:00.000Z","key":1486744800000,"doc_count":12},{"key_as_string":"2017-02-10T16:41:00.000Z","key":1486744860000,"doc_count":16},{"key_as_string":"2017-02-10T16:42:00.000Z","key":1486744920000,"doc_count":11},{"key_as_string":"2017-02-10T16:43:00.000Z","key":1486744980000,"doc_count":15},{"key_as_string":"2017-02-10T16:44:00.000Z","key":1486745040000,"doc_count":9},{"key_as_string":"2017-02-10T16:45:00.000Z","key":1486745100000,"doc_count":15},{"key_as_string":"2017-02-10T16:46:00.000Z","key":1486745160000,"doc_count":11},{"key_as_string":"2017-02-10T16:47:00.000Z","key":1486745220000,"doc_count":16},{"key_as_string":"2017-02-10T16:48:00.000Z","key":1486745280000,"doc_count":17},{"key_as_string":"2017-02-10T16:49:00.000Z","key":1486745340000,"doc_count":10},{"key_as_string":"2017-02-10T16:50:00.000Z","key":1486745400000,"doc_count":11},{"key_as_string":"2017-02-10T16:51:00.000Z","key":1486745460000,"doc_count":13},{"key_as_string":"2017-02-10T16:52:00.000Z","key":1486745520000,"doc_count":7},{"key_as_string":"2017-02-10T16:53:00.000Z","key":1486745580000,"doc_count":13},{"key_as_string":"2017-02-10T16:54:00.000Z","key":1486745640000,"doc_count":12},{"key_as_string":"2017-02-10T16:55:00.000Z","key":1486745700000,"doc_count":18},{"key_as_string":"2017-02-10T16:56:00.000Z","key":1486745760000,"doc_count":6},{"key_as_string":"2017-02-10T16:57:00.000Z","key":1486745820000,"doc_count":11},{"key_as_string":"2017-02-10T16:58:00.000Z","key":1486745880000,"doc_count":10},{"key_as_string":"2017-02-10T16:59:00.000Z","key":1486745940000,"doc_count":12},{"key_as_string":"2017-02-10T17:00:00.000Z","key":1486746000000,"doc_count":13},{"key_as_string":"2017-02-10T17:01:00.000Z","key":1486746060000,"doc_count":9},{"key_as_string":"2017-02-10T17:02:00.000Z","key":1486746120000,"doc_count":14},{"key_as_string":"2017-02-10T17:03:00.000Z","key":1486746180000,"doc_count":13},{"key_as_string":"2017-02-10T17:04:00.000Z","key":1486746240000,"doc_count":12},{"key_as_string":"2017-02-10T17:05:00.000Z","key":1486746300000,"doc_count":9},{"key_as_string":"2017-02-10T17:06:00.000Z","key":1486746360000,"doc_count":14},{"key_as_string":"2017-02-10T17:07:00.000Z","key":1486746420000,"doc_count":15},{"key_as_string":"2017-02-10T17:08:00.000Z","key":1486746480000,"doc_count":11},{"key_as_string":"2017-02-10T17:09:00.000Z","key":1486746540000,"doc_count":8},{"key_as_string":"2017-02-10T17:10:00.000Z","key":1486746600000,"doc_count":9},{"key_as_string":"2017-02-10T17:11:00.000Z","key":1486746660000,"doc_count":12},{"key_as_string":"2017-02-10T17:12:00.000Z","key":1486746720000,"doc_count":15},{"key_as_string":"2017-02-10T17:13:00.000Z","key":1486746780000,"doc_count":13},{"key_as_string":"2017-02-10T17:14:00.000Z","key":1486746840000,"doc_count":14},{"key_as_string":"2017-02-10T17:15:00.000Z","key":1486746900000,"doc_count":10},{"key_as_string":"2017-02-10T17:16:00.000Z","key":1486746960000,"doc_count":12},{"key_as_string":"2017-02-10T17:17:00.000Z","key":1486747020000,"doc_count":10},{"key_as_string":"2017-02-10T17:18:00.000Z","key":1486747080000,"doc_count":14},{"key_as_string":"2017-02-10T17:19:00.000Z","key":1486747140000,"doc_count":7},{"key_as_string":"2017-02-10T17:20:00.000Z","key":1486747200000,"doc_count":9},{"key_as_string":"2017-02-10T17:21:00.000Z","key":1486747260000,"doc_count":10},{"key_as_string":"2017-02-10T17:22:00.000Z","key":1486747320000,"doc_count":13},{"key_as_string":"2017-02-10T17:23:00.000Z","key":1486747380000,"doc_count":16},{"key_as_string":"2017-02-10T17:24:00.000Z","key":1486747440000,"doc_count":11},{"key_as_string":"2017-02-10T17:25:00.000Z","key":1486747500000,"doc_count":14},{"key_as_string":"2017-02-10T17:26:00.000Z","key":1486747560000,"doc_count":12},{"key_as_string":"2017-02-10T17:27:00.000Z","key":1486747620000,"doc_count":12},{"key_as_string":"2017-02-10T17:28:00.000Z","key":1486747680000,"doc_count":9},{"key_as_string":"2017-02-10T17:29:00.000Z","key":1486747740000,"doc_count":16},{"key_as_string":"2017-02-10T17:30:00.000Z","key":1486747800000,"doc_count":14},{"key_as_string":"2017-02-10T17:31:00.000Z","key":1486747860000,"doc_count":10},{"key_as_string":"2017-02-10T17:32:00.000Z","key":1486747920000,"doc_count":8},{"key_as_string":"2017-02-10T17:33:00.000Z","key":1486747980000,"doc_count":9},{"key_as_string":"2017-02-10T17:34:00.000Z","key":1486748040000,"doc_count":15},{"key_as_string":"2017-02-10T17:35:00.000Z","key":1486748100000,"doc_count":9},{"key_as_string":"2017-02-10T17:36:00.000Z","key":1486748160000,"doc_count":13},{"key_as_string":"2017-02-10T17:37:00.000Z","key":1486748220000,"doc_count":9},{"key_as_string":"2017-02-10T17:38:00.000Z","key":1486748280000,"doc_count":7},{"key_as_string":"2017-02-10T17:39:00.000Z","key":1486748340000,"doc_count":18},{"key_as_string":"2017-02-10T17:40:00.000Z","key":1486748400000,"doc_count":14},{"key_as_string":"2017-02-10T17:41:00.000Z","key":1486748460000,"doc_count":9},{"key_as_string":"2017-02-10T17:42:00.000Z","key":1486748520000,"doc_count":12},{"key_as_string":"2017-02-10T17:43:00.000Z","key":1486748580000,"doc_count":9},{"key_as_string":"2017-02-10T17:44:00.000Z","key":1486748640000,"doc_count":14},{"key_as_string":"2017-02-10T17:45:00.000Z","key":1486748700000,"doc_count":11},{"key_as_string":"2017-02-10T17:46:00.000Z","key":1486748760000,"doc_count":11},{"key_as_string":"2017-02-10T17:47:00.000Z","key":1486748820000,"doc_count":12},{"key_as_string":"2017-02-10T17:48:00.000Z","key":1486748880000,"doc_count":16},{"key_as_string":"2017-02-10T17:49:00.000Z","key":1486748940000,"doc_count":10},{"key_as_string":"2017-02-10T17:50:00.000Z","key":1486749000000,"doc_count":14},{"key_as_string":"2017-02-10T17:51:00.000Z","key":1486749060000,"doc_count":8},{"key_as_string":"2017-02-10T17:52:00.000Z","key":1486749120000,"doc_count":16},{"key_as_string":"2017-02-10T17:53:00.000Z","key":1486749180000,"doc_count":9},{"key_as_string":"2017-02-10T17:54:00.000Z","key":1486749240000,"doc_count":11},{"key_as_string":"2017-02-10T17:55:00.000Z","key":1486749300000,"doc_count":9},{"key_as_string":"2017-02-10T17:56:00.000Z","key":1486749360000,"doc_count":12},{"key_as_string":"2017-02-10T17:57:00.000Z","key":1486749420000,"doc_count":13},{"key_as_string":"2017-02-10T17:58:00.000Z","key":1486749480000,"doc_count":12},{"key_as_string":"2017-02-10T17:59:00.000Z","key":1486749540000,"doc_count":15},{"key_as_string":"2017-02-10T18:00:00.000Z","key":1486749600000,"doc_count":10},{"key_as_string":"2017-02-10T18:01:00.000Z","key":1486749660000,"doc_count":10},{"key_as_string":"2017-02-10T18:02:00.000Z","key":1486749720000,"doc_count":10},{"key_as_string":"2017-02-10T18:03:00.000Z","key":1486749780000,"doc_count":9},{"key_as_string":"2017-02-10T18:04:00.000Z","key":1486749840000,"doc_count":12},{"key_as_string":"2017-02-10T18:05:00.000Z","key":1486749900000,"doc_count":14},{"key_as_string":"2017-02-10T18:06:00.000Z","key":1486749960000,"doc_count":16},{"key_as_string":"2017-02-10T18:07:00.000Z","key":1486750020000,"doc_count":12},{"key_as_string":"2017-02-10T18:08:00.000Z","key":1486750080000,"doc_count":10},{"key_as_string":"2017-02-10T18:09:00.000Z","key":1486750140000,"doc_count":11},{"key_as_string":"2017-02-10T18:10:00.000Z","key":1486750200000,"doc_count":12},{"key_as_string":"2017-02-10T18:11:00.000Z","key":1486750260000,"doc_count":10},{"key_as_string":"2017-02-10T18:12:00.000Z","key":1486750320000,"doc_count":13},{"key_as_string":"2017-02-10T18:13:00.000Z","key":1486750380000,"doc_count":12},{"key_as_string":"2017-02-10T18:14:00.000Z","key":1486750440000,"doc_count":14},{"key_as_string":"2017-02-10T18:15:00.000Z","key":1486750500000,"doc_count":11},{"key_as_string":"2017-02-10T18:16:00.000Z","key":1486750560000,"doc_count":12},{"key_as_string":"2017-02-10T18:17:00.000Z","key":1486750620000,"doc_count":10},{"key_as_string":"2017-02-10T18:18:00.000Z","key":1486750680000,"doc_count":9},{"key_as_string":"2017-02-10T18:19:00.000Z","key":1486750740000,"doc_count":14},{"key_as_string":"2017-02-10T18:20:00.000Z","key":1486750800000,"doc_count":6},{"key_as_string":"2017-02-10T18:21:00.000Z","key":1486750860000,"doc_count":13},{"key_as_string":"2017-02-10T18:22:00.000Z","key":1486750920000,"doc_count":14},{"key_as_string":"2017-02-10T18:23:00.000Z","key":1486750980000,"doc_count":9},{"key_as_string":"2017-02-10T18:24:00.000Z","key":1486751040000,"doc_count":9},{"key_as_string":"2017-02-10T18:25:00.000Z","key":1486751100000,"doc_count":10},{"key_as_string":"2017-02-10T18:26:00.000Z","key":1486751160000,"doc_count":9},{"key_as_string":"2017-02-10T18:27:00.000Z","key":1486751220000,"doc_count":12},{"key_as_string":"2017-02-10T18:28:00.000Z","key":1486751280000,"doc_count":7},{"key_as_string":"2017-02-10T18:29:00.000Z","key":1486751340000,"doc_count":12},{"key_as_string":"2017-02-10T18:30:00.000Z","key":1486751400000,"doc_count":13},{"key_as_string":"2017-02-10T18:31:00.000Z","key":1486751460000,"doc_count":11},{"key_as_string":"2017-02-10T18:32:00.000Z","key":1486751520000,"doc_count":13},{"key_as_string":"2017-02-10T18:33:00.000Z","key":1486751580000,"doc_count":4},{"key_as_string":"2017-02-10T18:34:00.000Z","key":1486751640000,"doc_count":12},{"key_as_string":"2017-02-10T18:35:00.000Z","key":1486751700000,"doc_count":12},{"key_as_string":"2017-02-10T18:36:00.000Z","key":1486751760000,"doc_count":9},{"key_as_string":"2017-02-10T18:37:00.000Z","key":1486751820000,"doc_count":14},{"key_as_string":"2017-02-10T18:38:00.000Z","key":1486751880000,"doc_count":9},{"key_as_string":"2017-02-10T18:39:00.000Z","key":1486751940000,"doc_count":12},{"key_as_string":"2017-02-10T18:40:00.000Z","key":1486752000000,"doc_count":10},{"key_as_string":"2017-02-10T18:41:00.000Z","key":1486752060000,"doc_count":15},{"key_as_string":"2017-02-10T18:42:00.000Z","key":1486752120000,"doc_count":13},{"key_as_string":"2017-02-10T18:43:00.000Z","key":1486752180000,"doc_count":16},{"key_as_string":"2017-02-10T18:44:00.000Z","key":1486752240000,"doc_count":12},{"key_as_string":"2017-02-10T18:45:00.000Z","key":1486752300000,"doc_count":11},{"key_as_string":"2017-02-10T18:46:00.000Z","key":1486752360000,"doc_count":13},{"key_as_string":"2017-02-10T18:47:00.000Z","key":1486752420000,"doc_count":15},{"key_as_string":"2017-02-10T18:48:00.000Z","key":1486752480000,"doc_count":6},{"key_as_string":"2017-02-10T18:49:00.000Z","key":1486752540000,"doc_count":9},{"key_as_string":"2017-02-10T18:50:00.000Z","key":1486752600000,"doc_count":19},{"key_as_string":"2017-02-10T18:51:00.000Z","key":1486752660000,"doc_count":11},{"key_as_string":"2017-02-10T18:52:00.000Z","key":1486752720000,"doc_count":11},{"key_as_string":"2017-02-10T18:53:00.000Z","key":1486752780000,"doc_count":11},{"key_as_string":"2017-02-10T18:54:00.000Z","key":1486752840000,"doc_count":12},{"key_as_string":"2017-02-10T18:55:00.000Z","key":1486752900000,"doc_count":11},{"key_as_string":"2017-02-10T18:56:00.000Z","key":1486752960000,"doc_count":12},{"key_as_string":"2017-02-10T18:57:00.000Z","key":1486753020000,"doc_count":11},{"key_as_string":"2017-02-10T18:58:00.000Z","key":1486753080000,"doc_count":13},{"key_as_string":"2017-02-10T18:59:00.000Z","key":1486753140000,"doc_count":12},{"key_as_string":"2017-02-10T19:00:00.000Z","key":1486753200000,"doc_count":10},{"key_as_string":"2017-02-10T19:01:00.000Z","key":1486753260000,"doc_count":7},{"key_as_string":"2017-02-10T19:02:00.000Z","key":1486753320000,"doc_count":15},{"key_as_string":"2017-02-10T19:03:00.000Z","key":1486753380000,"doc_count":10},{"key_as_string":"2017-02-10T19:04:00.000Z","key":1486753440000,"doc_count":11},{"key_as_string":"2017-02-10T19:05:00.000Z","key":1486753500000,"doc_count":10},{"key_as_string":"2017-02-10T19:06:00.000Z","key":1486753560000,"doc_count":15},{"key_as_string":"2017-02-10T19:07:00.000Z","key":1486753620000,"doc_count":12},{"key_as_string":"2017-02-10T19:08:00.000Z","key":1486753680000,"doc_count":7},{"key_as_string":"2017-02-10T19:09:00.000Z","key":1486753740000,"doc_count":11},{"key_as_string":"2017-02-10T19:10:00.000Z","key":1486753800000,"doc_count":9},{"key_as_string":"2017-02-10T19:11:00.000Z","key":1486753860000,"doc_count":11},{"key_as_string":"2017-02-10T19:12:00.000Z","key":1486753920000,"doc_count":10},{"key_as_string":"2017-02-10T19:13:00.000Z","key":1486753980000,"doc_count":11},{"key_as_string":"2017-02-10T19:14:00.000Z","key":1486754040000,"doc_count":14},{"key_as_string":"2017-02-10T19:15:00.000Z","key":1486754100000,"doc_count":10},{"key_as_string":"2017-02-10T19:16:00.000Z","key":1486754160000,"doc_count":12},{"key_as_string":"2017-02-10T19:17:00.000Z","key":1486754220000,"doc_count":12},{"key_as_string":"2017-02-10T19:18:00.000Z","key":1486754280000,"doc_count":11},{"key_as_string":"2017-02-10T19:19:00.000Z","key":1486754340000,"doc_count":11},{"key_as_string":"2017-02-10T19:20:00.000Z","key":1486754400000,"doc_count":10},{"key_as_string":"2017-02-10T19:21:00.000Z","key":1486754460000,"doc_count":15},{"key_as_string":"2017-02-10T19:22:00.000Z","key":1486754520000,"doc_count":8},{"key_as_string":"2017-02-10T19:23:00.000Z","key":1486754580000,"doc_count":7},{"key_as_string":"2017-02-10T19:24:00.000Z","key":1486754640000,"doc_count":20},{"key_as_string":"2017-02-10T19:25:00.000Z","key":1486754700000,"doc_count":10},{"key_as_string":"2017-02-10T19:26:00.000Z","key":1486754760000,"doc_count":9},{"key_as_string":"2017-02-10T19:27:00.000Z","key":1486754820000,"doc_count":13},{"key_as_string":"2017-02-10T19:28:00.000Z","key":1486754880000,"doc_count":12},{"key_as_string":"2017-02-10T19:29:00.000Z","key":1486754940000,"doc_count":10},{"key_as_string":"2017-02-10T19:30:00.000Z","key":1486755000000,"doc_count":11},{"key_as_string":"2017-02-10T19:31:00.000Z","key":1486755060000,"doc_count":10},{"key_as_string":"2017-02-10T19:32:00.000Z","key":1486755120000,"doc_count":10},{"key_as_string":"2017-02-10T19:33:00.000Z","key":1486755180000,"doc_count":11},{"key_as_string":"2017-02-10T19:34:00.000Z","key":1486755240000,"doc_count":12},{"key_as_string":"2017-02-10T19:35:00.000Z","key":1486755300000,"doc_count":8},{"key_as_string":"2017-02-10T19:36:00.000Z","key":1486755360000,"doc_count":10},{"key_as_string":"2017-02-10T19:37:00.000Z","key":1486755420000,"doc_count":13},{"key_as_string":"2017-02-10T19:38:00.000Z","key":1486755480000,"doc_count":11},{"key_as_string":"2017-02-10T19:39:00.000Z","key":1486755540000,"doc_count":7},{"key_as_string":"2017-02-10T19:40:00.000Z","key":1486755600000,"doc_count":17},{"key_as_string":"2017-02-10T19:41:00.000Z","key":1486755660000,"doc_count":8},{"key_as_string":"2017-02-10T19:42:00.000Z","key":1486755720000,"doc_count":15},{"key_as_string":"2017-02-10T19:43:00.000Z","key":1486755780000,"doc_count":11},{"key_as_string":"2017-02-10T19:44:00.000Z","key":1486755840000,"doc_count":11},{"key_as_string":"2017-02-10T19:45:00.000Z","key":1486755900000,"doc_count":8},{"key_as_string":"2017-02-10T19:46:00.000Z","key":1486755960000,"doc_count":8},{"key_as_string":"2017-02-10T19:47:00.000Z","key":1486756020000,"doc_count":15},{"key_as_string":"2017-02-10T19:48:00.000Z","key":1486756080000,"doc_count":15},{"key_as_string":"2017-02-10T19:49:00.000Z","key":1486756140000,"doc_count":11},{"key_as_string":"2017-02-10T19:50:00.000Z","key":1486756200000,"doc_count":15},{"key_as_string":"2017-02-10T19:51:00.000Z","key":1486756260000,"doc_count":9},{"key_as_string":"2017-02-10T19:52:00.000Z","key":1486756320000,"doc_count":10},{"key_as_string":"2017-02-10T19:53:00.000Z","key":1486756380000,"doc_count":9},{"key_as_string":"2017-02-10T19:54:00.000Z","key":1486756440000,"doc_count":12},{"key_as_string":"2017-02-10T19:55:00.000Z","key":1486756500000,"doc_count":9},{"key_as_string":"2017-02-10T19:56:00.000Z","key":1486756560000,"doc_count":13},{"key_as_string":"2017-02-10T19:57:00.000Z","key":1486756620000,"doc_count":12},{"key_as_string":"2017-02-10T19:58:00.000Z","key":1486756680000,"doc_count":10},{"key_as_string":"2017-02-10T19:59:00.000Z","key":1486756740000,"doc_count":7},{"key_as_string":"2017-02-10T20:00:00.000Z","key":1486756800000,"doc_count":12},{"key_as_string":"2017-02-10T20:01:00.000Z","key":1486756860000,"doc_count":12},{"key_as_string":"2017-02-10T20:02:00.000Z","key":1486756920000,"doc_count":12},{"key_as_string":"2017-02-10T20:03:00.000Z","key":1486756980000,"doc_count":8},{"key_as_string":"2017-02-10T20:04:00.000Z","key":1486757040000,"doc_count":20},{"key_as_string":"2017-02-10T20:05:00.000Z","key":1486757100000,"doc_count":7},{"key_as_string":"2017-02-10T20:06:00.000Z","key":1486757160000,"doc_count":16},{"key_as_string":"2017-02-10T20:07:00.000Z","key":1486757220000,"doc_count":13},{"key_as_string":"2017-02-10T20:08:00.000Z","key":1486757280000,"doc_count":9},{"key_as_string":"2017-02-10T20:09:00.000Z","key":1486757340000,"doc_count":12},{"key_as_string":"2017-02-10T20:10:00.000Z","key":1486757400000,"doc_count":7},{"key_as_string":"2017-02-10T20:11:00.000Z","key":1486757460000,"doc_count":8},{"key_as_string":"2017-02-10T20:12:00.000Z","key":1486757520000,"doc_count":10},{"key_as_string":"2017-02-10T20:13:00.000Z","key":1486757580000,"doc_count":8},{"key_as_string":"2017-02-10T20:14:00.000Z","key":1486757640000,"doc_count":17},{"key_as_string":"2017-02-10T20:15:00.000Z","key":1486757700000,"doc_count":11},{"key_as_string":"2017-02-10T20:16:00.000Z","key":1486757760000,"doc_count":12},{"key_as_string":"2017-02-10T20:17:00.000Z","key":1486757820000,"doc_count":12},{"key_as_string":"2017-02-10T20:18:00.000Z","key":1486757880000,"doc_count":12},{"key_as_string":"2017-02-10T20:19:00.000Z","key":1486757940000,"doc_count":15},{"key_as_string":"2017-02-10T20:20:00.000Z","key":1486758000000,"doc_count":7},{"key_as_string":"2017-02-10T20:21:00.000Z","key":1486758060000,"doc_count":11},{"key_as_string":"2017-02-10T20:22:00.000Z","key":1486758120000,"doc_count":10},{"key_as_string":"2017-02-10T20:23:00.000Z","key":1486758180000,"doc_count":14},{"key_as_string":"2017-02-10T20:24:00.000Z","key":1486758240000,"doc_count":11},{"key_as_string":"2017-02-10T20:25:00.000Z","key":1486758300000,"doc_count":9},{"key_as_string":"2017-02-10T20:26:00.000Z","key":1486758360000,"doc_count":7},{"key_as_string":"2017-02-10T20:27:00.000Z","key":1486758420000,"doc_count":10},{"key_as_string":"2017-02-10T20:28:00.000Z","key":1486758480000,"doc_count":14},{"key_as_string":"2017-02-10T20:29:00.000Z","key":1486758540000,"doc_count":12},{"key_as_string":"2017-02-10T20:30:00.000Z","key":1486758600000,"doc_count":10},{"key_as_string":"2017-02-10T20:31:00.000Z","key":1486758660000,"doc_count":11},{"key_as_string":"2017-02-10T20:32:00.000Z","key":1486758720000,"doc_count":12},{"key_as_string":"2017-02-10T20:33:00.000Z","key":1486758780000,"doc_count":15},{"key_as_string":"2017-02-10T20:34:00.000Z","key":1486758840000,"doc_count":12},{"key_as_string":"2017-02-10T20:35:00.000Z","key":1486758900000,"doc_count":14},{"key_as_string":"2017-02-10T20:36:00.000Z","key":1486758960000,"doc_count":6},{"key_as_string":"2017-02-10T20:37:00.000Z","key":1486759020000,"doc_count":11},{"key_as_string":"2017-02-10T20:38:00.000Z","key":1486759080000,"doc_count":15},{"key_as_string":"2017-02-10T20:39:00.000Z","key":1486759140000,"doc_count":11},{"key_as_string":"2017-02-10T20:40:00.000Z","key":1486759200000,"doc_count":10},{"key_as_string":"2017-02-10T20:41:00.000Z","key":1486759260000,"doc_count":10},{"key_as_string":"2017-02-10T20:42:00.000Z","key":1486759320000,"doc_count":8},{"key_as_string":"2017-02-10T20:43:00.000Z","key":1486759380000,"doc_count":10},{"key_as_string":"2017-02-10T20:44:00.000Z","key":1486759440000,"doc_count":10},{"key_as_string":"2017-02-10T20:45:00.000Z","key":1486759500000,"doc_count":9},{"key_as_string":"2017-02-10T20:46:00.000Z","key":1486759560000,"doc_count":12},{"key_as_string":"2017-02-10T20:47:00.000Z","key":1486759620000,"doc_count":11},{"key_as_string":"2017-02-10T20:48:00.000Z","key":1486759680000,"doc_count":13},{"key_as_string":"2017-02-10T20:49:00.000Z","key":1486759740000,"doc_count":8},{"key_as_string":"2017-02-10T20:50:00.000Z","key":1486759800000,"doc_count":10},{"key_as_string":"2017-02-10T20:51:00.000Z","key":1486759860000,"doc_count":10},{"key_as_string":"2017-02-10T20:52:00.000Z","key":1486759920000,"doc_count":14},{"key_as_string":"2017-02-10T20:53:00.000Z","key":1486759980000,"doc_count":10},{"key_as_string":"2017-02-10T20:54:00.000Z","key":1486760040000,"doc_count":17},{"key_as_string":"2017-02-10T20:55:00.000Z","key":1486760100000,"doc_count":8},{"key_as_string":"2017-02-10T20:56:00.000Z","key":1486760160000,"doc_count":10},{"key_as_string":"2017-02-10T20:57:00.000Z","key":1486760220000,"doc_count":11},{"key_as_string":"2017-02-10T20:58:00.000Z","key":1486760280000,"doc_count":9},{"key_as_string":"2017-02-10T20:59:00.000Z","key":1486760340000,"doc_count":14},{"key_as_string":"2017-02-10T21:00:00.000Z","key":1486760400000,"doc_count":11},{"key_as_string":"2017-02-10T21:01:00.000Z","key":1486760460000,"doc_count":13},{"key_as_string":"2017-02-10T21:02:00.000Z","key":1486760520000,"doc_count":8},{"key_as_string":"2017-02-10T21:03:00.000Z","key":1486760580000,"doc_count":13},{"key_as_string":"2017-02-10T21:04:00.000Z","key":1486760640000,"doc_count":10},{"key_as_string":"2017-02-10T21:05:00.000Z","key":1486760700000,"doc_count":11},{"key_as_string":"2017-02-10T21:06:00.000Z","key":1486760760000,"doc_count":5},{"key_as_string":"2017-02-10T21:07:00.000Z","key":1486760820000,"doc_count":11},{"key_as_string":"2017-02-10T21:08:00.000Z","key":1486760880000,"doc_count":12},{"key_as_string":"2017-02-10T21:09:00.000Z","key":1486760940000,"doc_count":7},{"key_as_string":"2017-02-10T21:10:00.000Z","key":1486761000000,"doc_count":13},{"key_as_string":"2017-02-10T21:11:00.000Z","key":1486761060000,"doc_count":10},{"key_as_string":"2017-02-10T21:12:00.000Z","key":1486761120000,"doc_count":13},{"key_as_string":"2017-02-10T21:13:00.000Z","key":1486761180000,"doc_count":11},{"key_as_string":"2017-02-10T21:14:00.000Z","key":1486761240000,"doc_count":11},{"key_as_string":"2017-02-10T21:15:00.000Z","key":1486761300000,"doc_count":9},{"key_as_string":"2017-02-10T21:16:00.000Z","key":1486761360000,"doc_count":13},{"key_as_string":"2017-02-10T21:17:00.000Z","key":1486761420000,"doc_count":10},{"key_as_string":"2017-02-10T21:18:00.000Z","key":1486761480000,"doc_count":12},{"key_as_string":"2017-02-10T21:19:00.000Z","key":1486761540000,"doc_count":8},{"key_as_string":"2017-02-10T21:20:00.000Z","key":1486761600000,"doc_count":11},{"key_as_string":"2017-02-10T21:21:00.000Z","key":1486761660000,"doc_count":13},{"key_as_string":"2017-02-10T21:22:00.000Z","key":1486761720000,"doc_count":9},{"key_as_string":"2017-02-10T21:23:00.000Z","key":1486761780000,"doc_count":13},{"key_as_string":"2017-02-10T21:24:00.000Z","key":1486761840000,"doc_count":8},{"key_as_string":"2017-02-10T21:25:00.000Z","key":1486761900000,"doc_count":8},{"key_as_string":"2017-02-10T21:26:00.000Z","key":1486761960000,"doc_count":12},{"key_as_string":"2017-02-10T21:27:00.000Z","key":1486762020000,"doc_count":10},{"key_as_string":"2017-02-10T21:28:00.000Z","key":1486762080000,"doc_count":16},{"key_as_string":"2017-02-10T21:29:00.000Z","key":1486762140000,"doc_count":7},{"key_as_string":"2017-02-10T21:30:00.000Z","key":1486762200000,"doc_count":10},{"key_as_string":"2017-02-10T21:31:00.000Z","key":1486762260000,"doc_count":16},{"key_as_string":"2017-02-10T21:32:00.000Z","key":1486762320000,"doc_count":14},{"key_as_string":"2017-02-10T21:33:00.000Z","key":1486762380000,"doc_count":6},{"key_as_string":"2017-02-10T21:34:00.000Z","key":1486762440000,"doc_count":9},{"key_as_string":"2017-02-10T21:35:00.000Z","key":1486762500000,"doc_count":11},{"key_as_string":"2017-02-10T21:36:00.000Z","key":1486762560000,"doc_count":12},{"key_as_string":"2017-02-10T21:37:00.000Z","key":1486762620000,"doc_count":16},{"key_as_string":"2017-02-10T21:38:00.000Z","key":1486762680000,"doc_count":11},{"key_as_string":"2017-02-10T21:39:00.000Z","key":1486762740000,"doc_count":10},{"key_as_string":"2017-02-10T21:40:00.000Z","key":1486762800000,"doc_count":11},{"key_as_string":"2017-02-10T21:41:00.000Z","key":1486762860000,"doc_count":11},{"key_as_string":"2017-02-10T21:42:00.000Z","key":1486762920000,"doc_count":13},{"key_as_string":"2017-02-10T21:43:00.000Z","key":1486762980000,"doc_count":11},{"key_as_string":"2017-02-10T21:44:00.000Z","key":1486763040000,"doc_count":14},{"key_as_string":"2017-02-10T21:45:00.000Z","key":1486763100000,"doc_count":9},{"key_as_string":"2017-02-10T21:46:00.000Z","key":1486763160000,"doc_count":9},{"key_as_string":"2017-02-10T21:47:00.000Z","key":1486763220000,"doc_count":16},{"key_as_string":"2017-02-10T21:48:00.000Z","key":1486763280000,"doc_count":6},{"key_as_string":"2017-02-10T21:49:00.000Z","key":1486763340000,"doc_count":12},{"key_as_string":"2017-02-10T21:50:00.000Z","key":1486763400000,"doc_count":10},{"key_as_string":"2017-02-10T21:51:00.000Z","key":1486763460000,"doc_count":11},{"key_as_string":"2017-02-10T21:52:00.000Z","key":1486763520000,"doc_count":11},{"key_as_string":"2017-02-10T21:53:00.000Z","key":1486763580000,"doc_count":12},{"key_as_string":"2017-02-10T21:54:00.000Z","key":1486763640000,"doc_count":15},{"key_as_string":"2017-02-10T21:55:00.000Z","key":1486763700000,"doc_count":12},{"key_as_string":"2017-02-10T21:56:00.000Z","key":1486763760000,"doc_count":9},{"key_as_string":"2017-02-10T21:57:00.000Z","key":1486763820000,"doc_count":13},{"key_as_string":"2017-02-10T21:58:00.000Z","key":1486763880000,"doc_count":8},{"key_as_string":"2017-02-10T21:59:00.000Z","key":1486763940000,"doc_count":9},{"key_as_string":"2017-02-10T22:00:00.000Z","key":1486764000000,"doc_count":20},{"key_as_string":"2017-02-10T22:01:00.000Z","key":1486764060000,"doc_count":11},{"key_as_string":"2017-02-10T22:02:00.000Z","key":1486764120000,"doc_count":14},{"key_as_string":"2017-02-10T22:03:00.000Z","key":1486764180000,"doc_count":8},{"key_as_string":"2017-02-10T22:04:00.000Z","key":1486764240000,"doc_count":13},{"key_as_string":"2017-02-10T22:05:00.000Z","key":1486764300000,"doc_count":8},{"key_as_string":"2017-02-10T22:06:00.000Z","key":1486764360000,"doc_count":14},{"key_as_string":"2017-02-10T22:07:00.000Z","key":1486764420000,"doc_count":9},{"key_as_string":"2017-02-10T22:08:00.000Z","key":1486764480000,"doc_count":8},{"key_as_string":"2017-02-10T22:09:00.000Z","key":1486764540000,"doc_count":10},{"key_as_string":"2017-02-10T22:10:00.000Z","key":1486764600000,"doc_count":18},{"key_as_string":"2017-02-10T22:11:00.000Z","key":1486764660000,"doc_count":8},{"key_as_string":"2017-02-10T22:12:00.000Z","key":1486764720000,"doc_count":9},{"key_as_string":"2017-02-10T22:13:00.000Z","key":1486764780000,"doc_count":13},{"key_as_string":"2017-02-10T22:14:00.000Z","key":1486764840000,"doc_count":9},{"key_as_string":"2017-02-10T22:15:00.000Z","key":1486764900000,"doc_count":12},{"key_as_string":"2017-02-10T22:16:00.000Z","key":1486764960000,"doc_count":7},{"key_as_string":"2017-02-10T22:17:00.000Z","key":1486765020000,"doc_count":11},{"key_as_string":"2017-02-10T22:18:00.000Z","key":1486765080000,"doc_count":9},{"key_as_string":"2017-02-10T22:19:00.000Z","key":1486765140000,"doc_count":6},{"key_as_string":"2017-02-10T22:20:00.000Z","key":1486765200000,"doc_count":10},{"key_as_string":"2017-02-10T22:21:00.000Z","key":1486765260000,"doc_count":9},{"key_as_string":"2017-02-10T22:22:00.000Z","key":1486765320000,"doc_count":12},{"key_as_string":"2017-02-10T22:23:00.000Z","key":1486765380000,"doc_count":13},{"key_as_string":"2017-02-10T22:24:00.000Z","key":1486765440000,"doc_count":12},{"key_as_string":"2017-02-10T22:25:00.000Z","key":1486765500000,"doc_count":9},{"key_as_string":"2017-02-10T22:26:00.000Z","key":1486765560000,"doc_count":17},{"key_as_string":"2017-02-10T22:27:00.000Z","key":1486765620000,"doc_count":8},{"key_as_string":"2017-02-10T22:28:00.000Z","key":1486765680000,"doc_count":15},{"key_as_string":"2017-02-10T22:29:00.000Z","key":1486765740000,"doc_count":10},{"key_as_string":"2017-02-10T22:30:00.000Z","key":1486765800000,"doc_count":7},{"key_as_string":"2017-02-10T22:31:00.000Z","key":1486765860000,"doc_count":15},{"key_as_string":"2017-02-10T22:32:00.000Z","key":1486765920000,"doc_count":8},{"key_as_string":"2017-02-10T22:33:00.000Z","key":1486765980000,"doc_count":10},{"key_as_string":"2017-02-10T22:34:00.000Z","key":1486766040000,"doc_count":13},{"key_as_string":"2017-02-10T22:35:00.000Z","key":1486766100000,"doc_count":12},{"key_as_string":"2017-02-10T22:36:00.000Z","key":1486766160000,"doc_count":10},{"key_as_string":"2017-02-10T22:37:00.000Z","key":1486766220000,"doc_count":13},{"key_as_string":"2017-02-10T22:38:00.000Z","key":1486766280000,"doc_count":8},{"key_as_string":"2017-02-10T22:39:00.000Z","key":1486766340000,"doc_count":14},{"key_as_string":"2017-02-10T22:40:00.000Z","key":1486766400000,"doc_count":14},{"key_as_string":"2017-02-10T22:41:00.000Z","key":1486766460000,"doc_count":13},{"key_as_string":"2017-02-10T22:42:00.000Z","key":1486766520000,"doc_count":9},{"key_as_string":"2017-02-10T22:43:00.000Z","key":1486766580000,"doc_count":11},{"key_as_string":"2017-02-10T22:44:00.000Z","key":1486766640000,"doc_count":12},{"key_as_string":"2017-02-10T22:45:00.000Z","key":1486766700000,"doc_count":11},{"key_as_string":"2017-02-10T22:46:00.000Z","key":1486766760000,"doc_count":5},{"key_as_string":"2017-02-10T22:47:00.000Z","key":1486766820000,"doc_count":12},{"key_as_string":"2017-02-10T22:48:00.000Z","key":1486766880000,"doc_count":8},{"key_as_string":"2017-02-10T22:49:00.000Z","key":1486766940000,"doc_count":13},{"key_as_string":"2017-02-10T22:50:00.000Z","key":1486767000000,"doc_count":9},{"key_as_string":"2017-02-10T22:51:00.000Z","key":1486767060000,"doc_count":14},{"key_as_string":"2017-02-10T22:52:00.000Z","key":1486767120000,"doc_count":8},{"key_as_string":"2017-02-10T22:53:00.000Z","key":1486767180000,"doc_count":14},{"key_as_string":"2017-02-10T22:54:00.000Z","key":1486767240000,"doc_count":5},{"key_as_string":"2017-02-10T22:55:00.000Z","key":1486767300000,"doc_count":15},{"key_as_string":"2017-02-10T22:56:00.000Z","key":1486767360000,"doc_count":10},{"key_as_string":"2017-02-10T22:57:00.000Z","key":1486767420000,"doc_count":17},{"key_as_string":"2017-02-10T22:58:00.000Z","key":1486767480000,"doc_count":10},{"key_as_string":"2017-02-10T22:59:00.000Z","key":1486767540000,"doc_count":14},{"key_as_string":"2017-02-10T23:00:00.000Z","key":1486767600000,"doc_count":5},{"key_as_string":"2017-02-10T23:01:00.000Z","key":1486767660000,"doc_count":12},{"key_as_string":"2017-02-10T23:02:00.000Z","key":1486767720000,"doc_count":12},{"key_as_string":"2017-02-10T23:03:00.000Z","key":1486767780000,"doc_count":9},{"key_as_string":"2017-02-10T23:04:00.000Z","key":1486767840000,"doc_count":7},{"key_as_string":"2017-02-10T23:05:00.000Z","key":1486767900000,"doc_count":12},{"key_as_string":"2017-02-10T23:06:00.000Z","key":1486767960000,"doc_count":7},{"key_as_string":"2017-02-10T23:07:00.000Z","key":1486768020000,"doc_count":14},{"key_as_string":"2017-02-10T23:08:00.000Z","key":1486768080000,"doc_count":10},{"key_as_string":"2017-02-10T23:09:00.000Z","key":1486768140000,"doc_count":9},{"key_as_string":"2017-02-10T23:10:00.000Z","key":1486768200000,"doc_count":15},{"key_as_string":"2017-02-10T23:11:00.000Z","key":1486768260000,"doc_count":13},{"key_as_string":"2017-02-10T23:12:00.000Z","key":1486768320000,"doc_count":10},{"key_as_string":"2017-02-10T23:13:00.000Z","key":1486768380000,"doc_count":8},{"key_as_string":"2017-02-10T23:14:00.000Z","key":1486768440000,"doc_count":10},{"key_as_string":"2017-02-10T23:15:00.000Z","key":1486768500000,"doc_count":13},{"key_as_string":"2017-02-10T23:16:00.000Z","key":1486768560000,"doc_count":9},{"key_as_string":"2017-02-10T23:17:00.000Z","key":1486768620000,"doc_count":8},{"key_as_string":"2017-02-10T23:18:00.000Z","key":1486768680000,"doc_count":7},{"key_as_string":"2017-02-10T23:19:00.000Z","key":1486768740000,"doc_count":12},{"key_as_string":"2017-02-10T23:20:00.000Z","key":1486768800000,"doc_count":9},{"key_as_string":"2017-02-10T23:21:00.000Z","key":1486768860000,"doc_count":11},{"key_as_string":"2017-02-10T23:22:00.000Z","key":1486768920000,"doc_count":8},{"key_as_string":"2017-02-10T23:23:00.000Z","key":1486768980000,"doc_count":9},{"key_as_string":"2017-02-10T23:24:00.000Z","key":1486769040000,"doc_count":14},{"key_as_string":"2017-02-10T23:25:00.000Z","key":1486769100000,"doc_count":9},{"key_as_string":"2017-02-10T23:26:00.000Z","key":1486769160000,"doc_count":8},{"key_as_string":"2017-02-10T23:27:00.000Z","key":1486769220000,"doc_count":7},{"key_as_string":"2017-02-10T23:28:00.000Z","key":1486769280000,"doc_count":17},{"key_as_string":"2017-02-10T23:29:00.000Z","key":1486769340000,"doc_count":5},{"key_as_string":"2017-02-10T23:30:00.000Z","key":1486769400000,"doc_count":16},{"key_as_string":"2017-02-10T23:31:00.000Z","key":1486769460000,"doc_count":14},{"key_as_string":"2017-02-10T23:32:00.000Z","key":1486769520000,"doc_count":6},{"key_as_string":"2017-02-10T23:33:00.000Z","key":1486769580000,"doc_count":8},{"key_as_string":"2017-02-10T23:34:00.000Z","key":1486769640000,"doc_count":15},{"key_as_string":"2017-02-10T23:35:00.000Z","key":1486769700000,"doc_count":9},{"key_as_string":"2017-02-10T23:36:00.000Z","key":1486769760000,"doc_count":11},{"key_as_string":"2017-02-10T23:37:00.000Z","key":1486769820000,"doc_count":13},{"key_as_string":"2017-02-10T23:38:00.000Z","key":1486769880000,"doc_count":12},{"key_as_string":"2017-02-10T23:39:00.000Z","key":1486769940000,"doc_count":12},{"key_as_string":"2017-02-10T23:40:00.000Z","key":1486770000000,"doc_count":9},{"key_as_string":"2017-02-10T23:41:00.000Z","key":1486770060000,"doc_count":11},{"key_as_string":"2017-02-10T23:42:00.000Z","key":1486770120000,"doc_count":10},{"key_as_string":"2017-02-10T23:43:00.000Z","key":1486770180000,"doc_count":16},{"key_as_string":"2017-02-10T23:44:00.000Z","key":1486770240000,"doc_count":8},{"key_as_string":"2017-02-10T23:45:00.000Z","key":1486770300000,"doc_count":8},{"key_as_string":"2017-02-10T23:46:00.000Z","key":1486770360000,"doc_count":8},{"key_as_string":"2017-02-10T23:47:00.000Z","key":1486770420000,"doc_count":11},{"key_as_string":"2017-02-10T23:48:00.000Z","key":1486770480000,"doc_count":10},{"key_as_string":"2017-02-10T23:49:00.000Z","key":1486770540000,"doc_count":10},{"key_as_string":"2017-02-10T23:50:00.000Z","key":1486770600000,"doc_count":13},{"key_as_string":"2017-02-10T23:51:00.000Z","key":1486770660000,"doc_count":6},{"key_as_string":"2017-02-10T23:52:00.000Z","key":1486770720000,"doc_count":16},{"key_as_string":"2017-02-10T23:53:00.000Z","key":1486770780000,"doc_count":10},{"key_as_string":"2017-02-10T23:54:00.000Z","key":1486770840000,"doc_count":11},{"key_as_string":"2017-02-10T23:55:00.000Z","key":1486770900000,"doc_count":14},{"key_as_string":"2017-02-10T23:56:00.000Z","key":1486770960000,"doc_count":9},{"key_as_string":"2017-02-10T23:57:00.000Z","key":1486771020000,"doc_count":9},{"key_as_string":"2017-02-10T23:58:00.000Z","key":1486771080000,"doc_count":11},{"key_as_string":"2017-02-10T23:59:00.000Z","key":1486771140000,"doc_count":6},{"key_as_string":"2017-02-11T00:00:00.000Z","key":1486771200000,"doc_count":12},{"key_as_string":"2017-02-11T00:01:00.000Z","key":1486771260000,"doc_count":14},{"key_as_string":"2017-02-11T00:02:00.000Z","key":1486771320000,"doc_count":9},{"key_as_string":"2017-02-11T00:03:00.000Z","key":1486771380000,"doc_count":3},{"key_as_string":"2017-02-11T00:04:00.000Z","key":1486771440000,"doc_count":15},{"key_as_string":"2017-02-11T00:05:00.000Z","key":1486771500000,"doc_count":10},{"key_as_string":"2017-02-11T00:06:00.000Z","key":1486771560000,"doc_count":11},{"key_as_string":"2017-02-11T00:07:00.000Z","key":1486771620000,"doc_count":6},{"key_as_string":"2017-02-11T00:08:00.000Z","key":1486771680000,"doc_count":11},{"key_as_string":"2017-02-11T00:09:00.000Z","key":1486771740000,"doc_count":10},{"key_as_string":"2017-02-11T00:10:00.000Z","key":1486771800000,"doc_count":7},{"key_as_string":"2017-02-11T00:11:00.000Z","key":1486771860000,"doc_count":13},{"key_as_string":"2017-02-11T00:12:00.000Z","key":1486771920000,"doc_count":9},{"key_as_string":"2017-02-11T00:13:00.000Z","key":1486771980000,"doc_count":10},{"key_as_string":"2017-02-11T00:14:00.000Z","key":1486772040000,"doc_count":17},{"key_as_string":"2017-02-11T00:15:00.000Z","key":1486772100000,"doc_count":7},{"key_as_string":"2017-02-11T00:16:00.000Z","key":1486772160000,"doc_count":12},{"key_as_string":"2017-02-11T00:17:00.000Z","key":1486772220000,"doc_count":13},{"key_as_string":"2017-02-11T00:18:00.000Z","key":1486772280000,"doc_count":4},{"key_as_string":"2017-02-11T00:19:00.000Z","key":1486772340000,"doc_count":15},{"key_as_string":"2017-02-11T00:20:00.000Z","key":1486772400000,"doc_count":13},{"key_as_string":"2017-02-11T00:21:00.000Z","key":1486772460000,"doc_count":8},{"key_as_string":"2017-02-11T00:22:00.000Z","key":1486772520000,"doc_count":8},{"key_as_string":"2017-02-11T00:23:00.000Z","key":1486772580000,"doc_count":12},{"key_as_string":"2017-02-11T00:24:00.000Z","key":1486772640000,"doc_count":9},{"key_as_string":"2017-02-11T00:25:00.000Z","key":1486772700000,"doc_count":8},{"key_as_string":"2017-02-11T00:26:00.000Z","key":1486772760000,"doc_count":13},{"key_as_string":"2017-02-11T00:27:00.000Z","key":1486772820000,"doc_count":10},{"key_as_string":"2017-02-11T00:28:00.000Z","key":1486772880000,"doc_count":11},{"key_as_string":"2017-02-11T00:29:00.000Z","key":1486772940000,"doc_count":7},{"key_as_string":"2017-02-11T00:30:00.000Z","key":1486773000000,"doc_count":14},{"key_as_string":"2017-02-11T00:31:00.000Z","key":1486773060000,"doc_count":8},{"key_as_string":"2017-02-11T00:32:00.000Z","key":1486773120000,"doc_count":9},{"key_as_string":"2017-02-11T00:33:00.000Z","key":1486773180000,"doc_count":14},{"key_as_string":"2017-02-11T00:34:00.000Z","key":1486773240000,"doc_count":7},{"key_as_string":"2017-02-11T00:35:00.000Z","key":1486773300000,"doc_count":10},{"key_as_string":"2017-02-11T00:36:00.000Z","key":1486773360000,"doc_count":11},{"key_as_string":"2017-02-11T00:37:00.000Z","key":1486773420000,"doc_count":15},{"key_as_string":"2017-02-11T00:38:00.000Z","key":1486773480000,"doc_count":9},{"key_as_string":"2017-02-11T00:39:00.000Z","key":1486773540000,"doc_count":11},{"key_as_string":"2017-02-11T00:40:00.000Z","key":1486773600000,"doc_count":8},{"key_as_string":"2017-02-11T00:41:00.000Z","key":1486773660000,"doc_count":9},{"key_as_string":"2017-02-11T00:42:00.000Z","key":1486773720000,"doc_count":10},{"key_as_string":"2017-02-11T00:43:00.000Z","key":1486773780000,"doc_count":14},{"key_as_string":"2017-02-11T00:44:00.000Z","key":1486773840000,"doc_count":7},{"key_as_string":"2017-02-11T00:45:00.000Z","key":1486773900000,"doc_count":12},{"key_as_string":"2017-02-11T00:46:00.000Z","key":1486773960000,"doc_count":12},{"key_as_string":"2017-02-11T00:47:00.000Z","key":1486774020000,"doc_count":9},{"key_as_string":"2017-02-11T00:48:00.000Z","key":1486774080000,"doc_count":11},{"key_as_string":"2017-02-11T00:49:00.000Z","key":1486774140000,"doc_count":6},{"key_as_string":"2017-02-11T00:50:00.000Z","key":1486774200000,"doc_count":11},{"key_as_string":"2017-02-11T00:51:00.000Z","key":1486774260000,"doc_count":14},{"key_as_string":"2017-02-11T00:52:00.000Z","key":1486774320000,"doc_count":6},{"key_as_string":"2017-02-11T00:53:00.000Z","key":1486774380000,"doc_count":14},{"key_as_string":"2017-02-11T00:54:00.000Z","key":1486774440000,"doc_count":9},{"key_as_string":"2017-02-11T00:55:00.000Z","key":1486774500000,"doc_count":7},{"key_as_string":"2017-02-11T00:56:00.000Z","key":1486774560000,"doc_count":9},{"key_as_string":"2017-02-11T00:57:00.000Z","key":1486774620000,"doc_count":11},{"key_as_string":"2017-02-11T00:58:00.000Z","key":1486774680000,"doc_count":11},{"key_as_string":"2017-02-11T00:59:00.000Z","key":1486774740000,"doc_count":9},{"key_as_string":"2017-02-11T01:00:00.000Z","key":1486774800000,"doc_count":9},{"key_as_string":"2017-02-11T01:01:00.000Z","key":1486774860000,"doc_count":9},{"key_as_string":"2017-02-11T01:02:00.000Z","key":1486774920000,"doc_count":9},{"key_as_string":"2017-02-11T01:03:00.000Z","key":1486774980000,"doc_count":13},{"key_as_string":"2017-02-11T01:04:00.000Z","key":1486775040000,"doc_count":8},{"key_as_string":"2017-02-11T01:05:00.000Z","key":1486775100000,"doc_count":14},{"key_as_string":"2017-02-11T01:06:00.000Z","key":1486775160000,"doc_count":7},{"key_as_string":"2017-02-11T01:07:00.000Z","key":1486775220000,"doc_count":13},{"key_as_string":"2017-02-11T01:08:00.000Z","key":1486775280000,"doc_count":9},{"key_as_string":"2017-02-11T01:09:00.000Z","key":1486775340000,"doc_count":11},{"key_as_string":"2017-02-11T01:10:00.000Z","key":1486775400000,"doc_count":11},{"key_as_string":"2017-02-11T01:11:00.000Z","key":1486775460000,"doc_count":15},{"key_as_string":"2017-02-11T01:12:00.000Z","key":1486775520000,"doc_count":8},{"key_as_string":"2017-02-11T01:13:00.000Z","key":1486775580000,"doc_count":7},{"key_as_string":"2017-02-11T01:14:00.000Z","key":1486775640000,"doc_count":16},{"key_as_string":"2017-02-11T01:15:00.000Z","key":1486775700000,"doc_count":6},{"key_as_string":"2017-02-11T01:16:00.000Z","key":1486775760000,"doc_count":13},{"key_as_string":"2017-02-11T01:17:00.000Z","key":1486775820000,"doc_count":9},{"key_as_string":"2017-02-11T01:18:00.000Z","key":1486775880000,"doc_count":7},{"key_as_string":"2017-02-11T01:19:00.000Z","key":1486775940000,"doc_count":12},{"key_as_string":"2017-02-11T01:20:00.000Z","key":1486776000000,"doc_count":8},{"key_as_string":"2017-02-11T01:21:00.000Z","key":1486776060000,"doc_count":12},{"key_as_string":"2017-02-11T01:22:00.000Z","key":1486776120000,"doc_count":16},{"key_as_string":"2017-02-11T01:23:00.000Z","key":1486776180000,"doc_count":9},{"key_as_string":"2017-02-11T01:24:00.000Z","key":1486776240000,"doc_count":6},{"key_as_string":"2017-02-11T01:25:00.000Z","key":1486776300000,"doc_count":13},{"key_as_string":"2017-02-11T01:26:00.000Z","key":1486776360000,"doc_count":12},{"key_as_string":"2017-02-11T01:27:00.000Z","key":1486776420000,"doc_count":10},{"key_as_string":"2017-02-11T01:28:00.000Z","key":1486776480000,"doc_count":6},{"key_as_string":"2017-02-11T01:29:00.000Z","key":1486776540000,"doc_count":13},{"key_as_string":"2017-02-11T01:30:00.000Z","key":1486776600000,"doc_count":8},{"key_as_string":"2017-02-11T01:31:00.000Z","key":1486776660000,"doc_count":16},{"key_as_string":"2017-02-11T01:32:00.000Z","key":1486776720000,"doc_count":8},{"key_as_string":"2017-02-11T01:33:00.000Z","key":1486776780000,"doc_count":4},{"key_as_string":"2017-02-11T01:34:00.000Z","key":1486776840000,"doc_count":9},{"key_as_string":"2017-02-11T01:35:00.000Z","key":1486776900000,"doc_count":11},{"key_as_string":"2017-02-11T01:36:00.000Z","key":1486776960000,"doc_count":10},{"key_as_string":"2017-02-11T01:37:00.000Z","key":1486777020000,"doc_count":17},{"key_as_string":"2017-02-11T01:38:00.000Z","key":1486777080000,"doc_count":11},{"key_as_string":"2017-02-11T01:39:00.000Z","key":1486777140000,"doc_count":10},{"key_as_string":"2017-02-11T01:40:00.000Z","key":1486777200000,"doc_count":12},{"key_as_string":"2017-02-11T01:41:00.000Z","key":1486777260000,"doc_count":11},{"key_as_string":"2017-02-11T01:42:00.000Z","key":1486777320000,"doc_count":11},{"key_as_string":"2017-02-11T01:43:00.000Z","key":1486777380000,"doc_count":6},{"key_as_string":"2017-02-11T01:44:00.000Z","key":1486777440000,"doc_count":13},{"key_as_string":"2017-02-11T01:45:00.000Z","key":1486777500000,"doc_count":12},{"key_as_string":"2017-02-11T01:46:00.000Z","key":1486777560000,"doc_count":12},{"key_as_string":"2017-02-11T01:47:00.000Z","key":1486777620000,"doc_count":7},{"key_as_string":"2017-02-11T01:48:00.000Z","key":1486777680000,"doc_count":12},{"key_as_string":"2017-02-11T01:49:00.000Z","key":1486777740000,"doc_count":11},{"key_as_string":"2017-02-11T01:50:00.000Z","key":1486777800000,"doc_count":13},{"key_as_string":"2017-02-11T01:51:00.000Z","key":1486777860000,"doc_count":13},{"key_as_string":"2017-02-11T01:52:00.000Z","key":1486777920000,"doc_count":11},{"key_as_string":"2017-02-11T01:53:00.000Z","key":1486777980000,"doc_count":11},{"key_as_string":"2017-02-11T01:54:00.000Z","key":1486778040000,"doc_count":7},{"key_as_string":"2017-02-11T01:55:00.000Z","key":1486778100000,"doc_count":13},{"key_as_string":"2017-02-11T01:56:00.000Z","key":1486778160000,"doc_count":12},{"key_as_string":"2017-02-11T01:57:00.000Z","key":1486778220000,"doc_count":12},{"key_as_string":"2017-02-11T01:58:00.000Z","key":1486778280000,"doc_count":10},{"key_as_string":"2017-02-11T01:59:00.000Z","key":1486778340000,"doc_count":13},{"key_as_string":"2017-02-11T02:00:00.000Z","key":1486778400000,"doc_count":10},{"key_as_string":"2017-02-11T02:01:00.000Z","key":1486778460000,"doc_count":8},{"key_as_string":"2017-02-11T02:02:00.000Z","key":1486778520000,"doc_count":8},{"key_as_string":"2017-02-11T02:03:00.000Z","key":1486778580000,"doc_count":10},{"key_as_string":"2017-02-11T02:04:00.000Z","key":1486778640000,"doc_count":10},{"key_as_string":"2017-02-11T02:05:00.000Z","key":1486778700000,"doc_count":6},{"key_as_string":"2017-02-11T02:06:00.000Z","key":1486778760000,"doc_count":12},{"key_as_string":"2017-02-11T02:07:00.000Z","key":1486778820000,"doc_count":11},{"key_as_string":"2017-02-11T02:08:00.000Z","key":1486778880000,"doc_count":7},{"key_as_string":"2017-02-11T02:09:00.000Z","key":1486778940000,"doc_count":13},{"key_as_string":"2017-02-11T02:10:00.000Z","key":1486779000000,"doc_count":9},{"key_as_string":"2017-02-11T02:11:00.000Z","key":1486779060000,"doc_count":14},{"key_as_string":"2017-02-11T02:12:00.000Z","key":1486779120000,"doc_count":9},{"key_as_string":"2017-02-11T02:13:00.000Z","key":1486779180000,"doc_count":10},{"key_as_string":"2017-02-11T02:14:00.000Z","key":1486779240000,"doc_count":12},{"key_as_string":"2017-02-11T02:15:00.000Z","key":1486779300000,"doc_count":5},{"key_as_string":"2017-02-11T02:16:00.000Z","key":1486779360000,"doc_count":9},{"key_as_string":"2017-02-11T02:17:00.000Z","key":1486779420000,"doc_count":14},{"key_as_string":"2017-02-11T02:18:00.000Z","key":1486779480000,"doc_count":5},{"key_as_string":"2017-02-11T02:19:00.000Z","key":1486779540000,"doc_count":18},{"key_as_string":"2017-02-11T02:20:00.000Z","key":1486779600000,"doc_count":9},{"key_as_string":"2017-02-11T02:21:00.000Z","key":1486779660000,"doc_count":7},{"key_as_string":"2017-02-11T02:22:00.000Z","key":1486779720000,"doc_count":14},{"key_as_string":"2017-02-11T02:23:00.000Z","key":1486779780000,"doc_count":8},{"key_as_string":"2017-02-11T02:24:00.000Z","key":1486779840000,"doc_count":9},{"key_as_string":"2017-02-11T02:25:00.000Z","key":1486779900000,"doc_count":10},{"key_as_string":"2017-02-11T02:26:00.000Z","key":1486779960000,"doc_count":15},{"key_as_string":"2017-02-11T02:27:00.000Z","key":1486780020000,"doc_count":12},{"key_as_string":"2017-02-11T02:28:00.000Z","key":1486780080000,"doc_count":8},{"key_as_string":"2017-02-11T02:29:00.000Z","key":1486780140000,"doc_count":7},{"key_as_string":"2017-02-11T02:30:00.000Z","key":1486780200000,"doc_count":13},{"key_as_string":"2017-02-11T02:31:00.000Z","key":1486780260000,"doc_count":9},{"key_as_string":"2017-02-11T02:32:00.000Z","key":1486780320000,"doc_count":11},{"key_as_string":"2017-02-11T02:33:00.000Z","key":1486780380000,"doc_count":9},{"key_as_string":"2017-02-11T02:34:00.000Z","key":1486780440000,"doc_count":12},{"key_as_string":"2017-02-11T02:35:00.000Z","key":1486780500000,"doc_count":11},{"key_as_string":"2017-02-11T02:36:00.000Z","key":1486780560000,"doc_count":4},{"key_as_string":"2017-02-11T02:37:00.000Z","key":1486780620000,"doc_count":12},{"key_as_string":"2017-02-11T02:38:00.000Z","key":1486780680000,"doc_count":6},{"key_as_string":"2017-02-11T02:39:00.000Z","key":1486780740000,"doc_count":12},{"key_as_string":"2017-02-11T02:40:00.000Z","key":1486780800000,"doc_count":12},{"key_as_string":"2017-02-11T02:41:00.000Z","key":1486780860000,"doc_count":12},{"key_as_string":"2017-02-11T02:42:00.000Z","key":1486780920000,"doc_count":13},{"key_as_string":"2017-02-11T02:43:00.000Z","key":1486780980000,"doc_count":12},{"key_as_string":"2017-02-11T02:44:00.000Z","key":1486781040000,"doc_count":13},{"key_as_string":"2017-02-11T02:45:00.000Z","key":1486781100000,"doc_count":11},{"key_as_string":"2017-02-11T02:46:00.000Z","key":1486781160000,"doc_count":5},{"key_as_string":"2017-02-11T02:47:00.000Z","key":1486781220000,"doc_count":14},{"key_as_string":"2017-02-11T02:48:00.000Z","key":1486781280000,"doc_count":8},{"key_as_string":"2017-02-11T02:49:00.000Z","key":1486781340000,"doc_count":14},{"key_as_string":"2017-02-11T02:50:00.000Z","key":1486781400000,"doc_count":16},{"key_as_string":"2017-02-11T02:51:00.000Z","key":1486781460000,"doc_count":7},{"key_as_string":"2017-02-11T02:52:00.000Z","key":1486781520000,"doc_count":7},{"key_as_string":"2017-02-11T02:53:00.000Z","key":1486781580000,"doc_count":15},{"key_as_string":"2017-02-11T02:54:00.000Z","key":1486781640000,"doc_count":13},{"key_as_string":"2017-02-11T02:55:00.000Z","key":1486781700000,"doc_count":5},{"key_as_string":"2017-02-11T02:56:00.000Z","key":1486781760000,"doc_count":8},{"key_as_string":"2017-02-11T02:57:00.000Z","key":1486781820000,"doc_count":8},{"key_as_string":"2017-02-11T02:58:00.000Z","key":1486781880000,"doc_count":16},{"key_as_string":"2017-02-11T02:59:00.000Z","key":1486781940000,"doc_count":9},{"key_as_string":"2017-02-11T03:00:00.000Z","key":1486782000000,"doc_count":9},{"key_as_string":"2017-02-11T03:01:00.000Z","key":1486782060000,"doc_count":13},{"key_as_string":"2017-02-11T03:02:00.000Z","key":1486782120000,"doc_count":14},{"key_as_string":"2017-02-11T03:03:00.000Z","key":1486782180000,"doc_count":10},{"key_as_string":"2017-02-11T03:04:00.000Z","key":1486782240000,"doc_count":8},{"key_as_string":"2017-02-11T03:05:00.000Z","key":1486782300000,"doc_count":14},{"key_as_string":"2017-02-11T03:06:00.000Z","key":1486782360000,"doc_count":11},{"key_as_string":"2017-02-11T03:07:00.000Z","key":1486782420000,"doc_count":11},{"key_as_string":"2017-02-11T03:08:00.000Z","key":1486782480000,"doc_count":8},{"key_as_string":"2017-02-11T03:09:00.000Z","key":1486782540000,"doc_count":13},{"key_as_string":"2017-02-11T03:10:00.000Z","key":1486782600000,"doc_count":12},{"key_as_string":"2017-02-11T03:11:00.000Z","key":1486782660000,"doc_count":9},{"key_as_string":"2017-02-11T03:12:00.000Z","key":1486782720000,"doc_count":9},{"key_as_string":"2017-02-11T03:13:00.000Z","key":1486782780000,"doc_count":9},{"key_as_string":"2017-02-11T03:14:00.000Z","key":1486782840000,"doc_count":11},{"key_as_string":"2017-02-11T03:15:00.000Z","key":1486782900000,"doc_count":9},{"key_as_string":"2017-02-11T03:16:00.000Z","key":1486782960000,"doc_count":20},{"key_as_string":"2017-02-11T03:17:00.000Z","key":1486783020000,"doc_count":7},{"key_as_string":"2017-02-11T03:18:00.000Z","key":1486783080000,"doc_count":11},{"key_as_string":"2017-02-11T03:19:00.000Z","key":1486783140000,"doc_count":10},{"key_as_string":"2017-02-11T03:20:00.000Z","key":1486783200000,"doc_count":8},{"key_as_string":"2017-02-11T03:21:00.000Z","key":1486783260000,"doc_count":16},{"key_as_string":"2017-02-11T03:22:00.000Z","key":1486783320000,"doc_count":5},{"key_as_string":"2017-02-11T03:23:00.000Z","key":1486783380000,"doc_count":12},{"key_as_string":"2017-02-11T03:24:00.000Z","key":1486783440000,"doc_count":8},{"key_as_string":"2017-02-11T03:25:00.000Z","key":1486783500000,"doc_count":17},{"key_as_string":"2017-02-11T03:26:00.000Z","key":1486783560000,"doc_count":8},{"key_as_string":"2017-02-11T03:27:00.000Z","key":1486783620000,"doc_count":14},{"key_as_string":"2017-02-11T03:28:00.000Z","key":1486783680000,"doc_count":7},{"key_as_string":"2017-02-11T03:29:00.000Z","key":1486783740000,"doc_count":12},{"key_as_string":"2017-02-11T03:30:00.000Z","key":1486783800000,"doc_count":13},{"key_as_string":"2017-02-11T03:31:00.000Z","key":1486783860000,"doc_count":9},{"key_as_string":"2017-02-11T03:32:00.000Z","key":1486783920000,"doc_count":5},{"key_as_string":"2017-02-11T03:33:00.000Z","key":1486783980000,"doc_count":10},{"key_as_string":"2017-02-11T03:34:00.000Z","key":1486784040000,"doc_count":14},{"key_as_string":"2017-02-11T03:35:00.000Z","key":1486784100000,"doc_count":13},{"key_as_string":"2017-02-11T03:36:00.000Z","key":1486784160000,"doc_count":9},{"key_as_string":"2017-02-11T03:37:00.000Z","key":1486784220000,"doc_count":10},{"key_as_string":"2017-02-11T03:38:00.000Z","key":1486784280000,"doc_count":10},{"key_as_string":"2017-02-11T03:39:00.000Z","key":1486784340000,"doc_count":12},{"key_as_string":"2017-02-11T03:40:00.000Z","key":1486784400000,"doc_count":11},{"key_as_string":"2017-02-11T03:41:00.000Z","key":1486784460000,"doc_count":11},{"key_as_string":"2017-02-11T03:42:00.000Z","key":1486784520000,"doc_count":7},{"key_as_string":"2017-02-11T03:43:00.000Z","key":1486784580000,"doc_count":18},{"key_as_string":"2017-02-11T03:44:00.000Z","key":1486784640000,"doc_count":6},{"key_as_string":"2017-02-11T03:45:00.000Z","key":1486784700000,"doc_count":12},{"key_as_string":"2017-02-11T03:46:00.000Z","key":1486784760000,"doc_count":13},{"key_as_string":"2017-02-11T03:47:00.000Z","key":1486784820000,"doc_count":13},{"key_as_string":"2017-02-11T03:48:00.000Z","key":1486784880000,"doc_count":15},{"key_as_string":"2017-02-11T03:49:00.000Z","key":1486784940000,"doc_count":11},{"key_as_string":"2017-02-11T03:50:00.000Z","key":1486785000000,"doc_count":9},{"key_as_string":"2017-02-11T03:51:00.000Z","key":1486785060000,"doc_count":14},{"key_as_string":"2017-02-11T03:52:00.000Z","key":1486785120000,"doc_count":7},{"key_as_string":"2017-02-11T03:53:00.000Z","key":1486785180000,"doc_count":11},{"key_as_string":"2017-02-11T03:54:00.000Z","key":1486785240000,"doc_count":8},{"key_as_string":"2017-02-11T03:55:00.000Z","key":1486785300000,"doc_count":7},{"key_as_string":"2017-02-11T03:56:00.000Z","key":1486785360000,"doc_count":10},{"key_as_string":"2017-02-11T03:57:00.000Z","key":1486785420000,"doc_count":13},{"key_as_string":"2017-02-11T03:58:00.000Z","key":1486785480000,"doc_count":11},{"key_as_string":"2017-02-11T03:59:00.000Z","key":1486785540000,"doc_count":8},{"key_as_string":"2017-02-11T04:00:00.000Z","key":1486785600000,"doc_count":11},{"key_as_string":"2017-02-11T04:01:00.000Z","key":1486785660000,"doc_count":9},{"key_as_string":"2017-02-11T04:02:00.000Z","key":1486785720000,"doc_count":13},{"key_as_string":"2017-02-11T04:03:00.000Z","key":1486785780000,"doc_count":14},{"key_as_string":"2017-02-11T04:04:00.000Z","key":1486785840000,"doc_count":13},{"key_as_string":"2017-02-11T04:05:00.000Z","key":1486785900000,"doc_count":9},{"key_as_string":"2017-02-11T04:06:00.000Z","key":1486785960000,"doc_count":8},{"key_as_string":"2017-02-11T04:07:00.000Z","key":1486786020000,"doc_count":8},{"key_as_string":"2017-02-11T04:08:00.000Z","key":1486786080000,"doc_count":14},{"key_as_string":"2017-02-11T04:09:00.000Z","key":1486786140000,"doc_count":10},{"key_as_string":"2017-02-11T04:10:00.000Z","key":1486786200000,"doc_count":12},{"key_as_string":"2017-02-11T04:11:00.000Z","key":1486786260000,"doc_count":9},{"key_as_string":"2017-02-11T04:12:00.000Z","key":1486786320000,"doc_count":11},{"key_as_string":"2017-02-11T04:13:00.000Z","key":1486786380000,"doc_count":19},{"key_as_string":"2017-02-11T04:14:00.000Z","key":1486786440000,"doc_count":9},{"key_as_string":"2017-02-11T04:15:00.000Z","key":1486786500000,"doc_count":9},{"key_as_string":"2017-02-11T04:16:00.000Z","key":1486786560000,"doc_count":15},{"key_as_string":"2017-02-11T04:17:00.000Z","key":1486786620000,"doc_count":17},{"key_as_string":"2017-02-11T04:18:00.000Z","key":1486786680000,"doc_count":15},{"key_as_string":"2017-02-11T04:19:00.000Z","key":1486786740000,"doc_count":8},{"key_as_string":"2017-02-11T04:20:00.000Z","key":1486786800000,"doc_count":11},{"key_as_string":"2017-02-11T04:21:00.000Z","key":1486786860000,"doc_count":13},{"key_as_string":"2017-02-11T04:22:00.000Z","key":1486786920000,"doc_count":11},{"key_as_string":"2017-02-11T04:23:00.000Z","key":1486786980000,"doc_count":7},{"key_as_string":"2017-02-11T04:24:00.000Z","key":1486787040000,"doc_count":14},{"key_as_string":"2017-02-11T04:25:00.000Z","key":1486787100000,"doc_count":11},{"key_as_string":"2017-02-11T04:26:00.000Z","key":1486787160000,"doc_count":9},{"key_as_string":"2017-02-11T04:27:00.000Z","key":1486787220000,"doc_count":6},{"key_as_string":"2017-02-11T04:28:00.000Z","key":1486787280000,"doc_count":17},{"key_as_string":"2017-02-11T04:29:00.000Z","key":1486787340000,"doc_count":9},{"key_as_string":"2017-02-11T04:30:00.000Z","key":1486787400000,"doc_count":9},{"key_as_string":"2017-02-11T04:31:00.000Z","key":1486787460000,"doc_count":8},{"key_as_string":"2017-02-11T04:32:00.000Z","key":1486787520000,"doc_count":12},{"key_as_string":"2017-02-11T04:33:00.000Z","key":1486787580000,"doc_count":11},{"key_as_string":"2017-02-11T04:34:00.000Z","key":1486787640000,"doc_count":10},{"key_as_string":"2017-02-11T04:35:00.000Z","key":1486787700000,"doc_count":11},{"key_as_string":"2017-02-11T04:36:00.000Z","key":1486787760000,"doc_count":11},{"key_as_string":"2017-02-11T04:37:00.000Z","key":1486787820000,"doc_count":15},{"key_as_string":"2017-02-11T04:38:00.000Z","key":1486787880000,"doc_count":11},{"key_as_string":"2017-02-11T04:39:00.000Z","key":1486787940000,"doc_count":10},{"key_as_string":"2017-02-11T04:40:00.000Z","key":1486788000000,"doc_count":10},{"key_as_string":"2017-02-11T04:41:00.000Z","key":1486788060000,"doc_count":10},{"key_as_string":"2017-02-11T04:42:00.000Z","key":1486788120000,"doc_count":8},{"key_as_string":"2017-02-11T04:43:00.000Z","key":1486788180000,"doc_count":9},{"key_as_string":"2017-02-11T04:44:00.000Z","key":1486788240000,"doc_count":8},{"key_as_string":"2017-02-11T04:45:00.000Z","key":1486788300000,"doc_count":19},{"key_as_string":"2017-02-11T04:46:00.000Z","key":1486788360000,"doc_count":11},{"key_as_string":"2017-02-11T04:47:00.000Z","key":1486788420000,"doc_count":11},{"key_as_string":"2017-02-11T04:48:00.000Z","key":1486788480000,"doc_count":10},{"key_as_string":"2017-02-11T04:49:00.000Z","key":1486788540000,"doc_count":9},{"key_as_string":"2017-02-11T04:50:00.000Z","key":1486788600000,"doc_count":9},{"key_as_string":"2017-02-11T04:51:00.000Z","key":1486788660000,"doc_count":15},{"key_as_string":"2017-02-11T04:52:00.000Z","key":1486788720000,"doc_count":11},{"key_as_string":"2017-02-11T04:53:00.000Z","key":1486788780000,"doc_count":11},{"key_as_string":"2017-02-11T04:54:00.000Z","key":1486788840000,"doc_count":12},{"key_as_string":"2017-02-11T04:55:00.000Z","key":1486788900000,"doc_count":12},{"key_as_string":"2017-02-11T04:56:00.000Z","key":1486788960000,"doc_count":9},{"key_as_string":"2017-02-11T04:57:00.000Z","key":1486789020000,"doc_count":16},{"key_as_string":"2017-02-11T04:58:00.000Z","key":1486789080000,"doc_count":11},{"key_as_string":"2017-02-11T04:59:00.000Z","key":1486789140000,"doc_count":10},{"key_as_string":"2017-02-11T05:00:00.000Z","key":1486789200000,"doc_count":9},{"key_as_string":"2017-02-11T05:01:00.000Z","key":1486789260000,"doc_count":14},{"key_as_string":"2017-02-11T05:02:00.000Z","key":1486789320000,"doc_count":10},{"key_as_string":"2017-02-11T05:03:00.000Z","key":1486789380000,"doc_count":11},{"key_as_string":"2017-02-11T05:04:00.000Z","key":1486789440000,"doc_count":11},{"key_as_string":"2017-02-11T05:05:00.000Z","key":1486789500000,"doc_count":6},{"key_as_string":"2017-02-11T05:06:00.000Z","key":1486789560000,"doc_count":19},{"key_as_string":"2017-02-11T05:07:00.000Z","key":1486789620000,"doc_count":11},{"key_as_string":"2017-02-11T05:08:00.000Z","key":1486789680000,"doc_count":15},{"key_as_string":"2017-02-11T05:09:00.000Z","key":1486789740000,"doc_count":10},{"key_as_string":"2017-02-11T05:10:00.000Z","key":1486789800000,"doc_count":13},{"key_as_string":"2017-02-11T05:11:00.000Z","key":1486789860000,"doc_count":12},{"key_as_string":"2017-02-11T05:12:00.000Z","key":1486789920000,"doc_count":14},{"key_as_string":"2017-02-11T05:13:00.000Z","key":1486789980000,"doc_count":12},{"key_as_string":"2017-02-11T05:14:00.000Z","key":1486790040000,"doc_count":13},{"key_as_string":"2017-02-11T05:15:00.000Z","key":1486790100000,"doc_count":7},{"key_as_string":"2017-02-11T05:16:00.000Z","key":1486790160000,"doc_count":10},{"key_as_string":"2017-02-11T05:17:00.000Z","key":1486790220000,"doc_count":12},{"key_as_string":"2017-02-11T05:18:00.000Z","key":1486790280000,"doc_count":15},{"key_as_string":"2017-02-11T05:19:00.000Z","key":1486790340000,"doc_count":10},{"key_as_string":"2017-02-11T05:20:00.000Z","key":1486790400000,"doc_count":9},{"key_as_string":"2017-02-11T05:21:00.000Z","key":1486790460000,"doc_count":10},{"key_as_string":"2017-02-11T05:22:00.000Z","key":1486790520000,"doc_count":15},{"key_as_string":"2017-02-11T05:23:00.000Z","key":1486790580000,"doc_count":9},{"key_as_string":"2017-02-11T05:24:00.000Z","key":1486790640000,"doc_count":13},{"key_as_string":"2017-02-11T05:25:00.000Z","key":1486790700000,"doc_count":12},{"key_as_string":"2017-02-11T05:26:00.000Z","key":1486790760000,"doc_count":9},{"key_as_string":"2017-02-11T05:27:00.000Z","key":1486790820000,"doc_count":15},{"key_as_string":"2017-02-11T05:28:00.000Z","key":1486790880000,"doc_count":11},{"key_as_string":"2017-02-11T05:29:00.000Z","key":1486790940000,"doc_count":14},{"key_as_string":"2017-02-11T05:30:00.000Z","key":1486791000000,"doc_count":14},{"key_as_string":"2017-02-11T05:31:00.000Z","key":1486791060000,"doc_count":15},{"key_as_string":"2017-02-11T05:32:00.000Z","key":1486791120000,"doc_count":7},{"key_as_string":"2017-02-11T05:33:00.000Z","key":1486791180000,"doc_count":7},{"key_as_string":"2017-02-11T05:34:00.000Z","key":1486791240000,"doc_count":9},{"key_as_string":"2017-02-11T05:35:00.000Z","key":1486791300000,"doc_count":13},{"key_as_string":"2017-02-11T05:36:00.000Z","key":1486791360000,"doc_count":11},{"key_as_string":"2017-02-11T05:37:00.000Z","key":1486791420000,"doc_count":11},{"key_as_string":"2017-02-11T05:38:00.000Z","key":1486791480000,"doc_count":10},{"key_as_string":"2017-02-11T05:39:00.000Z","key":1486791540000,"doc_count":7},{"key_as_string":"2017-02-11T05:40:00.000Z","key":1486791600000,"doc_count":12},{"key_as_string":"2017-02-11T05:41:00.000Z","key":1486791660000,"doc_count":11},{"key_as_string":"2017-02-11T05:42:00.000Z","key":1486791720000,"doc_count":16},{"key_as_string":"2017-02-11T05:43:00.000Z","key":1486791780000,"doc_count":12},{"key_as_string":"2017-02-11T05:44:00.000Z","key":1486791840000,"doc_count":8},{"key_as_string":"2017-02-11T05:45:00.000Z","key":1486791900000,"doc_count":13},{"key_as_string":"2017-02-11T05:46:00.000Z","key":1486791960000,"doc_count":17},{"key_as_string":"2017-02-11T05:47:00.000Z","key":1486792020000,"doc_count":8},{"key_as_string":"2017-02-11T05:48:00.000Z","key":1486792080000,"doc_count":9},{"key_as_string":"2017-02-11T05:49:00.000Z","key":1486792140000,"doc_count":9},{"key_as_string":"2017-02-11T05:50:00.000Z","key":1486792200000,"doc_count":13},{"key_as_string":"2017-02-11T05:51:00.000Z","key":1486792260000,"doc_count":7},{"key_as_string":"2017-02-11T05:52:00.000Z","key":1486792320000,"doc_count":13},{"key_as_string":"2017-02-11T05:53:00.000Z","key":1486792380000,"doc_count":8},{"key_as_string":"2017-02-11T05:54:00.000Z","key":1486792440000,"doc_count":12},{"key_as_string":"2017-02-11T05:55:00.000Z","key":1486792500000,"doc_count":9},{"key_as_string":"2017-02-11T05:56:00.000Z","key":1486792560000,"doc_count":15},{"key_as_string":"2017-02-11T05:57:00.000Z","key":1486792620000,"doc_count":11},{"key_as_string":"2017-02-11T05:58:00.000Z","key":1486792680000,"doc_count":14},{"key_as_string":"2017-02-11T05:59:00.000Z","key":1486792740000,"doc_count":10},{"key_as_string":"2017-02-11T06:00:00.000Z","key":1486792800000,"doc_count":15},{"key_as_string":"2017-02-11T06:01:00.000Z","key":1486792860000,"doc_count":10},{"key_as_string":"2017-02-11T06:02:00.000Z","key":1486792920000,"doc_count":9},{"key_as_string":"2017-02-11T06:03:00.000Z","key":1486792980000,"doc_count":16},{"key_as_string":"2017-02-11T06:04:00.000Z","key":1486793040000,"doc_count":12},{"key_as_string":"2017-02-11T06:05:00.000Z","key":1486793100000,"doc_count":13},{"key_as_string":"2017-02-11T06:06:00.000Z","key":1486793160000,"doc_count":14},{"key_as_string":"2017-02-11T06:07:00.000Z","key":1486793220000,"doc_count":11},{"key_as_string":"2017-02-11T06:08:00.000Z","key":1486793280000,"doc_count":16},{"key_as_string":"2017-02-11T06:09:00.000Z","key":1486793340000,"doc_count":9},{"key_as_string":"2017-02-11T06:10:00.000Z","key":1486793400000,"doc_count":15},{"key_as_string":"2017-02-11T06:11:00.000Z","key":1486793460000,"doc_count":9},{"key_as_string":"2017-02-11T06:12:00.000Z","key":1486793520000,"doc_count":9},{"key_as_string":"2017-02-11T06:13:00.000Z","key":1486793580000,"doc_count":13},{"key_as_string":"2017-02-11T06:14:00.000Z","key":1486793640000,"doc_count":12},{"key_as_string":"2017-02-11T06:15:00.000Z","key":1486793700000,"doc_count":11},{"key_as_string":"2017-02-11T06:16:00.000Z","key":1486793760000,"doc_count":14},{"key_as_string":"2017-02-11T06:17:00.000Z","key":1486793820000,"doc_count":16},{"key_as_string":"2017-02-11T06:18:00.000Z","key":1486793880000,"doc_count":7},{"key_as_string":"2017-02-11T06:19:00.000Z","key":1486793940000,"doc_count":18},{"key_as_string":"2017-02-11T06:20:00.000Z","key":1486794000000,"doc_count":15},{"key_as_string":"2017-02-11T06:21:00.000Z","key":1486794060000,"doc_count":10},{"key_as_string":"2017-02-11T06:22:00.000Z","key":1486794120000,"doc_count":10},{"key_as_string":"2017-02-11T06:23:00.000Z","key":1486794180000,"doc_count":11},{"key_as_string":"2017-02-11T06:24:00.000Z","key":1486794240000,"doc_count":7},{"key_as_string":"2017-02-11T06:25:00.000Z","key":1486794300000,"doc_count":12},{"key_as_string":"2017-02-11T06:26:00.000Z","key":1486794360000,"doc_count":10},{"key_as_string":"2017-02-11T06:27:00.000Z","key":1486794420000,"doc_count":11},{"key_as_string":"2017-02-11T06:28:00.000Z","key":1486794480000,"doc_count":13},{"key_as_string":"2017-02-11T06:29:00.000Z","key":1486794540000,"doc_count":11},{"key_as_string":"2017-02-11T06:30:00.000Z","key":1486794600000,"doc_count":13},{"key_as_string":"2017-02-11T06:31:00.000Z","key":1486794660000,"doc_count":10},{"key_as_string":"2017-02-11T06:32:00.000Z","key":1486794720000,"doc_count":13},{"key_as_string":"2017-02-11T06:33:00.000Z","key":1486794780000,"doc_count":14},{"key_as_string":"2017-02-11T06:34:00.000Z","key":1486794840000,"doc_count":15},{"key_as_string":"2017-02-11T06:35:00.000Z","key":1486794900000,"doc_count":11},{"key_as_string":"2017-02-11T06:36:00.000Z","key":1486794960000,"doc_count":14},{"key_as_string":"2017-02-11T06:37:00.000Z","key":1486795020000,"doc_count":10},{"key_as_string":"2017-02-11T06:38:00.000Z","key":1486795080000,"doc_count":13},{"key_as_string":"2017-02-11T06:39:00.000Z","key":1486795140000,"doc_count":7},{"key_as_string":"2017-02-11T06:40:00.000Z","key":1486795200000,"doc_count":12},{"key_as_string":"2017-02-11T06:41:00.000Z","key":1486795260000,"doc_count":12},{"key_as_string":"2017-02-11T06:42:00.000Z","key":1486795320000,"doc_count":10},{"key_as_string":"2017-02-11T06:43:00.000Z","key":1486795380000,"doc_count":14},{"key_as_string":"2017-02-11T06:44:00.000Z","key":1486795440000,"doc_count":15},{"key_as_string":"2017-02-11T06:45:00.000Z","key":1486795500000,"doc_count":10},{"key_as_string":"2017-02-11T06:46:00.000Z","key":1486795560000,"doc_count":12},{"key_as_string":"2017-02-11T06:47:00.000Z","key":1486795620000,"doc_count":14},{"key_as_string":"2017-02-11T06:48:00.000Z","key":1486795680000,"doc_count":15},{"key_as_string":"2017-02-11T06:49:00.000Z","key":1486795740000,"doc_count":11},{"key_as_string":"2017-02-11T06:50:00.000Z","key":1486795800000,"doc_count":14},{"key_as_string":"2017-02-11T06:51:00.000Z","key":1486795860000,"doc_count":9},{"key_as_string":"2017-02-11T06:52:00.000Z","key":1486795920000,"doc_count":15},{"key_as_string":"2017-02-11T06:53:00.000Z","key":1486795980000,"doc_count":6},{"key_as_string":"2017-02-11T06:54:00.000Z","key":1486796040000,"doc_count":15},{"key_as_string":"2017-02-11T06:55:00.000Z","key":1486796100000,"doc_count":12},{"key_as_string":"2017-02-11T06:56:00.000Z","key":1486796160000,"doc_count":11},{"key_as_string":"2017-02-11T06:57:00.000Z","key":1486796220000,"doc_count":15},{"key_as_string":"2017-02-11T06:58:00.000Z","key":1486796280000,"doc_count":12},{"key_as_string":"2017-02-11T06:59:00.000Z","key":1486796340000,"doc_count":9},{"key_as_string":"2017-02-11T07:00:00.000Z","key":1486796400000,"doc_count":17},{"key_as_string":"2017-02-11T07:01:00.000Z","key":1486796460000,"doc_count":11},{"key_as_string":"2017-02-11T07:02:00.000Z","key":1486796520000,"doc_count":12},{"key_as_string":"2017-02-11T07:03:00.000Z","key":1486796580000,"doc_count":15},{"key_as_string":"2017-02-11T07:04:00.000Z","key":1486796640000,"doc_count":16},{"key_as_string":"2017-02-11T07:05:00.000Z","key":1486796700000,"doc_count":13},{"key_as_string":"2017-02-11T07:06:00.000Z","key":1486796760000,"doc_count":12},{"key_as_string":"2017-02-11T07:07:00.000Z","key":1486796820000,"doc_count":10},{"key_as_string":"2017-02-11T07:08:00.000Z","key":1486796880000,"doc_count":18},{"key_as_string":"2017-02-11T07:09:00.000Z","key":1486796940000,"doc_count":10},{"key_as_string":"2017-02-11T07:10:00.000Z","key":1486797000000,"doc_count":12},{"key_as_string":"2017-02-11T07:11:00.000Z","key":1486797060000,"doc_count":16},{"key_as_string":"2017-02-11T07:12:00.000Z","key":1486797120000,"doc_count":13},{"key_as_string":"2017-02-11T07:13:00.000Z","key":1486797180000,"doc_count":9},{"key_as_string":"2017-02-11T07:14:00.000Z","key":1486797240000,"doc_count":12},{"key_as_string":"2017-02-11T07:15:00.000Z","key":1486797300000,"doc_count":9},{"key_as_string":"2017-02-11T07:16:00.000Z","key":1486797360000,"doc_count":11},{"key_as_string":"2017-02-11T07:17:00.000Z","key":1486797420000,"doc_count":12},{"key_as_string":"2017-02-11T07:18:00.000Z","key":1486797480000,"doc_count":13},{"key_as_string":"2017-02-11T07:19:00.000Z","key":1486797540000,"doc_count":14},{"key_as_string":"2017-02-11T07:20:00.000Z","key":1486797600000,"doc_count":11},{"key_as_string":"2017-02-11T07:21:00.000Z","key":1486797660000,"doc_count":16},{"key_as_string":"2017-02-11T07:22:00.000Z","key":1486797720000,"doc_count":9},{"key_as_string":"2017-02-11T07:23:00.000Z","key":1486797780000,"doc_count":14},{"key_as_string":"2017-02-11T07:24:00.000Z","key":1486797840000,"doc_count":11},{"key_as_string":"2017-02-11T07:25:00.000Z","key":1486797900000,"doc_count":14},{"key_as_string":"2017-02-11T07:26:00.000Z","key":1486797960000,"doc_count":12},{"key_as_string":"2017-02-11T07:27:00.000Z","key":1486798020000,"doc_count":15},{"key_as_string":"2017-02-11T07:28:00.000Z","key":1486798080000,"doc_count":8},{"key_as_string":"2017-02-11T07:29:00.000Z","key":1486798140000,"doc_count":12},{"key_as_string":"2017-02-11T07:30:00.000Z","key":1486798200000,"doc_count":13},{"key_as_string":"2017-02-11T07:31:00.000Z","key":1486798260000,"doc_count":11},{"key_as_string":"2017-02-11T07:32:00.000Z","key":1486798320000,"doc_count":12},{"key_as_string":"2017-02-11T07:33:00.000Z","key":1486798380000,"doc_count":12},{"key_as_string":"2017-02-11T07:34:00.000Z","key":1486798440000,"doc_count":12},{"key_as_string":"2017-02-11T07:35:00.000Z","key":1486798500000,"doc_count":12},{"key_as_string":"2017-02-11T07:36:00.000Z","key":1486798560000,"doc_count":13},{"key_as_string":"2017-02-11T07:37:00.000Z","key":1486798620000,"doc_count":9},{"key_as_string":"2017-02-11T07:38:00.000Z","key":1486798680000,"doc_count":13},{"key_as_string":"2017-02-11T07:39:00.000Z","key":1486798740000,"doc_count":9},{"key_as_string":"2017-02-11T07:40:00.000Z","key":1486798800000,"doc_count":12},{"key_as_string":"2017-02-11T07:41:00.000Z","key":1486798860000,"doc_count":12},{"key_as_string":"2017-02-11T07:42:00.000Z","key":1486798920000,"doc_count":17},{"key_as_string":"2017-02-11T07:43:00.000Z","key":1486798980000,"doc_count":12},{"key_as_string":"2017-02-11T07:44:00.000Z","key":1486799040000,"doc_count":11},{"key_as_string":"2017-02-11T07:45:00.000Z","key":1486799100000,"doc_count":14},{"key_as_string":"2017-02-11T07:46:00.000Z","key":1486799160000,"doc_count":14},{"key_as_string":"2017-02-11T07:47:00.000Z","key":1486799220000,"doc_count":9},{"key_as_string":"2017-02-11T07:48:00.000Z","key":1486799280000,"doc_count":11},{"key_as_string":"2017-02-11T07:49:00.000Z","key":1486799340000,"doc_count":11},{"key_as_string":"2017-02-11T07:50:00.000Z","key":1486799400000,"doc_count":9},{"key_as_string":"2017-02-11T07:51:00.000Z","key":1486799460000,"doc_count":12},{"key_as_string":"2017-02-11T07:52:00.000Z","key":1486799520000,"doc_count":12},{"key_as_string":"2017-02-11T07:53:00.000Z","key":1486799580000,"doc_count":7},{"key_as_string":"2017-02-11T07:54:00.000Z","key":1486799640000,"doc_count":17},{"key_as_string":"2017-02-11T07:55:00.000Z","key":1486799700000,"doc_count":13},{"key_as_string":"2017-02-11T07:56:00.000Z","key":1486799760000,"doc_count":12},{"key_as_string":"2017-02-11T07:57:00.000Z","key":1486799820000,"doc_count":11},{"key_as_string":"2017-02-11T07:58:00.000Z","key":1486799880000,"doc_count":13},{"key_as_string":"2017-02-11T07:59:00.000Z","key":1486799940000,"doc_count":11},{"key_as_string":"2017-02-11T08:00:00.000Z","key":1486800000000,"doc_count":16},{"key_as_string":"2017-02-11T08:01:00.000Z","key":1486800060000,"doc_count":15},{"key_as_string":"2017-02-11T08:02:00.000Z","key":1486800120000,"doc_count":14},{"key_as_string":"2017-02-11T08:03:00.000Z","key":1486800180000,"doc_count":16},{"key_as_string":"2017-02-11T08:04:00.000Z","key":1486800240000,"doc_count":12},{"key_as_string":"2017-02-11T08:05:00.000Z","key":1486800300000,"doc_count":14},{"key_as_string":"2017-02-11T08:06:00.000Z","key":1486800360000,"doc_count":13},{"key_as_string":"2017-02-11T08:07:00.000Z","key":1486800420000,"doc_count":16},{"key_as_string":"2017-02-11T08:08:00.000Z","key":1486800480000,"doc_count":17},{"key_as_string":"2017-02-11T08:09:00.000Z","key":1486800540000,"doc_count":20},{"key_as_string":"2017-02-11T08:10:00.000Z","key":1486800600000,"doc_count":13},{"key_as_string":"2017-02-11T08:11:00.000Z","key":1486800660000,"doc_count":15},{"key_as_string":"2017-02-11T08:12:00.000Z","key":1486800720000,"doc_count":10},{"key_as_string":"2017-02-11T08:13:00.000Z","key":1486800780000,"doc_count":16},{"key_as_string":"2017-02-11T08:14:00.000Z","key":1486800840000,"doc_count":5},{"key_as_string":"2017-02-11T08:15:00.000Z","key":1486800900000,"doc_count":14},{"key_as_string":"2017-02-11T08:16:00.000Z","key":1486800960000,"doc_count":18},{"key_as_string":"2017-02-11T08:17:00.000Z","key":1486801020000,"doc_count":11},{"key_as_string":"2017-02-11T08:18:00.000Z","key":1486801080000,"doc_count":16},{"key_as_string":"2017-02-11T08:19:00.000Z","key":1486801140000,"doc_count":10},{"key_as_string":"2017-02-11T08:20:00.000Z","key":1486801200000,"doc_count":15},{"key_as_string":"2017-02-11T08:21:00.000Z","key":1486801260000,"doc_count":10},{"key_as_string":"2017-02-11T08:22:00.000Z","key":1486801320000,"doc_count":14},{"key_as_string":"2017-02-11T08:23:00.000Z","key":1486801380000,"doc_count":10},{"key_as_string":"2017-02-11T08:24:00.000Z","key":1486801440000,"doc_count":19},{"key_as_string":"2017-02-11T08:25:00.000Z","key":1486801500000,"doc_count":14},{"key_as_string":"2017-02-11T08:26:00.000Z","key":1486801560000,"doc_count":13},{"key_as_string":"2017-02-11T08:27:00.000Z","key":1486801620000,"doc_count":10},{"key_as_string":"2017-02-11T08:28:00.000Z","key":1486801680000,"doc_count":15},{"key_as_string":"2017-02-11T08:29:00.000Z","key":1486801740000,"doc_count":10},{"key_as_string":"2017-02-11T08:30:00.000Z","key":1486801800000,"doc_count":11},{"key_as_string":"2017-02-11T08:31:00.000Z","key":1486801860000,"doc_count":9},{"key_as_string":"2017-02-11T08:32:00.000Z","key":1486801920000,"doc_count":14},{"key_as_string":"2017-02-11T08:33:00.000Z","key":1486801980000,"doc_count":10},{"key_as_string":"2017-02-11T08:34:00.000Z","key":1486802040000,"doc_count":12},{"key_as_string":"2017-02-11T08:35:00.000Z","key":1486802100000,"doc_count":16},{"key_as_string":"2017-02-11T08:36:00.000Z","key":1486802160000,"doc_count":19},{"key_as_string":"2017-02-11T08:37:00.000Z","key":1486802220000,"doc_count":13},{"key_as_string":"2017-02-11T08:38:00.000Z","key":1486802280000,"doc_count":11},{"key_as_string":"2017-02-11T08:39:00.000Z","key":1486802340000,"doc_count":16},{"key_as_string":"2017-02-11T08:40:00.000Z","key":1486802400000,"doc_count":13},{"key_as_string":"2017-02-11T08:41:00.000Z","key":1486802460000,"doc_count":10},{"key_as_string":"2017-02-11T08:42:00.000Z","key":1486802520000,"doc_count":12},{"key_as_string":"2017-02-11T08:43:00.000Z","key":1486802580000,"doc_count":11},{"key_as_string":"2017-02-11T08:44:00.000Z","key":1486802640000,"doc_count":9},{"key_as_string":"2017-02-11T08:45:00.000Z","key":1486802700000,"doc_count":12},{"key_as_string":"2017-02-11T08:46:00.000Z","key":1486802760000,"doc_count":11},{"key_as_string":"2017-02-11T08:47:00.000Z","key":1486802820000,"doc_count":15},{"key_as_string":"2017-02-11T08:48:00.000Z","key":1486802880000,"doc_count":9},{"key_as_string":"2017-02-11T08:49:00.000Z","key":1486802940000,"doc_count":17},{"key_as_string":"2017-02-11T08:50:00.000Z","key":1486803000000,"doc_count":14},{"key_as_string":"2017-02-11T08:51:00.000Z","key":1486803060000,"doc_count":14},{"key_as_string":"2017-02-11T08:52:00.000Z","key":1486803120000,"doc_count":11},{"key_as_string":"2017-02-11T08:53:00.000Z","key":1486803180000,"doc_count":13},{"key_as_string":"2017-02-11T08:54:00.000Z","key":1486803240000,"doc_count":17},{"key_as_string":"2017-02-11T08:55:00.000Z","key":1486803300000,"doc_count":13},{"key_as_string":"2017-02-11T08:56:00.000Z","key":1486803360000,"doc_count":15},{"key_as_string":"2017-02-11T08:57:00.000Z","key":1486803420000,"doc_count":15},{"key_as_string":"2017-02-11T08:58:00.000Z","key":1486803480000,"doc_count":14},{"key_as_string":"2017-02-11T08:59:00.000Z","key":1486803540000,"doc_count":6},{"key_as_string":"2017-02-11T09:00:00.000Z","key":1486803600000,"doc_count":15},{"key_as_string":"2017-02-11T09:01:00.000Z","key":1486803660000,"doc_count":9},{"key_as_string":"2017-02-11T09:02:00.000Z","key":1486803720000,"doc_count":15},{"key_as_string":"2017-02-11T09:03:00.000Z","key":1486803780000,"doc_count":10},{"key_as_string":"2017-02-11T09:04:00.000Z","key":1486803840000,"doc_count":11},{"key_as_string":"2017-02-11T09:05:00.000Z","key":1486803900000,"doc_count":17},{"key_as_string":"2017-02-11T09:06:00.000Z","key":1486803960000,"doc_count":10},{"key_as_string":"2017-02-11T09:07:00.000Z","key":1486804020000,"doc_count":13},{"key_as_string":"2017-02-11T09:08:00.000Z","key":1486804080000,"doc_count":15},{"key_as_string":"2017-02-11T09:09:00.000Z","key":1486804140000,"doc_count":10},{"key_as_string":"2017-02-11T09:10:00.000Z","key":1486804200000,"doc_count":16},{"key_as_string":"2017-02-11T09:11:00.000Z","key":1486804260000,"doc_count":18},{"key_as_string":"2017-02-11T09:12:00.000Z","key":1486804320000,"doc_count":10},{"key_as_string":"2017-02-11T09:13:00.000Z","key":1486804380000,"doc_count":14},{"key_as_string":"2017-02-11T09:14:00.000Z","key":1486804440000,"doc_count":9},{"key_as_string":"2017-02-11T09:15:00.000Z","key":1486804500000,"doc_count":17},{"key_as_string":"2017-02-11T09:16:00.000Z","key":1486804560000,"doc_count":18},{"key_as_string":"2017-02-11T09:17:00.000Z","key":1486804620000,"doc_count":12},{"key_as_string":"2017-02-11T09:18:00.000Z","key":1486804680000,"doc_count":15},{"key_as_string":"2017-02-11T09:19:00.000Z","key":1486804740000,"doc_count":10},{"key_as_string":"2017-02-11T09:20:00.000Z","key":1486804800000,"doc_count":14},{"key_as_string":"2017-02-11T09:21:00.000Z","key":1486804860000,"doc_count":15},{"key_as_string":"2017-02-11T09:22:00.000Z","key":1486804920000,"doc_count":12},{"key_as_string":"2017-02-11T09:23:00.000Z","key":1486804980000,"doc_count":17},{"key_as_string":"2017-02-11T09:24:00.000Z","key":1486805040000,"doc_count":13},{"key_as_string":"2017-02-11T09:25:00.000Z","key":1486805100000,"doc_count":17},{"key_as_string":"2017-02-11T09:26:00.000Z","key":1486805160000,"doc_count":11},{"key_as_string":"2017-02-11T09:27:00.000Z","key":1486805220000,"doc_count":11},{"key_as_string":"2017-02-11T09:28:00.000Z","key":1486805280000,"doc_count":13},{"key_as_string":"2017-02-11T09:29:00.000Z","key":1486805340000,"doc_count":9},{"key_as_string":"2017-02-11T09:30:00.000Z","key":1486805400000,"doc_count":20},{"key_as_string":"2017-02-11T09:31:00.000Z","key":1486805460000,"doc_count":13},{"key_as_string":"2017-02-11T09:32:00.000Z","key":1486805520000,"doc_count":17},{"key_as_string":"2017-02-11T09:33:00.000Z","key":1486805580000,"doc_count":16},{"key_as_string":"2017-02-11T09:34:00.000Z","key":1486805640000,"doc_count":11},{"key_as_string":"2017-02-11T09:35:00.000Z","key":1486805700000,"doc_count":18},{"key_as_string":"2017-02-11T09:36:00.000Z","key":1486805760000,"doc_count":14},{"key_as_string":"2017-02-11T09:37:00.000Z","key":1486805820000,"doc_count":17},{"key_as_string":"2017-02-11T09:38:00.000Z","key":1486805880000,"doc_count":12},{"key_as_string":"2017-02-11T09:39:00.000Z","key":1486805940000,"doc_count":12},{"key_as_string":"2017-02-11T09:40:00.000Z","key":1486806000000,"doc_count":14},{"key_as_string":"2017-02-11T09:41:00.000Z","key":1486806060000,"doc_count":11},{"key_as_string":"2017-02-11T09:42:00.000Z","key":1486806120000,"doc_count":12},{"key_as_string":"2017-02-11T09:43:00.000Z","key":1486806180000,"doc_count":14},{"key_as_string":"2017-02-11T09:44:00.000Z","key":1486806240000,"doc_count":16},{"key_as_string":"2017-02-11T09:45:00.000Z","key":1486806300000,"doc_count":17},{"key_as_string":"2017-02-11T09:46:00.000Z","key":1486806360000,"doc_count":12},{"key_as_string":"2017-02-11T09:47:00.000Z","key":1486806420000,"doc_count":15},{"key_as_string":"2017-02-11T09:48:00.000Z","key":1486806480000,"doc_count":13},{"key_as_string":"2017-02-11T09:49:00.000Z","key":1486806540000,"doc_count":14},{"key_as_string":"2017-02-11T09:50:00.000Z","key":1486806600000,"doc_count":17},{"key_as_string":"2017-02-11T09:51:00.000Z","key":1486806660000,"doc_count":15},{"key_as_string":"2017-02-11T09:52:00.000Z","key":1486806720000,"doc_count":9},{"key_as_string":"2017-02-11T09:53:00.000Z","key":1486806780000,"doc_count":20},{"key_as_string":"2017-02-11T09:54:00.000Z","key":1486806840000,"doc_count":15},{"key_as_string":"2017-02-11T09:55:00.000Z","key":1486806900000,"doc_count":19},{"key_as_string":"2017-02-11T09:56:00.000Z","key":1486806960000,"doc_count":10},{"key_as_string":"2017-02-11T09:57:00.000Z","key":1486807020000,"doc_count":14},{"key_as_string":"2017-02-11T09:58:00.000Z","key":1486807080000,"doc_count":15},{"key_as_string":"2017-02-11T09:59:00.000Z","key":1486807140000,"doc_count":20},{"key_as_string":"2017-02-11T10:00:00.000Z","key":1486807200000,"doc_count":14},{"key_as_string":"2017-02-11T10:01:00.000Z","key":1486807260000,"doc_count":14},{"key_as_string":"2017-02-11T10:02:00.000Z","key":1486807320000,"doc_count":13},{"key_as_string":"2017-02-11T10:03:00.000Z","key":1486807380000,"doc_count":13},{"key_as_string":"2017-02-11T10:04:00.000Z","key":1486807440000,"doc_count":17},{"key_as_string":"2017-02-11T10:05:00.000Z","key":1486807500000,"doc_count":10},{"key_as_string":"2017-02-11T10:06:00.000Z","key":1486807560000,"doc_count":20},{"key_as_string":"2017-02-11T10:07:00.000Z","key":1486807620000,"doc_count":14},{"key_as_string":"2017-02-11T10:08:00.000Z","key":1486807680000,"doc_count":12},{"key_as_string":"2017-02-11T10:09:00.000Z","key":1486807740000,"doc_count":22},{"key_as_string":"2017-02-11T10:10:00.000Z","key":1486807800000,"doc_count":18},{"key_as_string":"2017-02-11T10:11:00.000Z","key":1486807860000,"doc_count":9},{"key_as_string":"2017-02-11T10:12:00.000Z","key":1486807920000,"doc_count":16},{"key_as_string":"2017-02-11T10:13:00.000Z","key":1486807980000,"doc_count":13},{"key_as_string":"2017-02-11T10:14:00.000Z","key":1486808040000,"doc_count":14},{"key_as_string":"2017-02-11T10:15:00.000Z","key":1486808100000,"doc_count":13},{"key_as_string":"2017-02-11T10:16:00.000Z","key":1486808160000,"doc_count":15},{"key_as_string":"2017-02-11T10:17:00.000Z","key":1486808220000,"doc_count":15},{"key_as_string":"2017-02-11T10:18:00.000Z","key":1486808280000,"doc_count":13},{"key_as_string":"2017-02-11T10:19:00.000Z","key":1486808340000,"doc_count":12},{"key_as_string":"2017-02-11T10:20:00.000Z","key":1486808400000,"doc_count":8},{"key_as_string":"2017-02-11T10:21:00.000Z","key":1486808460000,"doc_count":18},{"key_as_string":"2017-02-11T10:22:00.000Z","key":1486808520000,"doc_count":14},{"key_as_string":"2017-02-11T10:23:00.000Z","key":1486808580000,"doc_count":17},{"key_as_string":"2017-02-11T10:24:00.000Z","key":1486808640000,"doc_count":22},{"key_as_string":"2017-02-11T10:25:00.000Z","key":1486808700000,"doc_count":12},{"key_as_string":"2017-02-11T10:26:00.000Z","key":1486808760000,"doc_count":13},{"key_as_string":"2017-02-11T10:27:00.000Z","key":1486808820000,"doc_count":17},{"key_as_string":"2017-02-11T10:28:00.000Z","key":1486808880000,"doc_count":14},{"key_as_string":"2017-02-11T10:29:00.000Z","key":1486808940000,"doc_count":13},{"key_as_string":"2017-02-11T10:30:00.000Z","key":1486809000000,"doc_count":11},{"key_as_string":"2017-02-11T10:31:00.000Z","key":1486809060000,"doc_count":15},{"key_as_string":"2017-02-11T10:32:00.000Z","key":1486809120000,"doc_count":17},{"key_as_string":"2017-02-11T10:33:00.000Z","key":1486809180000,"doc_count":14},{"key_as_string":"2017-02-11T10:34:00.000Z","key":1486809240000,"doc_count":12},{"key_as_string":"2017-02-11T10:35:00.000Z","key":1486809300000,"doc_count":10},{"key_as_string":"2017-02-11T10:36:00.000Z","key":1486809360000,"doc_count":12},{"key_as_string":"2017-02-11T10:37:00.000Z","key":1486809420000,"doc_count":16},{"key_as_string":"2017-02-11T10:38:00.000Z","key":1486809480000,"doc_count":18},{"key_as_string":"2017-02-11T10:39:00.000Z","key":1486809540000,"doc_count":15},{"key_as_string":"2017-02-11T10:40:00.000Z","key":1486809600000,"doc_count":16},{"key_as_string":"2017-02-11T10:41:00.000Z","key":1486809660000,"doc_count":15},{"key_as_string":"2017-02-11T10:42:00.000Z","key":1486809720000,"doc_count":15},{"key_as_string":"2017-02-11T10:43:00.000Z","key":1486809780000,"doc_count":11},{"key_as_string":"2017-02-11T10:44:00.000Z","key":1486809840000,"doc_count":11},{"key_as_string":"2017-02-11T10:45:00.000Z","key":1486809900000,"doc_count":19},{"key_as_string":"2017-02-11T10:46:00.000Z","key":1486809960000,"doc_count":12},{"key_as_string":"2017-02-11T10:47:00.000Z","key":1486810020000,"doc_count":12},{"key_as_string":"2017-02-11T10:48:00.000Z","key":1486810080000,"doc_count":12},{"key_as_string":"2017-02-11T10:49:00.000Z","key":1486810140000,"doc_count":12},{"key_as_string":"2017-02-11T10:50:00.000Z","key":1486810200000,"doc_count":21},{"key_as_string":"2017-02-11T10:51:00.000Z","key":1486810260000,"doc_count":13},{"key_as_string":"2017-02-11T10:52:00.000Z","key":1486810320000,"doc_count":13},{"key_as_string":"2017-02-11T10:53:00.000Z","key":1486810380000,"doc_count":14},{"key_as_string":"2017-02-11T10:54:00.000Z","key":1486810440000,"doc_count":14},{"key_as_string":"2017-02-11T10:55:00.000Z","key":1486810500000,"doc_count":10},{"key_as_string":"2017-02-11T10:56:00.000Z","key":1486810560000,"doc_count":12},{"key_as_string":"2017-02-11T10:57:00.000Z","key":1486810620000,"doc_count":15},{"key_as_string":"2017-02-11T10:58:00.000Z","key":1486810680000,"doc_count":13},{"key_as_string":"2017-02-11T10:59:00.000Z","key":1486810740000,"doc_count":11},{"key_as_string":"2017-02-11T11:00:00.000Z","key":1486810800000,"doc_count":18},{"key_as_string":"2017-02-11T11:01:00.000Z","key":1486810860000,"doc_count":14},{"key_as_string":"2017-02-11T11:02:00.000Z","key":1486810920000,"doc_count":18},{"key_as_string":"2017-02-11T11:03:00.000Z","key":1486810980000,"doc_count":18},{"key_as_string":"2017-02-11T11:04:00.000Z","key":1486811040000,"doc_count":14},{"key_as_string":"2017-02-11T11:05:00.000Z","key":1486811100000,"doc_count":21},{"key_as_string":"2017-02-11T11:06:00.000Z","key":1486811160000,"doc_count":8},{"key_as_string":"2017-02-11T11:07:00.000Z","key":1486811220000,"doc_count":14},{"key_as_string":"2017-02-11T11:08:00.000Z","key":1486811280000,"doc_count":9},{"key_as_string":"2017-02-11T11:09:00.000Z","key":1486811340000,"doc_count":13},{"key_as_string":"2017-02-11T11:10:00.000Z","key":1486811400000,"doc_count":19},{"key_as_string":"2017-02-11T11:11:00.000Z","key":1486811460000,"doc_count":12},{"key_as_string":"2017-02-11T11:12:00.000Z","key":1486811520000,"doc_count":15},{"key_as_string":"2017-02-11T11:13:00.000Z","key":1486811580000,"doc_count":16},{"key_as_string":"2017-02-11T11:14:00.000Z","key":1486811640000,"doc_count":11},{"key_as_string":"2017-02-11T11:15:00.000Z","key":1486811700000,"doc_count":12},{"key_as_string":"2017-02-11T11:16:00.000Z","key":1486811760000,"doc_count":16},{"key_as_string":"2017-02-11T11:17:00.000Z","key":1486811820000,"doc_count":14},{"key_as_string":"2017-02-11T11:18:00.000Z","key":1486811880000,"doc_count":13},{"key_as_string":"2017-02-11T11:19:00.000Z","key":1486811940000,"doc_count":14},{"key_as_string":"2017-02-11T11:20:00.000Z","key":1486812000000,"doc_count":14},{"key_as_string":"2017-02-11T11:21:00.000Z","key":1486812060000,"doc_count":11},{"key_as_string":"2017-02-11T11:22:00.000Z","key":1486812120000,"doc_count":14},{"key_as_string":"2017-02-11T11:23:00.000Z","key":1486812180000,"doc_count":13},{"key_as_string":"2017-02-11T11:24:00.000Z","key":1486812240000,"doc_count":13},{"key_as_string":"2017-02-11T11:25:00.000Z","key":1486812300000,"doc_count":13},{"key_as_string":"2017-02-11T11:26:00.000Z","key":1486812360000,"doc_count":6},{"key_as_string":"2017-02-11T11:27:00.000Z","key":1486812420000,"doc_count":21},{"key_as_string":"2017-02-11T11:28:00.000Z","key":1486812480000,"doc_count":10},{"key_as_string":"2017-02-11T11:29:00.000Z","key":1486812540000,"doc_count":19},{"key_as_string":"2017-02-11T11:30:00.000Z","key":1486812600000,"doc_count":10},{"key_as_string":"2017-02-11T11:31:00.000Z","key":1486812660000,"doc_count":10},{"key_as_string":"2017-02-11T11:32:00.000Z","key":1486812720000,"doc_count":14},{"key_as_string":"2017-02-11T11:33:00.000Z","key":1486812780000,"doc_count":13},{"key_as_string":"2017-02-11T11:34:00.000Z","key":1486812840000,"doc_count":9},{"key_as_string":"2017-02-11T11:35:00.000Z","key":1486812900000,"doc_count":16},{"key_as_string":"2017-02-11T11:36:00.000Z","key":1486812960000,"doc_count":9},{"key_as_string":"2017-02-11T11:37:00.000Z","key":1486813020000,"doc_count":14},{"key_as_string":"2017-02-11T11:38:00.000Z","key":1486813080000,"doc_count":8},{"key_as_string":"2017-02-11T11:39:00.000Z","key":1486813140000,"doc_count":16},{"key_as_string":"2017-02-11T11:40:00.000Z","key":1486813200000,"doc_count":17},{"key_as_string":"2017-02-11T11:41:00.000Z","key":1486813260000,"doc_count":11},{"key_as_string":"2017-02-11T11:42:00.000Z","key":1486813320000,"doc_count":13},{"key_as_string":"2017-02-11T11:43:00.000Z","key":1486813380000,"doc_count":14},{"key_as_string":"2017-02-11T11:44:00.000Z","key":1486813440000,"doc_count":17},{"key_as_string":"2017-02-11T11:45:00.000Z","key":1486813500000,"doc_count":11},{"key_as_string":"2017-02-11T11:46:00.000Z","key":1486813560000,"doc_count":16},{"key_as_string":"2017-02-11T11:47:00.000Z","key":1486813620000,"doc_count":12},{"key_as_string":"2017-02-11T11:48:00.000Z","key":1486813680000,"doc_count":13},{"key_as_string":"2017-02-11T11:49:00.000Z","key":1486813740000,"doc_count":19},{"key_as_string":"2017-02-11T11:50:00.000Z","key":1486813800000,"doc_count":12},{"key_as_string":"2017-02-11T11:51:00.000Z","key":1486813860000,"doc_count":15},{"key_as_string":"2017-02-11T11:52:00.000Z","key":1486813920000,"doc_count":12},{"key_as_string":"2017-02-11T11:53:00.000Z","key":1486813980000,"doc_count":8},{"key_as_string":"2017-02-11T11:54:00.000Z","key":1486814040000,"doc_count":15},{"key_as_string":"2017-02-11T11:55:00.000Z","key":1486814100000,"doc_count":16},{"key_as_string":"2017-02-11T11:56:00.000Z","key":1486814160000,"doc_count":10},{"key_as_string":"2017-02-11T11:57:00.000Z","key":1486814220000,"doc_count":12},{"key_as_string":"2017-02-11T11:58:00.000Z","key":1486814280000,"doc_count":17},{"key_as_string":"2017-02-11T11:59:00.000Z","key":1486814340000,"doc_count":18},{"key_as_string":"2017-02-11T12:00:00.000Z","key":1486814400000,"doc_count":13},{"key_as_string":"2017-02-11T12:01:00.000Z","key":1486814460000,"doc_count":13},{"key_as_string":"2017-02-11T12:02:00.000Z","key":1486814520000,"doc_count":9},{"key_as_string":"2017-02-11T12:03:00.000Z","key":1486814580000,"doc_count":14},{"key_as_string":"2017-02-11T12:04:00.000Z","key":1486814640000,"doc_count":11},{"key_as_string":"2017-02-11T12:05:00.000Z","key":1486814700000,"doc_count":9},{"key_as_string":"2017-02-11T12:06:00.000Z","key":1486814760000,"doc_count":18},{"key_as_string":"2017-02-11T12:07:00.000Z","key":1486814820000,"doc_count":14},{"key_as_string":"2017-02-11T12:08:00.000Z","key":1486814880000,"doc_count":9},{"key_as_string":"2017-02-11T12:09:00.000Z","key":1486814940000,"doc_count":15},{"key_as_string":"2017-02-11T12:10:00.000Z","key":1486815000000,"doc_count":18},{"key_as_string":"2017-02-11T12:11:00.000Z","key":1486815060000,"doc_count":14},{"key_as_string":"2017-02-11T12:12:00.000Z","key":1486815120000,"doc_count":16},{"key_as_string":"2017-02-11T12:13:00.000Z","key":1486815180000,"doc_count":14},{"key_as_string":"2017-02-11T12:14:00.000Z","key":1486815240000,"doc_count":12},{"key_as_string":"2017-02-11T12:15:00.000Z","key":1486815300000,"doc_count":15},{"key_as_string":"2017-02-11T12:16:00.000Z","key":1486815360000,"doc_count":12},{"key_as_string":"2017-02-11T12:17:00.000Z","key":1486815420000,"doc_count":12},{"key_as_string":"2017-02-11T12:18:00.000Z","key":1486815480000,"doc_count":16},{"key_as_string":"2017-02-11T12:19:00.000Z","key":1486815540000,"doc_count":9},{"key_as_string":"2017-02-11T12:20:00.000Z","key":1486815600000,"doc_count":14},{"key_as_string":"2017-02-11T12:21:00.000Z","key":1486815660000,"doc_count":11},{"key_as_string":"2017-02-11T12:22:00.000Z","key":1486815720000,"doc_count":16},{"key_as_string":"2017-02-11T12:23:00.000Z","key":1486815780000,"doc_count":14},{"key_as_string":"2017-02-11T12:24:00.000Z","key":1486815840000,"doc_count":10},{"key_as_string":"2017-02-11T12:25:00.000Z","key":1486815900000,"doc_count":16},{"key_as_string":"2017-02-11T12:26:00.000Z","key":1486815960000,"doc_count":18},{"key_as_string":"2017-02-11T12:27:00.000Z","key":1486816020000,"doc_count":15},{"key_as_string":"2017-02-11T12:28:00.000Z","key":1486816080000,"doc_count":10},{"key_as_string":"2017-02-11T12:29:00.000Z","key":1486816140000,"doc_count":12},{"key_as_string":"2017-02-11T12:30:00.000Z","key":1486816200000,"doc_count":11},{"key_as_string":"2017-02-11T12:31:00.000Z","key":1486816260000,"doc_count":16},{"key_as_string":"2017-02-11T12:32:00.000Z","key":1486816320000,"doc_count":11},{"key_as_string":"2017-02-11T12:33:00.000Z","key":1486816380000,"doc_count":11},{"key_as_string":"2017-02-11T12:34:00.000Z","key":1486816440000,"doc_count":19},{"key_as_string":"2017-02-11T12:35:00.000Z","key":1486816500000,"doc_count":17},{"key_as_string":"2017-02-11T12:36:00.000Z","key":1486816560000,"doc_count":19},{"key_as_string":"2017-02-11T12:37:00.000Z","key":1486816620000,"doc_count":11},{"key_as_string":"2017-02-11T12:38:00.000Z","key":1486816680000,"doc_count":18},{"key_as_string":"2017-02-11T12:39:00.000Z","key":1486816740000,"doc_count":11},{"key_as_string":"2017-02-11T12:40:00.000Z","key":1486816800000,"doc_count":12},{"key_as_string":"2017-02-11T12:41:00.000Z","key":1486816860000,"doc_count":15},{"key_as_string":"2017-02-11T12:42:00.000Z","key":1486816920000,"doc_count":15},{"key_as_string":"2017-02-11T12:43:00.000Z","key":1486816980000,"doc_count":7},{"key_as_string":"2017-02-11T12:44:00.000Z","key":1486817040000,"doc_count":19},{"key_as_string":"2017-02-11T12:45:00.000Z","key":1486817100000,"doc_count":12},{"key_as_string":"2017-02-11T12:46:00.000Z","key":1486817160000,"doc_count":16},{"key_as_string":"2017-02-11T12:47:00.000Z","key":1486817220000,"doc_count":8},{"key_as_string":"2017-02-11T12:48:00.000Z","key":1486817280000,"doc_count":15},{"key_as_string":"2017-02-11T12:49:00.000Z","key":1486817340000,"doc_count":12},{"key_as_string":"2017-02-11T12:50:00.000Z","key":1486817400000,"doc_count":12},{"key_as_string":"2017-02-11T12:51:00.000Z","key":1486817460000,"doc_count":16},{"key_as_string":"2017-02-11T12:52:00.000Z","key":1486817520000,"doc_count":13},{"key_as_string":"2017-02-11T12:53:00.000Z","key":1486817580000,"doc_count":13},{"key_as_string":"2017-02-11T12:54:00.000Z","key":1486817640000,"doc_count":15},{"key_as_string":"2017-02-11T12:55:00.000Z","key":1486817700000,"doc_count":16},{"key_as_string":"2017-02-11T12:56:00.000Z","key":1486817760000,"doc_count":17},{"key_as_string":"2017-02-11T12:57:00.000Z","key":1486817820000,"doc_count":13},{"key_as_string":"2017-02-11T12:58:00.000Z","key":1486817880000,"doc_count":12},{"key_as_string":"2017-02-11T12:59:00.000Z","key":1486817940000,"doc_count":17},{"key_as_string":"2017-02-11T13:00:00.000Z","key":1486818000000,"doc_count":16},{"key_as_string":"2017-02-11T13:01:00.000Z","key":1486818060000,"doc_count":14},{"key_as_string":"2017-02-11T13:02:00.000Z","key":1486818120000,"doc_count":8},{"key_as_string":"2017-02-11T13:03:00.000Z","key":1486818180000,"doc_count":15},{"key_as_string":"2017-02-11T13:04:00.000Z","key":1486818240000,"doc_count":14},{"key_as_string":"2017-02-11T13:05:00.000Z","key":1486818300000,"doc_count":16},{"key_as_string":"2017-02-11T13:06:00.000Z","key":1486818360000,"doc_count":20},{"key_as_string":"2017-02-11T13:07:00.000Z","key":1486818420000,"doc_count":10},{"key_as_string":"2017-02-11T13:08:00.000Z","key":1486818480000,"doc_count":15},{"key_as_string":"2017-02-11T13:09:00.000Z","key":1486818540000,"doc_count":12},{"key_as_string":"2017-02-11T13:10:00.000Z","key":1486818600000,"doc_count":12},{"key_as_string":"2017-02-11T13:11:00.000Z","key":1486818660000,"doc_count":8},{"key_as_string":"2017-02-11T13:12:00.000Z","key":1486818720000,"doc_count":17},{"key_as_string":"2017-02-11T13:13:00.000Z","key":1486818780000,"doc_count":8},{"key_as_string":"2017-02-11T13:14:00.000Z","key":1486818840000,"doc_count":15},{"key_as_string":"2017-02-11T13:15:00.000Z","key":1486818900000,"doc_count":13},{"key_as_string":"2017-02-11T13:16:00.000Z","key":1486818960000,"doc_count":15},{"key_as_string":"2017-02-11T13:17:00.000Z","key":1486819020000,"doc_count":15},{"key_as_string":"2017-02-11T13:18:00.000Z","key":1486819080000,"doc_count":11},{"key_as_string":"2017-02-11T13:19:00.000Z","key":1486819140000,"doc_count":17},{"key_as_string":"2017-02-11T13:20:00.000Z","key":1486819200000,"doc_count":11},{"key_as_string":"2017-02-11T13:21:00.000Z","key":1486819260000,"doc_count":14},{"key_as_string":"2017-02-11T13:22:00.000Z","key":1486819320000,"doc_count":10},{"key_as_string":"2017-02-11T13:23:00.000Z","key":1486819380000,"doc_count":15},{"key_as_string":"2017-02-11T13:24:00.000Z","key":1486819440000,"doc_count":14},{"key_as_string":"2017-02-11T13:25:00.000Z","key":1486819500000,"doc_count":11},{"key_as_string":"2017-02-11T13:26:00.000Z","key":1486819560000,"doc_count":12},{"key_as_string":"2017-02-11T13:27:00.000Z","key":1486819620000,"doc_count":13},{"key_as_string":"2017-02-11T13:28:00.000Z","key":1486819680000,"doc_count":16},{"key_as_string":"2017-02-11T13:29:00.000Z","key":1486819740000,"doc_count":8},{"key_as_string":"2017-02-11T13:30:00.000Z","key":1486819800000,"doc_count":12},{"key_as_string":"2017-02-11T13:31:00.000Z","key":1486819860000,"doc_count":10},{"key_as_string":"2017-02-11T13:32:00.000Z","key":1486819920000,"doc_count":15},{"key_as_string":"2017-02-11T13:33:00.000Z","key":1486819980000,"doc_count":13},{"key_as_string":"2017-02-11T13:34:00.000Z","key":1486820040000,"doc_count":15},{"key_as_string":"2017-02-11T13:35:00.000Z","key":1486820100000,"doc_count":13},{"key_as_string":"2017-02-11T13:36:00.000Z","key":1486820160000,"doc_count":15},{"key_as_string":"2017-02-11T13:37:00.000Z","key":1486820220000,"doc_count":19},{"key_as_string":"2017-02-11T13:38:00.000Z","key":1486820280000,"doc_count":13},{"key_as_string":"2017-02-11T13:39:00.000Z","key":1486820340000,"doc_count":15},{"key_as_string":"2017-02-11T13:40:00.000Z","key":1486820400000,"doc_count":16},{"key_as_string":"2017-02-11T13:41:00.000Z","key":1486820460000,"doc_count":14},{"key_as_string":"2017-02-11T13:42:00.000Z","key":1486820520000,"doc_count":18},{"key_as_string":"2017-02-11T13:43:00.000Z","key":1486820580000,"doc_count":11},{"key_as_string":"2017-02-11T13:44:00.000Z","key":1486820640000,"doc_count":14},{"key_as_string":"2017-02-11T13:45:00.000Z","key":1486820700000,"doc_count":14},{"key_as_string":"2017-02-11T13:46:00.000Z","key":1486820760000,"doc_count":13},{"key_as_string":"2017-02-11T13:47:00.000Z","key":1486820820000,"doc_count":9},{"key_as_string":"2017-02-11T13:48:00.000Z","key":1486820880000,"doc_count":11},{"key_as_string":"2017-02-11T13:49:00.000Z","key":1486820940000,"doc_count":20},{"key_as_string":"2017-02-11T13:50:00.000Z","key":1486821000000,"doc_count":9},{"key_as_string":"2017-02-11T13:51:00.000Z","key":1486821060000,"doc_count":8},{"key_as_string":"2017-02-11T13:52:00.000Z","key":1486821120000,"doc_count":20},{"key_as_string":"2017-02-11T13:53:00.000Z","key":1486821180000,"doc_count":11},{"key_as_string":"2017-02-11T13:54:00.000Z","key":1486821240000,"doc_count":16},{"key_as_string":"2017-02-11T13:55:00.000Z","key":1486821300000,"doc_count":15},{"key_as_string":"2017-02-11T13:56:00.000Z","key":1486821360000,"doc_count":14},{"key_as_string":"2017-02-11T13:57:00.000Z","key":1486821420000,"doc_count":15},{"key_as_string":"2017-02-11T13:58:00.000Z","key":1486821480000,"doc_count":15},{"key_as_string":"2017-02-11T13:59:00.000Z","key":1486821540000,"doc_count":9},{"key_as_string":"2017-02-11T14:00:00.000Z","key":1486821600000,"doc_count":12},{"key_as_string":"2017-02-11T14:01:00.000Z","key":1486821660000,"doc_count":13},{"key_as_string":"2017-02-11T14:02:00.000Z","key":1486821720000,"doc_count":15},{"key_as_string":"2017-02-11T14:03:00.000Z","key":1486821780000,"doc_count":12},{"key_as_string":"2017-02-11T14:04:00.000Z","key":1486821840000,"doc_count":16},{"key_as_string":"2017-02-11T14:05:00.000Z","key":1486821900000,"doc_count":10},{"key_as_string":"2017-02-11T14:06:00.000Z","key":1486821960000,"doc_count":12},{"key_as_string":"2017-02-11T14:07:00.000Z","key":1486822020000,"doc_count":13},{"key_as_string":"2017-02-11T14:08:00.000Z","key":1486822080000,"doc_count":9},{"key_as_string":"2017-02-11T14:09:00.000Z","key":1486822140000,"doc_count":16},{"key_as_string":"2017-02-11T14:10:00.000Z","key":1486822200000,"doc_count":15},{"key_as_string":"2017-02-11T14:11:00.000Z","key":1486822260000,"doc_count":14},{"key_as_string":"2017-02-11T14:12:00.000Z","key":1486822320000,"doc_count":10},{"key_as_string":"2017-02-11T14:13:00.000Z","key":1486822380000,"doc_count":10},{"key_as_string":"2017-02-11T14:14:00.000Z","key":1486822440000,"doc_count":15},{"key_as_string":"2017-02-11T14:15:00.000Z","key":1486822500000,"doc_count":10},{"key_as_string":"2017-02-11T14:16:00.000Z","key":1486822560000,"doc_count":13},{"key_as_string":"2017-02-11T14:17:00.000Z","key":1486822620000,"doc_count":14},{"key_as_string":"2017-02-11T14:18:00.000Z","key":1486822680000,"doc_count":14},{"key_as_string":"2017-02-11T14:19:00.000Z","key":1486822740000,"doc_count":17},{"key_as_string":"2017-02-11T14:20:00.000Z","key":1486822800000,"doc_count":13},{"key_as_string":"2017-02-11T14:21:00.000Z","key":1486822860000,"doc_count":14},{"key_as_string":"2017-02-11T14:22:00.000Z","key":1486822920000,"doc_count":12},{"key_as_string":"2017-02-11T14:23:00.000Z","key":1486822980000,"doc_count":21},{"key_as_string":"2017-02-11T14:24:00.000Z","key":1486823040000,"doc_count":14},{"key_as_string":"2017-02-11T14:25:00.000Z","key":1486823100000,"doc_count":9},{"key_as_string":"2017-02-11T14:26:00.000Z","key":1486823160000,"doc_count":8},{"key_as_string":"2017-02-11T14:27:00.000Z","key":1486823220000,"doc_count":14},{"key_as_string":"2017-02-11T14:28:00.000Z","key":1486823280000,"doc_count":10},{"key_as_string":"2017-02-11T14:29:00.000Z","key":1486823340000,"doc_count":12},{"key_as_string":"2017-02-11T14:30:00.000Z","key":1486823400000,"doc_count":12},{"key_as_string":"2017-02-11T14:31:00.000Z","key":1486823460000,"doc_count":14},{"key_as_string":"2017-02-11T14:32:00.000Z","key":1486823520000,"doc_count":15},{"key_as_string":"2017-02-11T14:33:00.000Z","key":1486823580000,"doc_count":14},{"key_as_string":"2017-02-11T14:34:00.000Z","key":1486823640000,"doc_count":9},{"key_as_string":"2017-02-11T14:35:00.000Z","key":1486823700000,"doc_count":14},{"key_as_string":"2017-02-11T14:36:00.000Z","key":1486823760000,"doc_count":12},{"key_as_string":"2017-02-11T14:37:00.000Z","key":1486823820000,"doc_count":17},{"key_as_string":"2017-02-11T14:38:00.000Z","key":1486823880000,"doc_count":11},{"key_as_string":"2017-02-11T14:39:00.000Z","key":1486823940000,"doc_count":12},{"key_as_string":"2017-02-11T14:40:00.000Z","key":1486824000000,"doc_count":15},{"key_as_string":"2017-02-11T14:41:00.000Z","key":1486824060000,"doc_count":11},{"key_as_string":"2017-02-11T14:42:00.000Z","key":1486824120000,"doc_count":13},{"key_as_string":"2017-02-11T14:43:00.000Z","key":1486824180000,"doc_count":18},{"key_as_string":"2017-02-11T14:44:00.000Z","key":1486824240000,"doc_count":10},{"key_as_string":"2017-02-11T14:45:00.000Z","key":1486824300000,"doc_count":13},{"key_as_string":"2017-02-11T14:46:00.000Z","key":1486824360000,"doc_count":9},{"key_as_string":"2017-02-11T14:47:00.000Z","key":1486824420000,"doc_count":14},{"key_as_string":"2017-02-11T14:48:00.000Z","key":1486824480000,"doc_count":16},{"key_as_string":"2017-02-11T14:49:00.000Z","key":1486824540000,"doc_count":14},{"key_as_string":"2017-02-11T14:50:00.000Z","key":1486824600000,"doc_count":13},{"key_as_string":"2017-02-11T14:51:00.000Z","key":1486824660000,"doc_count":12},{"key_as_string":"2017-02-11T14:52:00.000Z","key":1486824720000,"doc_count":11},{"key_as_string":"2017-02-11T14:53:00.000Z","key":1486824780000,"doc_count":16},{"key_as_string":"2017-02-11T14:54:00.000Z","key":1486824840000,"doc_count":12},{"key_as_string":"2017-02-11T14:55:00.000Z","key":1486824900000,"doc_count":11},{"key_as_string":"2017-02-11T14:56:00.000Z","key":1486824960000,"doc_count":14},{"key_as_string":"2017-02-11T14:57:00.000Z","key":1486825020000,"doc_count":15},{"key_as_string":"2017-02-11T14:58:00.000Z","key":1486825080000,"doc_count":10},{"key_as_string":"2017-02-11T14:59:00.000Z","key":1486825140000,"doc_count":18},{"key_as_string":"2017-02-11T15:00:00.000Z","key":1486825200000,"doc_count":15},{"key_as_string":"2017-02-11T15:01:00.000Z","key":1486825260000,"doc_count":10},{"key_as_string":"2017-02-11T15:02:00.000Z","key":1486825320000,"doc_count":17},{"key_as_string":"2017-02-11T15:03:00.000Z","key":1486825380000,"doc_count":12},{"key_as_string":"2017-02-11T15:04:00.000Z","key":1486825440000,"doc_count":13},{"key_as_string":"2017-02-11T15:05:00.000Z","key":1486825500000,"doc_count":14},{"key_as_string":"2017-02-11T15:06:00.000Z","key":1486825560000,"doc_count":10},{"key_as_string":"2017-02-11T15:07:00.000Z","key":1486825620000,"doc_count":13},{"key_as_string":"2017-02-11T15:08:00.000Z","key":1486825680000,"doc_count":12},{"key_as_string":"2017-02-11T15:09:00.000Z","key":1486825740000,"doc_count":13},{"key_as_string":"2017-02-11T15:10:00.000Z","key":1486825800000,"doc_count":11},{"key_as_string":"2017-02-11T15:11:00.000Z","key":1486825860000,"doc_count":16},{"key_as_string":"2017-02-11T15:12:00.000Z","key":1486825920000,"doc_count":10},{"key_as_string":"2017-02-11T15:13:00.000Z","key":1486825980000,"doc_count":11},{"key_as_string":"2017-02-11T15:14:00.000Z","key":1486826040000,"doc_count":18},{"key_as_string":"2017-02-11T15:15:00.000Z","key":1486826100000,"doc_count":15},{"key_as_string":"2017-02-11T15:16:00.000Z","key":1486826160000,"doc_count":12},{"key_as_string":"2017-02-11T15:17:00.000Z","key":1486826220000,"doc_count":18},{"key_as_string":"2017-02-11T15:18:00.000Z","key":1486826280000,"doc_count":11},{"key_as_string":"2017-02-11T15:19:00.000Z","key":1486826340000,"doc_count":9},{"key_as_string":"2017-02-11T15:20:00.000Z","key":1486826400000,"doc_count":19},{"key_as_string":"2017-02-11T15:21:00.000Z","key":1486826460000,"doc_count":11},{"key_as_string":"2017-02-11T15:22:00.000Z","key":1486826520000,"doc_count":9},{"key_as_string":"2017-02-11T15:23:00.000Z","key":1486826580000,"doc_count":16},{"key_as_string":"2017-02-11T15:24:00.000Z","key":1486826640000,"doc_count":14},{"key_as_string":"2017-02-11T15:25:00.000Z","key":1486826700000,"doc_count":17},{"key_as_string":"2017-02-11T15:26:00.000Z","key":1486826760000,"doc_count":14},{"key_as_string":"2017-02-11T15:27:00.000Z","key":1486826820000,"doc_count":17},{"key_as_string":"2017-02-11T15:28:00.000Z","key":1486826880000,"doc_count":10},{"key_as_string":"2017-02-11T15:29:00.000Z","key":1486826940000,"doc_count":20},{"key_as_string":"2017-02-11T15:30:00.000Z","key":1486827000000,"doc_count":7},{"key_as_string":"2017-02-11T15:31:00.000Z","key":1486827060000,"doc_count":12},{"key_as_string":"2017-02-11T15:32:00.000Z","key":1486827120000,"doc_count":12},{"key_as_string":"2017-02-11T15:33:00.000Z","key":1486827180000,"doc_count":10},{"key_as_string":"2017-02-11T15:34:00.000Z","key":1486827240000,"doc_count":16},{"key_as_string":"2017-02-11T15:35:00.000Z","key":1486827300000,"doc_count":13},{"key_as_string":"2017-02-11T15:36:00.000Z","key":1486827360000,"doc_count":9},{"key_as_string":"2017-02-11T15:37:00.000Z","key":1486827420000,"doc_count":12},{"key_as_string":"2017-02-11T15:38:00.000Z","key":1486827480000,"doc_count":17},{"key_as_string":"2017-02-11T15:39:00.000Z","key":1486827540000,"doc_count":11},{"key_as_string":"2017-02-11T15:40:00.000Z","key":1486827600000,"doc_count":17},{"key_as_string":"2017-02-11T15:41:00.000Z","key":1486827660000,"doc_count":12},{"key_as_string":"2017-02-11T15:42:00.000Z","key":1486827720000,"doc_count":11},{"key_as_string":"2017-02-11T15:43:00.000Z","key":1486827780000,"doc_count":10},{"key_as_string":"2017-02-11T15:44:00.000Z","key":1486827840000,"doc_count":15},{"key_as_string":"2017-02-11T15:45:00.000Z","key":1486827900000,"doc_count":11},{"key_as_string":"2017-02-11T15:46:00.000Z","key":1486827960000,"doc_count":19},{"key_as_string":"2017-02-11T15:47:00.000Z","key":1486828020000,"doc_count":12},{"key_as_string":"2017-02-11T15:48:00.000Z","key":1486828080000,"doc_count":9},{"key_as_string":"2017-02-11T15:49:00.000Z","key":1486828140000,"doc_count":12},{"key_as_string":"2017-02-11T15:50:00.000Z","key":1486828200000,"doc_count":15},{"key_as_string":"2017-02-11T15:51:00.000Z","key":1486828260000,"doc_count":11},{"key_as_string":"2017-02-11T15:52:00.000Z","key":1486828320000,"doc_count":19},{"key_as_string":"2017-02-11T15:53:00.000Z","key":1486828380000,"doc_count":8},{"key_as_string":"2017-02-11T15:54:00.000Z","key":1486828440000,"doc_count":14},{"key_as_string":"2017-02-11T15:55:00.000Z","key":1486828500000,"doc_count":22},{"key_as_string":"2017-02-11T15:56:00.000Z","key":1486828560000,"doc_count":12},{"key_as_string":"2017-02-11T15:57:00.000Z","key":1486828620000,"doc_count":15},{"key_as_string":"2017-02-11T15:58:00.000Z","key":1486828680000,"doc_count":15},{"key_as_string":"2017-02-11T15:59:00.000Z","key":1486828740000,"doc_count":12},{"key_as_string":"2017-02-11T16:00:00.000Z","key":1486828800000,"doc_count":13},{"key_as_string":"2017-02-11T16:01:00.000Z","key":1486828860000,"doc_count":19},{"key_as_string":"2017-02-11T16:02:00.000Z","key":1486828920000,"doc_count":18},{"key_as_string":"2017-02-11T16:03:00.000Z","key":1486828980000,"doc_count":11},{"key_as_string":"2017-02-11T16:04:00.000Z","key":1486829040000,"doc_count":12},{"key_as_string":"2017-02-11T16:05:00.000Z","key":1486829100000,"doc_count":14},{"key_as_string":"2017-02-11T16:06:00.000Z","key":1486829160000,"doc_count":18},{"key_as_string":"2017-02-11T16:07:00.000Z","key":1486829220000,"doc_count":14},{"key_as_string":"2017-02-11T16:08:00.000Z","key":1486829280000,"doc_count":13},{"key_as_string":"2017-02-11T16:09:00.000Z","key":1486829340000,"doc_count":12},{"key_as_string":"2017-02-11T16:10:00.000Z","key":1486829400000,"doc_count":8},{"key_as_string":"2017-02-11T16:11:00.000Z","key":1486829460000,"doc_count":12},{"key_as_string":"2017-02-11T16:12:00.000Z","key":1486829520000,"doc_count":18},{"key_as_string":"2017-02-11T16:13:00.000Z","key":1486829580000,"doc_count":11},{"key_as_string":"2017-02-11T16:14:00.000Z","key":1486829640000,"doc_count":13},{"key_as_string":"2017-02-11T16:15:00.000Z","key":1486829700000,"doc_count":9},{"key_as_string":"2017-02-11T16:16:00.000Z","key":1486829760000,"doc_count":12},{"key_as_string":"2017-02-11T16:17:00.000Z","key":1486829820000,"doc_count":8},{"key_as_string":"2017-02-11T16:18:00.000Z","key":1486829880000,"doc_count":15},{"key_as_string":"2017-02-11T16:19:00.000Z","key":1486829940000,"doc_count":12},{"key_as_string":"2017-02-11T16:20:00.000Z","key":1486830000000,"doc_count":18},{"key_as_string":"2017-02-11T16:21:00.000Z","key":1486830060000,"doc_count":12},{"key_as_string":"2017-02-11T16:22:00.000Z","key":1486830120000,"doc_count":15},{"key_as_string":"2017-02-11T16:23:00.000Z","key":1486830180000,"doc_count":9},{"key_as_string":"2017-02-11T16:24:00.000Z","key":1486830240000,"doc_count":19},{"key_as_string":"2017-02-11T16:25:00.000Z","key":1486830300000,"doc_count":10},{"key_as_string":"2017-02-11T16:26:00.000Z","key":1486830360000,"doc_count":13},{"key_as_string":"2017-02-11T16:27:00.000Z","key":1486830420000,"doc_count":11},{"key_as_string":"2017-02-11T16:28:00.000Z","key":1486830480000,"doc_count":8},{"key_as_string":"2017-02-11T16:29:00.000Z","key":1486830540000,"doc_count":16},{"key_as_string":"2017-02-11T16:30:00.000Z","key":1486830600000,"doc_count":14},{"key_as_string":"2017-02-11T16:31:00.000Z","key":1486830660000,"doc_count":12},{"key_as_string":"2017-02-11T16:32:00.000Z","key":1486830720000,"doc_count":6},{"key_as_string":"2017-02-11T16:33:00.000Z","key":1486830780000,"doc_count":13},{"key_as_string":"2017-02-11T16:34:00.000Z","key":1486830840000,"doc_count":12},{"key_as_string":"2017-02-11T16:35:00.000Z","key":1486830900000,"doc_count":15},{"key_as_string":"2017-02-11T16:36:00.000Z","key":1486830960000,"doc_count":8},{"key_as_string":"2017-02-11T16:37:00.000Z","key":1486831020000,"doc_count":11},{"key_as_string":"2017-02-11T16:38:00.000Z","key":1486831080000,"doc_count":17},{"key_as_string":"2017-02-11T16:39:00.000Z","key":1486831140000,"doc_count":11},{"key_as_string":"2017-02-11T16:40:00.000Z","key":1486831200000,"doc_count":15},{"key_as_string":"2017-02-11T16:41:00.000Z","key":1486831260000,"doc_count":15},{"key_as_string":"2017-02-11T16:42:00.000Z","key":1486831320000,"doc_count":14},{"key_as_string":"2017-02-11T16:43:00.000Z","key":1486831380000,"doc_count":11},{"key_as_string":"2017-02-11T16:44:00.000Z","key":1486831440000,"doc_count":13},{"key_as_string":"2017-02-11T16:45:00.000Z","key":1486831500000,"doc_count":12},{"key_as_string":"2017-02-11T16:46:00.000Z","key":1486831560000,"doc_count":14},{"key_as_string":"2017-02-11T16:47:00.000Z","key":1486831620000,"doc_count":10},{"key_as_string":"2017-02-11T16:48:00.000Z","key":1486831680000,"doc_count":11},{"key_as_string":"2017-02-11T16:49:00.000Z","key":1486831740000,"doc_count":14},{"key_as_string":"2017-02-11T16:50:00.000Z","key":1486831800000,"doc_count":16},{"key_as_string":"2017-02-11T16:51:00.000Z","key":1486831860000,"doc_count":11},{"key_as_string":"2017-02-11T16:52:00.000Z","key":1486831920000,"doc_count":9},{"key_as_string":"2017-02-11T16:53:00.000Z","key":1486831980000,"doc_count":13},{"key_as_string":"2017-02-11T16:54:00.000Z","key":1486832040000,"doc_count":13},{"key_as_string":"2017-02-11T16:55:00.000Z","key":1486832100000,"doc_count":16},{"key_as_string":"2017-02-11T16:56:00.000Z","key":1486832160000,"doc_count":10},{"key_as_string":"2017-02-11T16:57:00.000Z","key":1486832220000,"doc_count":9},{"key_as_string":"2017-02-11T16:58:00.000Z","key":1486832280000,"doc_count":22},{"key_as_string":"2017-02-11T16:59:00.000Z","key":1486832340000,"doc_count":12},{"key_as_string":"2017-02-11T17:00:00.000Z","key":1486832400000,"doc_count":8},{"key_as_string":"2017-02-11T17:01:00.000Z","key":1486832460000,"doc_count":13},{"key_as_string":"2017-02-11T17:02:00.000Z","key":1486832520000,"doc_count":13},{"key_as_string":"2017-02-11T17:03:00.000Z","key":1486832580000,"doc_count":9},{"key_as_string":"2017-02-11T17:04:00.000Z","key":1486832640000,"doc_count":11},{"key_as_string":"2017-02-11T17:05:00.000Z","key":1486832700000,"doc_count":10},{"key_as_string":"2017-02-11T17:06:00.000Z","key":1486832760000,"doc_count":15},{"key_as_string":"2017-02-11T17:07:00.000Z","key":1486832820000,"doc_count":12},{"key_as_string":"2017-02-11T17:08:00.000Z","key":1486832880000,"doc_count":10},{"key_as_string":"2017-02-11T17:09:00.000Z","key":1486832940000,"doc_count":16},{"key_as_string":"2017-02-11T17:10:00.000Z","key":1486833000000,"doc_count":14},{"key_as_string":"2017-02-11T17:11:00.000Z","key":1486833060000,"doc_count":13},{"key_as_string":"2017-02-11T17:12:00.000Z","key":1486833120000,"doc_count":16},{"key_as_string":"2017-02-11T17:13:00.000Z","key":1486833180000,"doc_count":9},{"key_as_string":"2017-02-11T17:14:00.000Z","key":1486833240000,"doc_count":5},{"key_as_string":"2017-02-11T17:15:00.000Z","key":1486833300000,"doc_count":15},{"key_as_string":"2017-02-11T17:16:00.000Z","key":1486833360000,"doc_count":14},{"key_as_string":"2017-02-11T17:17:00.000Z","key":1486833420000,"doc_count":8},{"key_as_string":"2017-02-11T17:18:00.000Z","key":1486833480000,"doc_count":12},{"key_as_string":"2017-02-11T17:19:00.000Z","key":1486833540000,"doc_count":13},{"key_as_string":"2017-02-11T17:20:00.000Z","key":1486833600000,"doc_count":13},{"key_as_string":"2017-02-11T17:21:00.000Z","key":1486833660000,"doc_count":13},{"key_as_string":"2017-02-11T17:22:00.000Z","key":1486833720000,"doc_count":11},{"key_as_string":"2017-02-11T17:23:00.000Z","key":1486833780000,"doc_count":11},{"key_as_string":"2017-02-11T17:24:00.000Z","key":1486833840000,"doc_count":14},{"key_as_string":"2017-02-11T17:25:00.000Z","key":1486833900000,"doc_count":7},{"key_as_string":"2017-02-11T17:26:00.000Z","key":1486833960000,"doc_count":15},{"key_as_string":"2017-02-11T17:27:00.000Z","key":1486834020000,"doc_count":11},{"key_as_string":"2017-02-11T17:28:00.000Z","key":1486834080000,"doc_count":9},{"key_as_string":"2017-02-11T17:29:00.000Z","key":1486834140000,"doc_count":13},{"key_as_string":"2017-02-11T17:30:00.000Z","key":1486834200000,"doc_count":11},{"key_as_string":"2017-02-11T17:31:00.000Z","key":1486834260000,"doc_count":7},{"key_as_string":"2017-02-11T17:32:00.000Z","key":1486834320000,"doc_count":14},{"key_as_string":"2017-02-11T17:33:00.000Z","key":1486834380000,"doc_count":13},{"key_as_string":"2017-02-11T17:34:00.000Z","key":1486834440000,"doc_count":10},{"key_as_string":"2017-02-11T17:35:00.000Z","key":1486834500000,"doc_count":13},{"key_as_string":"2017-02-11T17:36:00.000Z","key":1486834560000,"doc_count":12},{"key_as_string":"2017-02-11T17:37:00.000Z","key":1486834620000,"doc_count":13},{"key_as_string":"2017-02-11T17:38:00.000Z","key":1486834680000,"doc_count":12},{"key_as_string":"2017-02-11T17:39:00.000Z","key":1486834740000,"doc_count":11},{"key_as_string":"2017-02-11T17:40:00.000Z","key":1486834800000,"doc_count":15},{"key_as_string":"2017-02-11T17:41:00.000Z","key":1486834860000,"doc_count":15},{"key_as_string":"2017-02-11T17:42:00.000Z","key":1486834920000,"doc_count":14},{"key_as_string":"2017-02-11T17:43:00.000Z","key":1486834980000,"doc_count":10},{"key_as_string":"2017-02-11T17:44:00.000Z","key":1486835040000,"doc_count":8},{"key_as_string":"2017-02-11T17:45:00.000Z","key":1486835100000,"doc_count":14},{"key_as_string":"2017-02-11T17:46:00.000Z","key":1486835160000,"doc_count":8},{"key_as_string":"2017-02-11T17:47:00.000Z","key":1486835220000,"doc_count":11},{"key_as_string":"2017-02-11T17:48:00.000Z","key":1486835280000,"doc_count":13},{"key_as_string":"2017-02-11T17:49:00.000Z","key":1486835340000,"doc_count":13},{"key_as_string":"2017-02-11T17:50:00.000Z","key":1486835400000,"doc_count":10},{"key_as_string":"2017-02-11T17:51:00.000Z","key":1486835460000,"doc_count":11},{"key_as_string":"2017-02-11T17:52:00.000Z","key":1486835520000,"doc_count":22},{"key_as_string":"2017-02-11T17:53:00.000Z","key":1486835580000,"doc_count":14},{"key_as_string":"2017-02-11T17:54:00.000Z","key":1486835640000,"doc_count":11},{"key_as_string":"2017-02-11T17:55:00.000Z","key":1486835700000,"doc_count":11},{"key_as_string":"2017-02-11T17:56:00.000Z","key":1486835760000,"doc_count":10},{"key_as_string":"2017-02-11T17:57:00.000Z","key":1486835820000,"doc_count":13},{"key_as_string":"2017-02-11T17:58:00.000Z","key":1486835880000,"doc_count":6},{"key_as_string":"2017-02-11T17:59:00.000Z","key":1486835940000,"doc_count":16},{"key_as_string":"2017-02-11T18:00:00.000Z","key":1486836000000,"doc_count":11},{"key_as_string":"2017-02-11T18:01:00.000Z","key":1486836060000,"doc_count":15},{"key_as_string":"2017-02-11T18:02:00.000Z","key":1486836120000,"doc_count":8},{"key_as_string":"2017-02-11T18:03:00.000Z","key":1486836180000,"doc_count":12},{"key_as_string":"2017-02-11T18:04:00.000Z","key":1486836240000,"doc_count":14},{"key_as_string":"2017-02-11T18:05:00.000Z","key":1486836300000,"doc_count":11},{"key_as_string":"2017-02-11T18:06:00.000Z","key":1486836360000,"doc_count":15},{"key_as_string":"2017-02-11T18:07:00.000Z","key":1486836420000,"doc_count":9},{"key_as_string":"2017-02-11T18:08:00.000Z","key":1486836480000,"doc_count":11},{"key_as_string":"2017-02-11T18:09:00.000Z","key":1486836540000,"doc_count":9},{"key_as_string":"2017-02-11T18:10:00.000Z","key":1486836600000,"doc_count":10},{"key_as_string":"2017-02-11T18:11:00.000Z","key":1486836660000,"doc_count":12},{"key_as_string":"2017-02-11T18:12:00.000Z","key":1486836720000,"doc_count":10},{"key_as_string":"2017-02-11T18:13:00.000Z","key":1486836780000,"doc_count":11},{"key_as_string":"2017-02-11T18:14:00.000Z","key":1486836840000,"doc_count":12},{"key_as_string":"2017-02-11T18:15:00.000Z","key":1486836900000,"doc_count":11},{"key_as_string":"2017-02-11T18:16:00.000Z","key":1486836960000,"doc_count":9},{"key_as_string":"2017-02-11T18:17:00.000Z","key":1486837020000,"doc_count":7},{"key_as_string":"2017-02-11T18:18:00.000Z","key":1486837080000,"doc_count":16},{"key_as_string":"2017-02-11T18:19:00.000Z","key":1486837140000,"doc_count":10},{"key_as_string":"2017-02-11T18:20:00.000Z","key":1486837200000,"doc_count":14},{"key_as_string":"2017-02-11T18:21:00.000Z","key":1486837260000,"doc_count":5},{"key_as_string":"2017-02-11T18:22:00.000Z","key":1486837320000,"doc_count":12},{"key_as_string":"2017-02-11T18:23:00.000Z","key":1486837380000,"doc_count":13},{"key_as_string":"2017-02-11T18:24:00.000Z","key":1486837440000,"doc_count":12},{"key_as_string":"2017-02-11T18:25:00.000Z","key":1486837500000,"doc_count":9},{"key_as_string":"2017-02-11T18:26:00.000Z","key":1486837560000,"doc_count":12},{"key_as_string":"2017-02-11T18:27:00.000Z","key":1486837620000,"doc_count":11},{"key_as_string":"2017-02-11T18:28:00.000Z","key":1486837680000,"doc_count":12},{"key_as_string":"2017-02-11T18:29:00.000Z","key":1486837740000,"doc_count":10},{"key_as_string":"2017-02-11T18:30:00.000Z","key":1486837800000,"doc_count":11},{"key_as_string":"2017-02-11T18:31:00.000Z","key":1486837860000,"doc_count":9},{"key_as_string":"2017-02-11T18:32:00.000Z","key":1486837920000,"doc_count":13},{"key_as_string":"2017-02-11T18:33:00.000Z","key":1486837980000,"doc_count":11},{"key_as_string":"2017-02-11T18:34:00.000Z","key":1486838040000,"doc_count":13},{"key_as_string":"2017-02-11T18:35:00.000Z","key":1486838100000,"doc_count":12},{"key_as_string":"2017-02-11T18:36:00.000Z","key":1486838160000,"doc_count":8},{"key_as_string":"2017-02-11T18:37:00.000Z","key":1486838220000,"doc_count":10},{"key_as_string":"2017-02-11T18:38:00.000Z","key":1486838280000,"doc_count":10},{"key_as_string":"2017-02-11T18:39:00.000Z","key":1486838340000,"doc_count":10},{"key_as_string":"2017-02-11T18:40:00.000Z","key":1486838400000,"doc_count":14},{"key_as_string":"2017-02-11T18:41:00.000Z","key":1486838460000,"doc_count":13},{"key_as_string":"2017-02-11T18:42:00.000Z","key":1486838520000,"doc_count":15},{"key_as_string":"2017-02-11T18:43:00.000Z","key":1486838580000,"doc_count":9},{"key_as_string":"2017-02-11T18:44:00.000Z","key":1486838640000,"doc_count":10},{"key_as_string":"2017-02-11T18:45:00.000Z","key":1486838700000,"doc_count":14},{"key_as_string":"2017-02-11T18:46:00.000Z","key":1486838760000,"doc_count":9},{"key_as_string":"2017-02-11T18:47:00.000Z","key":1486838820000,"doc_count":14},{"key_as_string":"2017-02-11T18:48:00.000Z","key":1486838880000,"doc_count":8},{"key_as_string":"2017-02-11T18:49:00.000Z","key":1486838940000,"doc_count":10},{"key_as_string":"2017-02-11T18:50:00.000Z","key":1486839000000,"doc_count":15},{"key_as_string":"2017-02-11T18:51:00.000Z","key":1486839060000,"doc_count":9},{"key_as_string":"2017-02-11T18:52:00.000Z","key":1486839120000,"doc_count":13},{"key_as_string":"2017-02-11T18:53:00.000Z","key":1486839180000,"doc_count":14},{"key_as_string":"2017-02-11T18:54:00.000Z","key":1486839240000,"doc_count":9},{"key_as_string":"2017-02-11T18:55:00.000Z","key":1486839300000,"doc_count":9},{"key_as_string":"2017-02-11T18:56:00.000Z","key":1486839360000,"doc_count":9},{"key_as_string":"2017-02-11T18:57:00.000Z","key":1486839420000,"doc_count":12},{"key_as_string":"2017-02-11T18:58:00.000Z","key":1486839480000,"doc_count":14},{"key_as_string":"2017-02-11T18:59:00.000Z","key":1486839540000,"doc_count":9},{"key_as_string":"2017-02-11T19:00:00.000Z","key":1486839600000,"doc_count":14},{"key_as_string":"2017-02-11T19:01:00.000Z","key":1486839660000,"doc_count":13},{"key_as_string":"2017-02-11T19:02:00.000Z","key":1486839720000,"doc_count":13},{"key_as_string":"2017-02-11T19:03:00.000Z","key":1486839780000,"doc_count":15},{"key_as_string":"2017-02-11T19:04:00.000Z","key":1486839840000,"doc_count":11},{"key_as_string":"2017-02-11T19:05:00.000Z","key":1486839900000,"doc_count":11},{"key_as_string":"2017-02-11T19:06:00.000Z","key":1486839960000,"doc_count":10},{"key_as_string":"2017-02-11T19:07:00.000Z","key":1486840020000,"doc_count":11},{"key_as_string":"2017-02-11T19:08:00.000Z","key":1486840080000,"doc_count":15},{"key_as_string":"2017-02-11T19:09:00.000Z","key":1486840140000,"doc_count":13},{"key_as_string":"2017-02-11T19:10:00.000Z","key":1486840200000,"doc_count":17},{"key_as_string":"2017-02-11T19:11:00.000Z","key":1486840260000,"doc_count":9},{"key_as_string":"2017-02-11T19:12:00.000Z","key":1486840320000,"doc_count":10},{"key_as_string":"2017-02-11T19:13:00.000Z","key":1486840380000,"doc_count":8},{"key_as_string":"2017-02-11T19:14:00.000Z","key":1486840440000,"doc_count":17},{"key_as_string":"2017-02-11T19:15:00.000Z","key":1486840500000,"doc_count":8},{"key_as_string":"2017-02-11T19:16:00.000Z","key":1486840560000,"doc_count":13},{"key_as_string":"2017-02-11T19:17:00.000Z","key":1486840620000,"doc_count":10},{"key_as_string":"2017-02-11T19:18:00.000Z","key":1486840680000,"doc_count":9},{"key_as_string":"2017-02-11T19:19:00.000Z","key":1486840740000,"doc_count":13},{"key_as_string":"2017-02-11T19:20:00.000Z","key":1486840800000,"doc_count":12},{"key_as_string":"2017-02-11T19:21:00.000Z","key":1486840860000,"doc_count":18},{"key_as_string":"2017-02-11T19:22:00.000Z","key":1486840920000,"doc_count":14},{"key_as_string":"2017-02-11T19:23:00.000Z","key":1486840980000,"doc_count":10},{"key_as_string":"2017-02-11T19:24:00.000Z","key":1486841040000,"doc_count":11},{"key_as_string":"2017-02-11T19:25:00.000Z","key":1486841100000,"doc_count":12},{"key_as_string":"2017-02-11T19:26:00.000Z","key":1486841160000,"doc_count":16},{"key_as_string":"2017-02-11T19:27:00.000Z","key":1486841220000,"doc_count":10},{"key_as_string":"2017-02-11T19:28:00.000Z","key":1486841280000,"doc_count":16},{"key_as_string":"2017-02-11T19:29:00.000Z","key":1486841340000,"doc_count":6},{"key_as_string":"2017-02-11T19:30:00.000Z","key":1486841400000,"doc_count":12},{"key_as_string":"2017-02-11T19:31:00.000Z","key":1486841460000,"doc_count":10},{"key_as_string":"2017-02-11T19:32:00.000Z","key":1486841520000,"doc_count":15},{"key_as_string":"2017-02-11T19:33:00.000Z","key":1486841580000,"doc_count":16},{"key_as_string":"2017-02-11T19:34:00.000Z","key":1486841640000,"doc_count":14},{"key_as_string":"2017-02-11T19:35:00.000Z","key":1486841700000,"doc_count":5},{"key_as_string":"2017-02-11T19:36:00.000Z","key":1486841760000,"doc_count":15},{"key_as_string":"2017-02-11T19:37:00.000Z","key":1486841820000,"doc_count":9},{"key_as_string":"2017-02-11T19:38:00.000Z","key":1486841880000,"doc_count":11},{"key_as_string":"2017-02-11T19:39:00.000Z","key":1486841940000,"doc_count":9},{"key_as_string":"2017-02-11T19:40:00.000Z","key":1486842000000,"doc_count":14},{"key_as_string":"2017-02-11T19:41:00.000Z","key":1486842060000,"doc_count":8},{"key_as_string":"2017-02-11T19:42:00.000Z","key":1486842120000,"doc_count":7},{"key_as_string":"2017-02-11T19:43:00.000Z","key":1486842180000,"doc_count":8},{"key_as_string":"2017-02-11T19:44:00.000Z","key":1486842240000,"doc_count":12},{"key_as_string":"2017-02-11T19:45:00.000Z","key":1486842300000,"doc_count":12},{"key_as_string":"2017-02-11T19:46:00.000Z","key":1486842360000,"doc_count":11},{"key_as_string":"2017-02-11T19:47:00.000Z","key":1486842420000,"doc_count":10},{"key_as_string":"2017-02-11T19:48:00.000Z","key":1486842480000,"doc_count":8},{"key_as_string":"2017-02-11T19:49:00.000Z","key":1486842540000,"doc_count":11},{"key_as_string":"2017-02-11T19:50:00.000Z","key":1486842600000,"doc_count":12},{"key_as_string":"2017-02-11T19:51:00.000Z","key":1486842660000,"doc_count":10},{"key_as_string":"2017-02-11T19:52:00.000Z","key":1486842720000,"doc_count":11},{"key_as_string":"2017-02-11T19:53:00.000Z","key":1486842780000,"doc_count":17},{"key_as_string":"2017-02-11T19:54:00.000Z","key":1486842840000,"doc_count":11},{"key_as_string":"2017-02-11T19:55:00.000Z","key":1486842900000,"doc_count":11},{"key_as_string":"2017-02-11T19:56:00.000Z","key":1486842960000,"doc_count":14},{"key_as_string":"2017-02-11T19:57:00.000Z","key":1486843020000,"doc_count":5},{"key_as_string":"2017-02-11T19:58:00.000Z","key":1486843080000,"doc_count":17},{"key_as_string":"2017-02-11T19:59:00.000Z","key":1486843140000,"doc_count":7},{"key_as_string":"2017-02-11T20:00:00.000Z","key":1486843200000,"doc_count":12},{"key_as_string":"2017-02-11T20:01:00.000Z","key":1486843260000,"doc_count":12},{"key_as_string":"2017-02-11T20:02:00.000Z","key":1486843320000,"doc_count":15},{"key_as_string":"2017-02-11T20:03:00.000Z","key":1486843380000,"doc_count":8},{"key_as_string":"2017-02-11T20:04:00.000Z","key":1486843440000,"doc_count":10},{"key_as_string":"2017-02-11T20:05:00.000Z","key":1486843500000,"doc_count":10},{"key_as_string":"2017-02-11T20:06:00.000Z","key":1486843560000,"doc_count":10},{"key_as_string":"2017-02-11T20:07:00.000Z","key":1486843620000,"doc_count":14},{"key_as_string":"2017-02-11T20:08:00.000Z","key":1486843680000,"doc_count":11},{"key_as_string":"2017-02-11T20:09:00.000Z","key":1486843740000,"doc_count":9},{"key_as_string":"2017-02-11T20:10:00.000Z","key":1486843800000,"doc_count":7},{"key_as_string":"2017-02-11T20:11:00.000Z","key":1486843860000,"doc_count":17},{"key_as_string":"2017-02-11T20:12:00.000Z","key":1486843920000,"doc_count":11},{"key_as_string":"2017-02-11T20:13:00.000Z","key":1486843980000,"doc_count":15},{"key_as_string":"2017-02-11T20:14:00.000Z","key":1486844040000,"doc_count":12},{"key_as_string":"2017-02-11T20:15:00.000Z","key":1486844100000,"doc_count":9},{"key_as_string":"2017-02-11T20:16:00.000Z","key":1486844160000,"doc_count":7},{"key_as_string":"2017-02-11T20:17:00.000Z","key":1486844220000,"doc_count":9},{"key_as_string":"2017-02-11T20:18:00.000Z","key":1486844280000,"doc_count":14},{"key_as_string":"2017-02-11T20:19:00.000Z","key":1486844340000,"doc_count":9},{"key_as_string":"2017-02-11T20:20:00.000Z","key":1486844400000,"doc_count":13},{"key_as_string":"2017-02-11T20:21:00.000Z","key":1486844460000,"doc_count":11},{"key_as_string":"2017-02-11T20:22:00.000Z","key":1486844520000,"doc_count":15},{"key_as_string":"2017-02-11T20:23:00.000Z","key":1486844580000,"doc_count":13},{"key_as_string":"2017-02-11T20:24:00.000Z","key":1486844640000,"doc_count":11},{"key_as_string":"2017-02-11T20:25:00.000Z","key":1486844700000,"doc_count":7},{"key_as_string":"2017-02-11T20:26:00.000Z","key":1486844760000,"doc_count":7},{"key_as_string":"2017-02-11T20:27:00.000Z","key":1486844820000,"doc_count":13},{"key_as_string":"2017-02-11T20:28:00.000Z","key":1486844880000,"doc_count":8},{"key_as_string":"2017-02-11T20:29:00.000Z","key":1486844940000,"doc_count":12},{"key_as_string":"2017-02-11T20:30:00.000Z","key":1486845000000,"doc_count":12},{"key_as_string":"2017-02-11T20:31:00.000Z","key":1486845060000,"doc_count":11},{"key_as_string":"2017-02-11T20:32:00.000Z","key":1486845120000,"doc_count":11},{"key_as_string":"2017-02-11T20:33:00.000Z","key":1486845180000,"doc_count":10},{"key_as_string":"2017-02-11T20:34:00.000Z","key":1486845240000,"doc_count":15},{"key_as_string":"2017-02-11T20:35:00.000Z","key":1486845300000,"doc_count":5},{"key_as_string":"2017-02-11T20:36:00.000Z","key":1486845360000,"doc_count":9},{"key_as_string":"2017-02-11T20:37:00.000Z","key":1486845420000,"doc_count":11},{"key_as_string":"2017-02-11T20:38:00.000Z","key":1486845480000,"doc_count":9},{"key_as_string":"2017-02-11T20:39:00.000Z","key":1486845540000,"doc_count":12},{"key_as_string":"2017-02-11T20:40:00.000Z","key":1486845600000,"doc_count":16},{"key_as_string":"2017-02-11T20:41:00.000Z","key":1486845660000,"doc_count":6},{"key_as_string":"2017-02-11T20:42:00.000Z","key":1486845720000,"doc_count":11},{"key_as_string":"2017-02-11T20:43:00.000Z","key":1486845780000,"doc_count":13},{"key_as_string":"2017-02-11T20:44:00.000Z","key":1486845840000,"doc_count":7},{"key_as_string":"2017-02-11T20:45:00.000Z","key":1486845900000,"doc_count":11},{"key_as_string":"2017-02-11T20:46:00.000Z","key":1486845960000,"doc_count":7},{"key_as_string":"2017-02-11T20:47:00.000Z","key":1486846020000,"doc_count":9},{"key_as_string":"2017-02-11T20:48:00.000Z","key":1486846080000,"doc_count":14},{"key_as_string":"2017-02-11T20:49:00.000Z","key":1486846140000,"doc_count":9},{"key_as_string":"2017-02-11T20:50:00.000Z","key":1486846200000,"doc_count":12},{"key_as_string":"2017-02-11T20:51:00.000Z","key":1486846260000,"doc_count":17},{"key_as_string":"2017-02-11T20:52:00.000Z","key":1486846320000,"doc_count":13},{"key_as_string":"2017-02-11T20:53:00.000Z","key":1486846380000,"doc_count":7},{"key_as_string":"2017-02-11T20:54:00.000Z","key":1486846440000,"doc_count":15},{"key_as_string":"2017-02-11T20:55:00.000Z","key":1486846500000,"doc_count":13},{"key_as_string":"2017-02-11T20:56:00.000Z","key":1486846560000,"doc_count":16},{"key_as_string":"2017-02-11T20:57:00.000Z","key":1486846620000,"doc_count":12},{"key_as_string":"2017-02-11T20:58:00.000Z","key":1486846680000,"doc_count":8},{"key_as_string":"2017-02-11T20:59:00.000Z","key":1486846740000,"doc_count":14},{"key_as_string":"2017-02-11T21:00:00.000Z","key":1486846800000,"doc_count":11},{"key_as_string":"2017-02-11T21:01:00.000Z","key":1486846860000,"doc_count":8},{"key_as_string":"2017-02-11T21:02:00.000Z","key":1486846920000,"doc_count":13},{"key_as_string":"2017-02-11T21:03:00.000Z","key":1486846980000,"doc_count":8},{"key_as_string":"2017-02-11T21:04:00.000Z","key":1486847040000,"doc_count":9},{"key_as_string":"2017-02-11T21:05:00.000Z","key":1486847100000,"doc_count":12},{"key_as_string":"2017-02-11T21:06:00.000Z","key":1486847160000,"doc_count":11},{"key_as_string":"2017-02-11T21:07:00.000Z","key":1486847220000,"doc_count":15},{"key_as_string":"2017-02-11T21:08:00.000Z","key":1486847280000,"doc_count":6},{"key_as_string":"2017-02-11T21:09:00.000Z","key":1486847340000,"doc_count":14},{"key_as_string":"2017-02-11T21:10:00.000Z","key":1486847400000,"doc_count":5},{"key_as_string":"2017-02-11T21:11:00.000Z","key":1486847460000,"doc_count":11},{"key_as_string":"2017-02-11T21:12:00.000Z","key":1486847520000,"doc_count":12},{"key_as_string":"2017-02-11T21:13:00.000Z","key":1486847580000,"doc_count":10},{"key_as_string":"2017-02-11T21:14:00.000Z","key":1486847640000,"doc_count":7},{"key_as_string":"2017-02-11T21:15:00.000Z","key":1486847700000,"doc_count":9},{"key_as_string":"2017-02-11T21:16:00.000Z","key":1486847760000,"doc_count":12},{"key_as_string":"2017-02-11T21:17:00.000Z","key":1486847820000,"doc_count":11},{"key_as_string":"2017-02-11T21:18:00.000Z","key":1486847880000,"doc_count":12},{"key_as_string":"2017-02-11T21:19:00.000Z","key":1486847940000,"doc_count":14},{"key_as_string":"2017-02-11T21:20:00.000Z","key":1486848000000,"doc_count":10},{"key_as_string":"2017-02-11T21:21:00.000Z","key":1486848060000,"doc_count":9},{"key_as_string":"2017-02-11T21:22:00.000Z","key":1486848120000,"doc_count":16},{"key_as_string":"2017-02-11T21:23:00.000Z","key":1486848180000,"doc_count":10},{"key_as_string":"2017-02-11T21:24:00.000Z","key":1486848240000,"doc_count":10},{"key_as_string":"2017-02-11T21:25:00.000Z","key":1486848300000,"doc_count":11},{"key_as_string":"2017-02-11T21:26:00.000Z","key":1486848360000,"doc_count":12},{"key_as_string":"2017-02-11T21:27:00.000Z","key":1486848420000,"doc_count":6},{"key_as_string":"2017-02-11T21:28:00.000Z","key":1486848480000,"doc_count":14},{"key_as_string":"2017-02-11T21:29:00.000Z","key":1486848540000,"doc_count":10},{"key_as_string":"2017-02-11T21:30:00.000Z","key":1486848600000,"doc_count":12},{"key_as_string":"2017-02-11T21:31:00.000Z","key":1486848660000,"doc_count":7},{"key_as_string":"2017-02-11T21:32:00.000Z","key":1486848720000,"doc_count":11},{"key_as_string":"2017-02-11T21:33:00.000Z","key":1486848780000,"doc_count":11},{"key_as_string":"2017-02-11T21:34:00.000Z","key":1486848840000,"doc_count":11},{"key_as_string":"2017-02-11T21:35:00.000Z","key":1486848900000,"doc_count":5},{"key_as_string":"2017-02-11T21:36:00.000Z","key":1486848960000,"doc_count":15},{"key_as_string":"2017-02-11T21:37:00.000Z","key":1486849020000,"doc_count":8},{"key_as_string":"2017-02-11T21:38:00.000Z","key":1486849080000,"doc_count":11},{"key_as_string":"2017-02-11T21:39:00.000Z","key":1486849140000,"doc_count":9},{"key_as_string":"2017-02-11T21:40:00.000Z","key":1486849200000,"doc_count":9},{"key_as_string":"2017-02-11T21:41:00.000Z","key":1486849260000,"doc_count":10},{"key_as_string":"2017-02-11T21:42:00.000Z","key":1486849320000,"doc_count":7},{"key_as_string":"2017-02-11T21:43:00.000Z","key":1486849380000,"doc_count":13},{"key_as_string":"2017-02-11T21:44:00.000Z","key":1486849440000,"doc_count":10},{"key_as_string":"2017-02-11T21:45:00.000Z","key":1486849500000,"doc_count":10},{"key_as_string":"2017-02-11T21:46:00.000Z","key":1486849560000,"doc_count":11},{"key_as_string":"2017-02-11T21:47:00.000Z","key":1486849620000,"doc_count":10},{"key_as_string":"2017-02-11T21:48:00.000Z","key":1486849680000,"doc_count":7},{"key_as_string":"2017-02-11T21:49:00.000Z","key":1486849740000,"doc_count":8},{"key_as_string":"2017-02-11T21:50:00.000Z","key":1486849800000,"doc_count":12},{"key_as_string":"2017-02-11T21:51:00.000Z","key":1486849860000,"doc_count":8},{"key_as_string":"2017-02-11T21:52:00.000Z","key":1486849920000,"doc_count":7},{"key_as_string":"2017-02-11T21:53:00.000Z","key":1486849980000,"doc_count":15},{"key_as_string":"2017-02-11T21:54:00.000Z","key":1486850040000,"doc_count":14},{"key_as_string":"2017-02-11T21:55:00.000Z","key":1486850100000,"doc_count":9},{"key_as_string":"2017-02-11T21:56:00.000Z","key":1486850160000,"doc_count":9},{"key_as_string":"2017-02-11T21:57:00.000Z","key":1486850220000,"doc_count":9},{"key_as_string":"2017-02-11T21:58:00.000Z","key":1486850280000,"doc_count":13},{"key_as_string":"2017-02-11T21:59:00.000Z","key":1486850340000,"doc_count":13},{"key_as_string":"2017-02-11T22:00:00.000Z","key":1486850400000,"doc_count":11},{"key_as_string":"2017-02-11T22:01:00.000Z","key":1486850460000,"doc_count":12},{"key_as_string":"2017-02-11T22:02:00.000Z","key":1486850520000,"doc_count":12},{"key_as_string":"2017-02-11T22:03:00.000Z","key":1486850580000,"doc_count":11},{"key_as_string":"2017-02-11T22:04:00.000Z","key":1486850640000,"doc_count":7},{"key_as_string":"2017-02-11T22:05:00.000Z","key":1486850700000,"doc_count":9},{"key_as_string":"2017-02-11T22:06:00.000Z","key":1486850760000,"doc_count":12},{"key_as_string":"2017-02-11T22:07:00.000Z","key":1486850820000,"doc_count":13},{"key_as_string":"2017-02-11T22:08:00.000Z","key":1486850880000,"doc_count":11},{"key_as_string":"2017-02-11T22:09:00.000Z","key":1486850940000,"doc_count":8},{"key_as_string":"2017-02-11T22:10:00.000Z","key":1486851000000,"doc_count":6},{"key_as_string":"2017-02-11T22:11:00.000Z","key":1486851060000,"doc_count":9},{"key_as_string":"2017-02-11T22:12:00.000Z","key":1486851120000,"doc_count":15},{"key_as_string":"2017-02-11T22:13:00.000Z","key":1486851180000,"doc_count":12},{"key_as_string":"2017-02-11T22:14:00.000Z","key":1486851240000,"doc_count":12},{"key_as_string":"2017-02-11T22:15:00.000Z","key":1486851300000,"doc_count":9},{"key_as_string":"2017-02-11T22:16:00.000Z","key":1486851360000,"doc_count":11},{"key_as_string":"2017-02-11T22:17:00.000Z","key":1486851420000,"doc_count":9},{"key_as_string":"2017-02-11T22:18:00.000Z","key":1486851480000,"doc_count":13},{"key_as_string":"2017-02-11T22:19:00.000Z","key":1486851540000,"doc_count":10},{"key_as_string":"2017-02-11T22:20:00.000Z","key":1486851600000,"doc_count":16},{"key_as_string":"2017-02-11T22:21:00.000Z","key":1486851660000,"doc_count":10},{"key_as_string":"2017-02-11T22:22:00.000Z","key":1486851720000,"doc_count":7},{"key_as_string":"2017-02-11T22:23:00.000Z","key":1486851780000,"doc_count":8},{"key_as_string":"2017-02-11T22:24:00.000Z","key":1486851840000,"doc_count":13},{"key_as_string":"2017-02-11T22:25:00.000Z","key":1486851900000,"doc_count":11},{"key_as_string":"2017-02-11T22:26:00.000Z","key":1486851960000,"doc_count":11},{"key_as_string":"2017-02-11T22:27:00.000Z","key":1486852020000,"doc_count":15},{"key_as_string":"2017-02-11T22:28:00.000Z","key":1486852080000,"doc_count":10},{"key_as_string":"2017-02-11T22:29:00.000Z","key":1486852140000,"doc_count":13},{"key_as_string":"2017-02-11T22:30:00.000Z","key":1486852200000,"doc_count":3},{"key_as_string":"2017-02-11T22:31:00.000Z","key":1486852260000,"doc_count":16},{"key_as_string":"2017-02-11T22:32:00.000Z","key":1486852320000,"doc_count":8},{"key_as_string":"2017-02-11T22:33:00.000Z","key":1486852380000,"doc_count":13},{"key_as_string":"2017-02-11T22:34:00.000Z","key":1486852440000,"doc_count":10},{"key_as_string":"2017-02-11T22:35:00.000Z","key":1486852500000,"doc_count":10},{"key_as_string":"2017-02-11T22:36:00.000Z","key":1486852560000,"doc_count":15},{"key_as_string":"2017-02-11T22:37:00.000Z","key":1486852620000,"doc_count":10},{"key_as_string":"2017-02-11T22:38:00.000Z","key":1486852680000,"doc_count":9},{"key_as_string":"2017-02-11T22:39:00.000Z","key":1486852740000,"doc_count":11},{"key_as_string":"2017-02-11T22:40:00.000Z","key":1486852800000,"doc_count":11},{"key_as_string":"2017-02-11T22:41:00.000Z","key":1486852860000,"doc_count":8},{"key_as_string":"2017-02-11T22:42:00.000Z","key":1486852920000,"doc_count":10},{"key_as_string":"2017-02-11T22:43:00.000Z","key":1486852980000,"doc_count":12},{"key_as_string":"2017-02-11T22:44:00.000Z","key":1486853040000,"doc_count":8},{"key_as_string":"2017-02-11T22:45:00.000Z","key":1486853100000,"doc_count":14},{"key_as_string":"2017-02-11T22:46:00.000Z","key":1486853160000,"doc_count":9},{"key_as_string":"2017-02-11T22:47:00.000Z","key":1486853220000,"doc_count":10},{"key_as_string":"2017-02-11T22:48:00.000Z","key":1486853280000,"doc_count":12},{"key_as_string":"2017-02-11T22:49:00.000Z","key":1486853340000,"doc_count":13},{"key_as_string":"2017-02-11T22:50:00.000Z","key":1486853400000,"doc_count":13},{"key_as_string":"2017-02-11T22:51:00.000Z","key":1486853460000,"doc_count":5},{"key_as_string":"2017-02-11T22:52:00.000Z","key":1486853520000,"doc_count":10},{"key_as_string":"2017-02-11T22:53:00.000Z","key":1486853580000,"doc_count":8},{"key_as_string":"2017-02-11T22:54:00.000Z","key":1486853640000,"doc_count":12},{"key_as_string":"2017-02-11T22:55:00.000Z","key":1486853700000,"doc_count":6},{"key_as_string":"2017-02-11T22:56:00.000Z","key":1486853760000,"doc_count":15},{"key_as_string":"2017-02-11T22:57:00.000Z","key":1486853820000,"doc_count":8},{"key_as_string":"2017-02-11T22:58:00.000Z","key":1486853880000,"doc_count":11},{"key_as_string":"2017-02-11T22:59:00.000Z","key":1486853940000,"doc_count":10},{"key_as_string":"2017-02-11T23:00:00.000Z","key":1486854000000,"doc_count":8},{"key_as_string":"2017-02-11T23:01:00.000Z","key":1486854060000,"doc_count":9},{"key_as_string":"2017-02-11T23:02:00.000Z","key":1486854120000,"doc_count":11},{"key_as_string":"2017-02-11T23:03:00.000Z","key":1486854180000,"doc_count":15},{"key_as_string":"2017-02-11T23:04:00.000Z","key":1486854240000,"doc_count":9},{"key_as_string":"2017-02-11T23:05:00.000Z","key":1486854300000,"doc_count":14},{"key_as_string":"2017-02-11T23:06:00.000Z","key":1486854360000,"doc_count":8},{"key_as_string":"2017-02-11T23:07:00.000Z","key":1486854420000,"doc_count":10},{"key_as_string":"2017-02-11T23:08:00.000Z","key":1486854480000,"doc_count":8},{"key_as_string":"2017-02-11T23:09:00.000Z","key":1486854540000,"doc_count":13},{"key_as_string":"2017-02-11T23:10:00.000Z","key":1486854600000,"doc_count":11},{"key_as_string":"2017-02-11T23:11:00.000Z","key":1486854660000,"doc_count":12},{"key_as_string":"2017-02-11T23:12:00.000Z","key":1486854720000,"doc_count":11},{"key_as_string":"2017-02-11T23:13:00.000Z","key":1486854780000,"doc_count":6},{"key_as_string":"2017-02-11T23:14:00.000Z","key":1486854840000,"doc_count":14},{"key_as_string":"2017-02-11T23:15:00.000Z","key":1486854900000,"doc_count":14},{"key_as_string":"2017-02-11T23:16:00.000Z","key":1486854960000,"doc_count":14},{"key_as_string":"2017-02-11T23:17:00.000Z","key":1486855020000,"doc_count":11},{"key_as_string":"2017-02-11T23:18:00.000Z","key":1486855080000,"doc_count":9},{"key_as_string":"2017-02-11T23:19:00.000Z","key":1486855140000,"doc_count":10},{"key_as_string":"2017-02-11T23:20:00.000Z","key":1486855200000,"doc_count":8},{"key_as_string":"2017-02-11T23:21:00.000Z","key":1486855260000,"doc_count":13},{"key_as_string":"2017-02-11T23:22:00.000Z","key":1486855320000,"doc_count":12},{"key_as_string":"2017-02-11T23:23:00.000Z","key":1486855380000,"doc_count":10},{"key_as_string":"2017-02-11T23:24:00.000Z","key":1486855440000,"doc_count":13},{"key_as_string":"2017-02-11T23:25:00.000Z","key":1486855500000,"doc_count":11},{"key_as_string":"2017-02-11T23:26:00.000Z","key":1486855560000,"doc_count":11},{"key_as_string":"2017-02-11T23:27:00.000Z","key":1486855620000,"doc_count":9},{"key_as_string":"2017-02-11T23:28:00.000Z","key":1486855680000,"doc_count":13},{"key_as_string":"2017-02-11T23:29:00.000Z","key":1486855740000,"doc_count":3},{"key_as_string":"2017-02-11T23:30:00.000Z","key":1486855800000,"doc_count":10},{"key_as_string":"2017-02-11T23:31:00.000Z","key":1486855860000,"doc_count":9},{"key_as_string":"2017-02-11T23:32:00.000Z","key":1486855920000,"doc_count":7},{"key_as_string":"2017-02-11T23:33:00.000Z","key":1486855980000,"doc_count":15},{"key_as_string":"2017-02-11T23:34:00.000Z","key":1486856040000,"doc_count":12},{"key_as_string":"2017-02-11T23:35:00.000Z","key":1486856100000,"doc_count":17},{"key_as_string":"2017-02-11T23:36:00.000Z","key":1486856160000,"doc_count":9},{"key_as_string":"2017-02-11T23:37:00.000Z","key":1486856220000,"doc_count":8},{"key_as_string":"2017-02-11T23:38:00.000Z","key":1486856280000,"doc_count":9},{"key_as_string":"2017-02-11T23:39:00.000Z","key":1486856340000,"doc_count":14},{"key_as_string":"2017-02-11T23:40:00.000Z","key":1486856400000,"doc_count":9},{"key_as_string":"2017-02-11T23:41:00.000Z","key":1486856460000,"doc_count":7},{"key_as_string":"2017-02-11T23:42:00.000Z","key":1486856520000,"doc_count":10},{"key_as_string":"2017-02-11T23:43:00.000Z","key":1486856580000,"doc_count":7},{"key_as_string":"2017-02-11T23:44:00.000Z","key":1486856640000,"doc_count":12},{"key_as_string":"2017-02-11T23:45:00.000Z","key":1486856700000,"doc_count":10},{"key_as_string":"2017-02-11T23:46:00.000Z","key":1486856760000,"doc_count":10},{"key_as_string":"2017-02-11T23:47:00.000Z","key":1486856820000,"doc_count":9},{"key_as_string":"2017-02-11T23:48:00.000Z","key":1486856880000,"doc_count":7},{"key_as_string":"2017-02-11T23:49:00.000Z","key":1486856940000,"doc_count":9},{"key_as_string":"2017-02-11T23:50:00.000Z","key":1486857000000,"doc_count":11},{"key_as_string":"2017-02-11T23:51:00.000Z","key":1486857060000,"doc_count":10},{"key_as_string":"2017-02-11T23:52:00.000Z","key":1486857120000,"doc_count":11},{"key_as_string":"2017-02-11T23:53:00.000Z","key":1486857180000,"doc_count":10},{"key_as_string":"2017-02-11T23:54:00.000Z","key":1486857240000,"doc_count":15},{"key_as_string":"2017-02-11T23:55:00.000Z","key":1486857300000,"doc_count":5},{"key_as_string":"2017-02-11T23:56:00.000Z","key":1486857360000,"doc_count":7},{"key_as_string":"2017-02-11T23:57:00.000Z","key":1486857420000,"doc_count":14},{"key_as_string":"2017-02-11T23:58:00.000Z","key":1486857480000,"doc_count":10},{"key_as_string":"2017-02-11T23:59:00.000Z","key":1486857540000,"doc_count":9}]}}} +{"took":41,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value": 86274,"relation":"eq"},"max_score":0,"hits":[]},"aggregations":{"non_empty_buckets":{"buckets":[{"key_as_string":"2017-02-07T00:00:00.000Z","key":1486425600000,"doc_count":23},{"key_as_string":"2017-02-07T00:01:00.000Z","key":1486425660000,"doc_count":10},{"key_as_string":"2017-02-07T00:02:00.000Z","key":1486425720000,"doc_count":6},{"key_as_string":"2017-02-07T00:03:00.000Z","key":1486425780000,"doc_count":11},{"key_as_string":"2017-02-07T00:04:00.000Z","key":1486425840000,"doc_count":11},{"key_as_string":"2017-02-07T00:05:00.000Z","key":1486425900000,"doc_count":11},{"key_as_string":"2017-02-07T00:06:00.000Z","key":1486425960000,"doc_count":11},{"key_as_string":"2017-02-07T00:07:00.000Z","key":1486426020000,"doc_count":11},{"key_as_string":"2017-02-07T00:08:00.000Z","key":1486426080000,"doc_count":8},{"key_as_string":"2017-02-07T00:09:00.000Z","key":1486426140000,"doc_count":11},{"key_as_string":"2017-02-07T00:10:00.000Z","key":1486426200000,"doc_count":10},{"key_as_string":"2017-02-07T00:11:00.000Z","key":1486426260000,"doc_count":9},{"key_as_string":"2017-02-07T00:12:00.000Z","key":1486426320000,"doc_count":12},{"key_as_string":"2017-02-07T00:13:00.000Z","key":1486426380000,"doc_count":9},{"key_as_string":"2017-02-07T00:14:00.000Z","key":1486426440000,"doc_count":8},{"key_as_string":"2017-02-07T00:15:00.000Z","key":1486426500000,"doc_count":14},{"key_as_string":"2017-02-07T00:16:00.000Z","key":1486426560000,"doc_count":9},{"key_as_string":"2017-02-07T00:17:00.000Z","key":1486426620000,"doc_count":8},{"key_as_string":"2017-02-07T00:18:00.000Z","key":1486426680000,"doc_count":13},{"key_as_string":"2017-02-07T00:19:00.000Z","key":1486426740000,"doc_count":9},{"key_as_string":"2017-02-07T00:20:00.000Z","key":1486426800000,"doc_count":11},{"key_as_string":"2017-02-07T00:21:00.000Z","key":1486426860000,"doc_count":9},{"key_as_string":"2017-02-07T00:22:00.000Z","key":1486426920000,"doc_count":9},{"key_as_string":"2017-02-07T00:23:00.000Z","key":1486426980000,"doc_count":12},{"key_as_string":"2017-02-07T00:24:00.000Z","key":1486427040000,"doc_count":11},{"key_as_string":"2017-02-07T00:25:00.000Z","key":1486427100000,"doc_count":11},{"key_as_string":"2017-02-07T00:26:00.000Z","key":1486427160000,"doc_count":8},{"key_as_string":"2017-02-07T00:27:00.000Z","key":1486427220000,"doc_count":12},{"key_as_string":"2017-02-07T00:28:00.000Z","key":1486427280000,"doc_count":7},{"key_as_string":"2017-02-07T00:29:00.000Z","key":1486427340000,"doc_count":12},{"key_as_string":"2017-02-07T00:30:00.000Z","key":1486427400000,"doc_count":13},{"key_as_string":"2017-02-07T00:31:00.000Z","key":1486427460000,"doc_count":10},{"key_as_string":"2017-02-07T00:32:00.000Z","key":1486427520000,"doc_count":13},{"key_as_string":"2017-02-07T00:33:00.000Z","key":1486427580000,"doc_count":13},{"key_as_string":"2017-02-07T00:34:00.000Z","key":1486427640000,"doc_count":11},{"key_as_string":"2017-02-07T00:35:00.000Z","key":1486427700000,"doc_count":9},{"key_as_string":"2017-02-07T00:36:00.000Z","key":1486427760000,"doc_count":11},{"key_as_string":"2017-02-07T00:37:00.000Z","key":1486427820000,"doc_count":15},{"key_as_string":"2017-02-07T00:38:00.000Z","key":1486427880000,"doc_count":7},{"key_as_string":"2017-02-07T00:39:00.000Z","key":1486427940000,"doc_count":13},{"key_as_string":"2017-02-07T00:40:00.000Z","key":1486428000000,"doc_count":9},{"key_as_string":"2017-02-07T00:41:00.000Z","key":1486428060000,"doc_count":12},{"key_as_string":"2017-02-07T00:42:00.000Z","key":1486428120000,"doc_count":16},{"key_as_string":"2017-02-07T00:43:00.000Z","key":1486428180000,"doc_count":7},{"key_as_string":"2017-02-07T00:44:00.000Z","key":1486428240000,"doc_count":6},{"key_as_string":"2017-02-07T00:45:00.000Z","key":1486428300000,"doc_count":12},{"key_as_string":"2017-02-07T00:46:00.000Z","key":1486428360000,"doc_count":11},{"key_as_string":"2017-02-07T00:47:00.000Z","key":1486428420000,"doc_count":12},{"key_as_string":"2017-02-07T00:48:00.000Z","key":1486428480000,"doc_count":13},{"key_as_string":"2017-02-07T00:49:00.000Z","key":1486428540000,"doc_count":10},{"key_as_string":"2017-02-07T00:50:00.000Z","key":1486428600000,"doc_count":13},{"key_as_string":"2017-02-07T00:51:00.000Z","key":1486428660000,"doc_count":7},{"key_as_string":"2017-02-07T00:52:00.000Z","key":1486428720000,"doc_count":11},{"key_as_string":"2017-02-07T00:53:00.000Z","key":1486428780000,"doc_count":12},{"key_as_string":"2017-02-07T00:54:00.000Z","key":1486428840000,"doc_count":16},{"key_as_string":"2017-02-07T00:55:00.000Z","key":1486428900000,"doc_count":8},{"key_as_string":"2017-02-07T00:56:00.000Z","key":1486428960000,"doc_count":12},{"key_as_string":"2017-02-07T00:57:00.000Z","key":1486429020000,"doc_count":12},{"key_as_string":"2017-02-07T00:58:00.000Z","key":1486429080000,"doc_count":8},{"key_as_string":"2017-02-07T00:59:00.000Z","key":1486429140000,"doc_count":11},{"key_as_string":"2017-02-07T01:00:00.000Z","key":1486429200000,"doc_count":8},{"key_as_string":"2017-02-07T01:01:00.000Z","key":1486429260000,"doc_count":7},{"key_as_string":"2017-02-07T01:02:00.000Z","key":1486429320000,"doc_count":6},{"key_as_string":"2017-02-07T01:03:00.000Z","key":1486429380000,"doc_count":15},{"key_as_string":"2017-02-07T01:04:00.000Z","key":1486429440000,"doc_count":12},{"key_as_string":"2017-02-07T01:05:00.000Z","key":1486429500000,"doc_count":5},{"key_as_string":"2017-02-07T01:06:00.000Z","key":1486429560000,"doc_count":11},{"key_as_string":"2017-02-07T01:07:00.000Z","key":1486429620000,"doc_count":15},{"key_as_string":"2017-02-07T01:08:00.000Z","key":1486429680000,"doc_count":8},{"key_as_string":"2017-02-07T01:09:00.000Z","key":1486429740000,"doc_count":14},{"key_as_string":"2017-02-07T01:10:00.000Z","key":1486429800000,"doc_count":8},{"key_as_string":"2017-02-07T01:11:00.000Z","key":1486429860000,"doc_count":6},{"key_as_string":"2017-02-07T01:12:00.000Z","key":1486429920000,"doc_count":16},{"key_as_string":"2017-02-07T01:13:00.000Z","key":1486429980000,"doc_count":10},{"key_as_string":"2017-02-07T01:14:00.000Z","key":1486430040000,"doc_count":8},{"key_as_string":"2017-02-07T01:15:00.000Z","key":1486430100000,"doc_count":12},{"key_as_string":"2017-02-07T01:16:00.000Z","key":1486430160000,"doc_count":13},{"key_as_string":"2017-02-07T01:17:00.000Z","key":1486430220000,"doc_count":10},{"key_as_string":"2017-02-07T01:18:00.000Z","key":1486430280000,"doc_count":11},{"key_as_string":"2017-02-07T01:19:00.000Z","key":1486430340000,"doc_count":12},{"key_as_string":"2017-02-07T01:20:00.000Z","key":1486430400000,"doc_count":12},{"key_as_string":"2017-02-07T01:21:00.000Z","key":1486430460000,"doc_count":12},{"key_as_string":"2017-02-07T01:22:00.000Z","key":1486430520000,"doc_count":7},{"key_as_string":"2017-02-07T01:23:00.000Z","key":1486430580000,"doc_count":13},{"key_as_string":"2017-02-07T01:24:00.000Z","key":1486430640000,"doc_count":5},{"key_as_string":"2017-02-07T01:25:00.000Z","key":1486430700000,"doc_count":11},{"key_as_string":"2017-02-07T01:26:00.000Z","key":1486430760000,"doc_count":11},{"key_as_string":"2017-02-07T01:27:00.000Z","key":1486430820000,"doc_count":9},{"key_as_string":"2017-02-07T01:28:00.000Z","key":1486430880000,"doc_count":11},{"key_as_string":"2017-02-07T01:29:00.000Z","key":1486430940000,"doc_count":10},{"key_as_string":"2017-02-07T01:30:00.000Z","key":1486431000000,"doc_count":10},{"key_as_string":"2017-02-07T01:31:00.000Z","key":1486431060000,"doc_count":12},{"key_as_string":"2017-02-07T01:32:00.000Z","key":1486431120000,"doc_count":12},{"key_as_string":"2017-02-07T01:33:00.000Z","key":1486431180000,"doc_count":10},{"key_as_string":"2017-02-07T01:34:00.000Z","key":1486431240000,"doc_count":10},{"key_as_string":"2017-02-07T01:35:00.000Z","key":1486431300000,"doc_count":10},{"key_as_string":"2017-02-07T01:36:00.000Z","key":1486431360000,"doc_count":14},{"key_as_string":"2017-02-07T01:37:00.000Z","key":1486431420000,"doc_count":9},{"key_as_string":"2017-02-07T01:38:00.000Z","key":1486431480000,"doc_count":8},{"key_as_string":"2017-02-07T01:39:00.000Z","key":1486431540000,"doc_count":16},{"key_as_string":"2017-02-07T01:40:00.000Z","key":1486431600000,"doc_count":8},{"key_as_string":"2017-02-07T01:41:00.000Z","key":1486431660000,"doc_count":11},{"key_as_string":"2017-02-07T01:42:00.000Z","key":1486431720000,"doc_count":12},{"key_as_string":"2017-02-07T01:43:00.000Z","key":1486431780000,"doc_count":10},{"key_as_string":"2017-02-07T01:44:00.000Z","key":1486431840000,"doc_count":16},{"key_as_string":"2017-02-07T01:45:00.000Z","key":1486431900000,"doc_count":10},{"key_as_string":"2017-02-07T01:46:00.000Z","key":1486431960000,"doc_count":7},{"key_as_string":"2017-02-07T01:47:00.000Z","key":1486432020000,"doc_count":10},{"key_as_string":"2017-02-07T01:48:00.000Z","key":1486432080000,"doc_count":5},{"key_as_string":"2017-02-07T01:49:00.000Z","key":1486432140000,"doc_count":11},{"key_as_string":"2017-02-07T01:50:00.000Z","key":1486432200000,"doc_count":7},{"key_as_string":"2017-02-07T01:51:00.000Z","key":1486432260000,"doc_count":16},{"key_as_string":"2017-02-07T01:52:00.000Z","key":1486432320000,"doc_count":8},{"key_as_string":"2017-02-07T01:53:00.000Z","key":1486432380000,"doc_count":14},{"key_as_string":"2017-02-07T01:54:00.000Z","key":1486432440000,"doc_count":10},{"key_as_string":"2017-02-07T01:55:00.000Z","key":1486432500000,"doc_count":8},{"key_as_string":"2017-02-07T01:56:00.000Z","key":1486432560000,"doc_count":12},{"key_as_string":"2017-02-07T01:57:00.000Z","key":1486432620000,"doc_count":14},{"key_as_string":"2017-02-07T01:58:00.000Z","key":1486432680000,"doc_count":9},{"key_as_string":"2017-02-07T01:59:00.000Z","key":1486432740000,"doc_count":10},{"key_as_string":"2017-02-07T02:00:00.000Z","key":1486432800000,"doc_count":9},{"key_as_string":"2017-02-07T02:01:00.000Z","key":1486432860000,"doc_count":14},{"key_as_string":"2017-02-07T02:02:00.000Z","key":1486432920000,"doc_count":10},{"key_as_string":"2017-02-07T02:03:00.000Z","key":1486432980000,"doc_count":7},{"key_as_string":"2017-02-07T02:04:00.000Z","key":1486433040000,"doc_count":8},{"key_as_string":"2017-02-07T02:05:00.000Z","key":1486433100000,"doc_count":8},{"key_as_string":"2017-02-07T02:06:00.000Z","key":1486433160000,"doc_count":14},{"key_as_string":"2017-02-07T02:07:00.000Z","key":1486433220000,"doc_count":10},{"key_as_string":"2017-02-07T02:08:00.000Z","key":1486433280000,"doc_count":7},{"key_as_string":"2017-02-07T02:09:00.000Z","key":1486433340000,"doc_count":11},{"key_as_string":"2017-02-07T02:10:00.000Z","key":1486433400000,"doc_count":6},{"key_as_string":"2017-02-07T02:11:00.000Z","key":1486433460000,"doc_count":8},{"key_as_string":"2017-02-07T02:12:00.000Z","key":1486433520000,"doc_count":7},{"key_as_string":"2017-02-07T02:13:00.000Z","key":1486433580000,"doc_count":15},{"key_as_string":"2017-02-07T02:14:00.000Z","key":1486433640000,"doc_count":8},{"key_as_string":"2017-02-07T02:15:00.000Z","key":1486433700000,"doc_count":14},{"key_as_string":"2017-02-07T02:16:00.000Z","key":1486433760000,"doc_count":5},{"key_as_string":"2017-02-07T02:17:00.000Z","key":1486433820000,"doc_count":9},{"key_as_string":"2017-02-07T02:18:00.000Z","key":1486433880000,"doc_count":7},{"key_as_string":"2017-02-07T02:19:00.000Z","key":1486433940000,"doc_count":8},{"key_as_string":"2017-02-07T02:20:00.000Z","key":1486434000000,"doc_count":13},{"key_as_string":"2017-02-07T02:21:00.000Z","key":1486434060000,"doc_count":13},{"key_as_string":"2017-02-07T02:22:00.000Z","key":1486434120000,"doc_count":13},{"key_as_string":"2017-02-07T02:23:00.000Z","key":1486434180000,"doc_count":8},{"key_as_string":"2017-02-07T02:24:00.000Z","key":1486434240000,"doc_count":11},{"key_as_string":"2017-02-07T02:25:00.000Z","key":1486434300000,"doc_count":9},{"key_as_string":"2017-02-07T02:26:00.000Z","key":1486434360000,"doc_count":11},{"key_as_string":"2017-02-07T02:27:00.000Z","key":1486434420000,"doc_count":14},{"key_as_string":"2017-02-07T02:28:00.000Z","key":1486434480000,"doc_count":9},{"key_as_string":"2017-02-07T02:29:00.000Z","key":1486434540000,"doc_count":15},{"key_as_string":"2017-02-07T02:30:00.000Z","key":1486434600000,"doc_count":13},{"key_as_string":"2017-02-07T02:31:00.000Z","key":1486434660000,"doc_count":6},{"key_as_string":"2017-02-07T02:32:00.000Z","key":1486434720000,"doc_count":16},{"key_as_string":"2017-02-07T02:33:00.000Z","key":1486434780000,"doc_count":11},{"key_as_string":"2017-02-07T02:34:00.000Z","key":1486434840000,"doc_count":9},{"key_as_string":"2017-02-07T02:35:00.000Z","key":1486434900000,"doc_count":11},{"key_as_string":"2017-02-07T02:36:00.000Z","key":1486434960000,"doc_count":15},{"key_as_string":"2017-02-07T02:37:00.000Z","key":1486435020000,"doc_count":6},{"key_as_string":"2017-02-07T02:38:00.000Z","key":1486435080000,"doc_count":11},{"key_as_string":"2017-02-07T02:39:00.000Z","key":1486435140000,"doc_count":5},{"key_as_string":"2017-02-07T02:40:00.000Z","key":1486435200000,"doc_count":14},{"key_as_string":"2017-02-07T02:41:00.000Z","key":1486435260000,"doc_count":9},{"key_as_string":"2017-02-07T02:42:00.000Z","key":1486435320000,"doc_count":9},{"key_as_string":"2017-02-07T02:43:00.000Z","key":1486435380000,"doc_count":9},{"key_as_string":"2017-02-07T02:44:00.000Z","key":1486435440000,"doc_count":16},{"key_as_string":"2017-02-07T02:45:00.000Z","key":1486435500000,"doc_count":10},{"key_as_string":"2017-02-07T02:46:00.000Z","key":1486435560000,"doc_count":8},{"key_as_string":"2017-02-07T02:47:00.000Z","key":1486435620000,"doc_count":10},{"key_as_string":"2017-02-07T02:48:00.000Z","key":1486435680000,"doc_count":11},{"key_as_string":"2017-02-07T02:49:00.000Z","key":1486435740000,"doc_count":13},{"key_as_string":"2017-02-07T02:50:00.000Z","key":1486435800000,"doc_count":9},{"key_as_string":"2017-02-07T02:51:00.000Z","key":1486435860000,"doc_count":14},{"key_as_string":"2017-02-07T02:52:00.000Z","key":1486435920000,"doc_count":17},{"key_as_string":"2017-02-07T02:53:00.000Z","key":1486435980000,"doc_count":10},{"key_as_string":"2017-02-07T02:54:00.000Z","key":1486436040000,"doc_count":8},{"key_as_string":"2017-02-07T02:55:00.000Z","key":1486436100000,"doc_count":10},{"key_as_string":"2017-02-07T02:56:00.000Z","key":1486436160000,"doc_count":9},{"key_as_string":"2017-02-07T02:57:00.000Z","key":1486436220000,"doc_count":9},{"key_as_string":"2017-02-07T02:58:00.000Z","key":1486436280000,"doc_count":12},{"key_as_string":"2017-02-07T02:59:00.000Z","key":1486436340000,"doc_count":8},{"key_as_string":"2017-02-07T03:00:00.000Z","key":1486436400000,"doc_count":11},{"key_as_string":"2017-02-07T03:01:00.000Z","key":1486436460000,"doc_count":12},{"key_as_string":"2017-02-07T03:02:00.000Z","key":1486436520000,"doc_count":7},{"key_as_string":"2017-02-07T03:03:00.000Z","key":1486436580000,"doc_count":16},{"key_as_string":"2017-02-07T03:04:00.000Z","key":1486436640000,"doc_count":13},{"key_as_string":"2017-02-07T03:05:00.000Z","key":1486436700000,"doc_count":10},{"key_as_string":"2017-02-07T03:06:00.000Z","key":1486436760000,"doc_count":11},{"key_as_string":"2017-02-07T03:07:00.000Z","key":1486436820000,"doc_count":6},{"key_as_string":"2017-02-07T03:08:00.000Z","key":1486436880000,"doc_count":9},{"key_as_string":"2017-02-07T03:09:00.000Z","key":1486436940000,"doc_count":7},{"key_as_string":"2017-02-07T03:10:00.000Z","key":1486437000000,"doc_count":14},{"key_as_string":"2017-02-07T03:11:00.000Z","key":1486437060000,"doc_count":9},{"key_as_string":"2017-02-07T03:12:00.000Z","key":1486437120000,"doc_count":12},{"key_as_string":"2017-02-07T03:13:00.000Z","key":1486437180000,"doc_count":10},{"key_as_string":"2017-02-07T03:14:00.000Z","key":1486437240000,"doc_count":9},{"key_as_string":"2017-02-07T03:15:00.000Z","key":1486437300000,"doc_count":12},{"key_as_string":"2017-02-07T03:16:00.000Z","key":1486437360000,"doc_count":12},{"key_as_string":"2017-02-07T03:17:00.000Z","key":1486437420000,"doc_count":8},{"key_as_string":"2017-02-07T03:18:00.000Z","key":1486437480000,"doc_count":13},{"key_as_string":"2017-02-07T03:19:00.000Z","key":1486437540000,"doc_count":12},{"key_as_string":"2017-02-07T03:20:00.000Z","key":1486437600000,"doc_count":8},{"key_as_string":"2017-02-07T03:21:00.000Z","key":1486437660000,"doc_count":20},{"key_as_string":"2017-02-07T03:22:00.000Z","key":1486437720000,"doc_count":8},{"key_as_string":"2017-02-07T03:23:00.000Z","key":1486437780000,"doc_count":9},{"key_as_string":"2017-02-07T03:24:00.000Z","key":1486437840000,"doc_count":12},{"key_as_string":"2017-02-07T03:25:00.000Z","key":1486437900000,"doc_count":9},{"key_as_string":"2017-02-07T03:26:00.000Z","key":1486437960000,"doc_count":9},{"key_as_string":"2017-02-07T03:27:00.000Z","key":1486438020000,"doc_count":12},{"key_as_string":"2017-02-07T03:28:00.000Z","key":1486438080000,"doc_count":12},{"key_as_string":"2017-02-07T03:29:00.000Z","key":1486438140000,"doc_count":7},{"key_as_string":"2017-02-07T03:30:00.000Z","key":1486438200000,"doc_count":12},{"key_as_string":"2017-02-07T03:31:00.000Z","key":1486438260000,"doc_count":15},{"key_as_string":"2017-02-07T03:32:00.000Z","key":1486438320000,"doc_count":12},{"key_as_string":"2017-02-07T03:33:00.000Z","key":1486438380000,"doc_count":9},{"key_as_string":"2017-02-07T03:34:00.000Z","key":1486438440000,"doc_count":9},{"key_as_string":"2017-02-07T03:35:00.000Z","key":1486438500000,"doc_count":12},{"key_as_string":"2017-02-07T03:36:00.000Z","key":1486438560000,"doc_count":8},{"key_as_string":"2017-02-07T03:37:00.000Z","key":1486438620000,"doc_count":12},{"key_as_string":"2017-02-07T03:38:00.000Z","key":1486438680000,"doc_count":13},{"key_as_string":"2017-02-07T03:39:00.000Z","key":1486438740000,"doc_count":10},{"key_as_string":"2017-02-07T03:40:00.000Z","key":1486438800000,"doc_count":8},{"key_as_string":"2017-02-07T03:41:00.000Z","key":1486438860000,"doc_count":10},{"key_as_string":"2017-02-07T03:42:00.000Z","key":1486438920000,"doc_count":12},{"key_as_string":"2017-02-07T03:43:00.000Z","key":1486438980000,"doc_count":9},{"key_as_string":"2017-02-07T03:44:00.000Z","key":1486439040000,"doc_count":13},{"key_as_string":"2017-02-07T03:45:00.000Z","key":1486439100000,"doc_count":11},{"key_as_string":"2017-02-07T03:46:00.000Z","key":1486439160000,"doc_count":16},{"key_as_string":"2017-02-07T03:47:00.000Z","key":1486439220000,"doc_count":10},{"key_as_string":"2017-02-07T03:48:00.000Z","key":1486439280000,"doc_count":10},{"key_as_string":"2017-02-07T03:49:00.000Z","key":1486439340000,"doc_count":10},{"key_as_string":"2017-02-07T03:50:00.000Z","key":1486439400000,"doc_count":10},{"key_as_string":"2017-02-07T03:51:00.000Z","key":1486439460000,"doc_count":8},{"key_as_string":"2017-02-07T03:52:00.000Z","key":1486439520000,"doc_count":8},{"key_as_string":"2017-02-07T03:53:00.000Z","key":1486439580000,"doc_count":14},{"key_as_string":"2017-02-07T03:54:00.000Z","key":1486439640000,"doc_count":12},{"key_as_string":"2017-02-07T03:55:00.000Z","key":1486439700000,"doc_count":9},{"key_as_string":"2017-02-07T03:56:00.000Z","key":1486439760000,"doc_count":10},{"key_as_string":"2017-02-07T03:57:00.000Z","key":1486439820000,"doc_count":15},{"key_as_string":"2017-02-07T03:58:00.000Z","key":1486439880000,"doc_count":8},{"key_as_string":"2017-02-07T03:59:00.000Z","key":1486439940000,"doc_count":13},{"key_as_string":"2017-02-07T04:00:00.000Z","key":1486440000000,"doc_count":9},{"key_as_string":"2017-02-07T04:01:00.000Z","key":1486440060000,"doc_count":13},{"key_as_string":"2017-02-07T04:02:00.000Z","key":1486440120000,"doc_count":7},{"key_as_string":"2017-02-07T04:03:00.000Z","key":1486440180000,"doc_count":10},{"key_as_string":"2017-02-07T04:04:00.000Z","key":1486440240000,"doc_count":15},{"key_as_string":"2017-02-07T04:05:00.000Z","key":1486440300000,"doc_count":12},{"key_as_string":"2017-02-07T04:06:00.000Z","key":1486440360000,"doc_count":10},{"key_as_string":"2017-02-07T04:07:00.000Z","key":1486440420000,"doc_count":9},{"key_as_string":"2017-02-07T04:08:00.000Z","key":1486440480000,"doc_count":8},{"key_as_string":"2017-02-07T04:09:00.000Z","key":1486440540000,"doc_count":12},{"key_as_string":"2017-02-07T04:10:00.000Z","key":1486440600000,"doc_count":10},{"key_as_string":"2017-02-07T04:11:00.000Z","key":1486440660000,"doc_count":11},{"key_as_string":"2017-02-07T04:12:00.000Z","key":1486440720000,"doc_count":9},{"key_as_string":"2017-02-07T04:13:00.000Z","key":1486440780000,"doc_count":14},{"key_as_string":"2017-02-07T04:14:00.000Z","key":1486440840000,"doc_count":16},{"key_as_string":"2017-02-07T04:15:00.000Z","key":1486440900000,"doc_count":12},{"key_as_string":"2017-02-07T04:16:00.000Z","key":1486440960000,"doc_count":12},{"key_as_string":"2017-02-07T04:17:00.000Z","key":1486441020000,"doc_count":12},{"key_as_string":"2017-02-07T04:18:00.000Z","key":1486441080000,"doc_count":10},{"key_as_string":"2017-02-07T04:19:00.000Z","key":1486441140000,"doc_count":14},{"key_as_string":"2017-02-07T04:20:00.000Z","key":1486441200000,"doc_count":10},{"key_as_string":"2017-02-07T04:21:00.000Z","key":1486441260000,"doc_count":13},{"key_as_string":"2017-02-07T04:22:00.000Z","key":1486441320000,"doc_count":13},{"key_as_string":"2017-02-07T04:23:00.000Z","key":1486441380000,"doc_count":8},{"key_as_string":"2017-02-07T04:24:00.000Z","key":1486441440000,"doc_count":10},{"key_as_string":"2017-02-07T04:25:00.000Z","key":1486441500000,"doc_count":14},{"key_as_string":"2017-02-07T04:26:00.000Z","key":1486441560000,"doc_count":6},{"key_as_string":"2017-02-07T04:27:00.000Z","key":1486441620000,"doc_count":15},{"key_as_string":"2017-02-07T04:28:00.000Z","key":1486441680000,"doc_count":12},{"key_as_string":"2017-02-07T04:29:00.000Z","key":1486441740000,"doc_count":11},{"key_as_string":"2017-02-07T04:30:00.000Z","key":1486441800000,"doc_count":8},{"key_as_string":"2017-02-07T04:31:00.000Z","key":1486441860000,"doc_count":8},{"key_as_string":"2017-02-07T04:32:00.000Z","key":1486441920000,"doc_count":14},{"key_as_string":"2017-02-07T04:33:00.000Z","key":1486441980000,"doc_count":10},{"key_as_string":"2017-02-07T04:34:00.000Z","key":1486442040000,"doc_count":12},{"key_as_string":"2017-02-07T04:35:00.000Z","key":1486442100000,"doc_count":13},{"key_as_string":"2017-02-07T04:36:00.000Z","key":1486442160000,"doc_count":7},{"key_as_string":"2017-02-07T04:37:00.000Z","key":1486442220000,"doc_count":11},{"key_as_string":"2017-02-07T04:38:00.000Z","key":1486442280000,"doc_count":10},{"key_as_string":"2017-02-07T04:39:00.000Z","key":1486442340000,"doc_count":13},{"key_as_string":"2017-02-07T04:40:00.000Z","key":1486442400000,"doc_count":10},{"key_as_string":"2017-02-07T04:41:00.000Z","key":1486442460000,"doc_count":17},{"key_as_string":"2017-02-07T04:42:00.000Z","key":1486442520000,"doc_count":9},{"key_as_string":"2017-02-07T04:43:00.000Z","key":1486442580000,"doc_count":14},{"key_as_string":"2017-02-07T04:44:00.000Z","key":1486442640000,"doc_count":11},{"key_as_string":"2017-02-07T04:45:00.000Z","key":1486442700000,"doc_count":12},{"key_as_string":"2017-02-07T04:46:00.000Z","key":1486442760000,"doc_count":10},{"key_as_string":"2017-02-07T04:47:00.000Z","key":1486442820000,"doc_count":17},{"key_as_string":"2017-02-07T04:48:00.000Z","key":1486442880000,"doc_count":7},{"key_as_string":"2017-02-07T04:49:00.000Z","key":1486442940000,"doc_count":12},{"key_as_string":"2017-02-07T04:50:00.000Z","key":1486443000000,"doc_count":7},{"key_as_string":"2017-02-07T04:51:00.000Z","key":1486443060000,"doc_count":12},{"key_as_string":"2017-02-07T04:52:00.000Z","key":1486443120000,"doc_count":14},{"key_as_string":"2017-02-07T04:53:00.000Z","key":1486443180000,"doc_count":6},{"key_as_string":"2017-02-07T04:54:00.000Z","key":1486443240000,"doc_count":10},{"key_as_string":"2017-02-07T04:55:00.000Z","key":1486443300000,"doc_count":16},{"key_as_string":"2017-02-07T04:56:00.000Z","key":1486443360000,"doc_count":7},{"key_as_string":"2017-02-07T04:57:00.000Z","key":1486443420000,"doc_count":6},{"key_as_string":"2017-02-07T04:58:00.000Z","key":1486443480000,"doc_count":11},{"key_as_string":"2017-02-07T04:59:00.000Z","key":1486443540000,"doc_count":14},{"key_as_string":"2017-02-07T05:00:00.000Z","key":1486443600000,"doc_count":10},{"key_as_string":"2017-02-07T05:01:00.000Z","key":1486443660000,"doc_count":9},{"key_as_string":"2017-02-07T05:02:00.000Z","key":1486443720000,"doc_count":6},{"key_as_string":"2017-02-07T05:03:00.000Z","key":1486443780000,"doc_count":11},{"key_as_string":"2017-02-07T05:04:00.000Z","key":1486443840000,"doc_count":14},{"key_as_string":"2017-02-07T05:05:00.000Z","key":1486443900000,"doc_count":12},{"key_as_string":"2017-02-07T05:06:00.000Z","key":1486443960000,"doc_count":7},{"key_as_string":"2017-02-07T05:07:00.000Z","key":1486444020000,"doc_count":15},{"key_as_string":"2017-02-07T05:08:00.000Z","key":1486444080000,"doc_count":12},{"key_as_string":"2017-02-07T05:09:00.000Z","key":1486444140000,"doc_count":7},{"key_as_string":"2017-02-07T05:10:00.000Z","key":1486444200000,"doc_count":14},{"key_as_string":"2017-02-07T05:11:00.000Z","key":1486444260000,"doc_count":10},{"key_as_string":"2017-02-07T05:12:00.000Z","key":1486444320000,"doc_count":12},{"key_as_string":"2017-02-07T05:13:00.000Z","key":1486444380000,"doc_count":12},{"key_as_string":"2017-02-07T05:14:00.000Z","key":1486444440000,"doc_count":12},{"key_as_string":"2017-02-07T05:15:00.000Z","key":1486444500000,"doc_count":11},{"key_as_string":"2017-02-07T05:16:00.000Z","key":1486444560000,"doc_count":10},{"key_as_string":"2017-02-07T05:17:00.000Z","key":1486444620000,"doc_count":12},{"key_as_string":"2017-02-07T05:18:00.000Z","key":1486444680000,"doc_count":9},{"key_as_string":"2017-02-07T05:19:00.000Z","key":1486444740000,"doc_count":14},{"key_as_string":"2017-02-07T05:20:00.000Z","key":1486444800000,"doc_count":7},{"key_as_string":"2017-02-07T05:21:00.000Z","key":1486444860000,"doc_count":12},{"key_as_string":"2017-02-07T05:22:00.000Z","key":1486444920000,"doc_count":10},{"key_as_string":"2017-02-07T05:23:00.000Z","key":1486444980000,"doc_count":14},{"key_as_string":"2017-02-07T05:24:00.000Z","key":1486445040000,"doc_count":14},{"key_as_string":"2017-02-07T05:25:00.000Z","key":1486445100000,"doc_count":5},{"key_as_string":"2017-02-07T05:26:00.000Z","key":1486445160000,"doc_count":10},{"key_as_string":"2017-02-07T05:27:00.000Z","key":1486445220000,"doc_count":10},{"key_as_string":"2017-02-07T05:28:00.000Z","key":1486445280000,"doc_count":13},{"key_as_string":"2017-02-07T05:29:00.000Z","key":1486445340000,"doc_count":12},{"key_as_string":"2017-02-07T05:30:00.000Z","key":1486445400000,"doc_count":13},{"key_as_string":"2017-02-07T05:31:00.000Z","key":1486445460000,"doc_count":13},{"key_as_string":"2017-02-07T05:32:00.000Z","key":1486445520000,"doc_count":13},{"key_as_string":"2017-02-07T05:33:00.000Z","key":1486445580000,"doc_count":16},{"key_as_string":"2017-02-07T05:34:00.000Z","key":1486445640000,"doc_count":10},{"key_as_string":"2017-02-07T05:35:00.000Z","key":1486445700000,"doc_count":16},{"key_as_string":"2017-02-07T05:36:00.000Z","key":1486445760000,"doc_count":14},{"key_as_string":"2017-02-07T05:37:00.000Z","key":1486445820000,"doc_count":10},{"key_as_string":"2017-02-07T05:38:00.000Z","key":1486445880000,"doc_count":11},{"key_as_string":"2017-02-07T05:39:00.000Z","key":1486445940000,"doc_count":13},{"key_as_string":"2017-02-07T05:40:00.000Z","key":1486446000000,"doc_count":16},{"key_as_string":"2017-02-07T05:41:00.000Z","key":1486446060000,"doc_count":9},{"key_as_string":"2017-02-07T05:42:00.000Z","key":1486446120000,"doc_count":10},{"key_as_string":"2017-02-07T05:43:00.000Z","key":1486446180000,"doc_count":16},{"key_as_string":"2017-02-07T05:44:00.000Z","key":1486446240000,"doc_count":18},{"key_as_string":"2017-02-07T05:45:00.000Z","key":1486446300000,"doc_count":10},{"key_as_string":"2017-02-07T05:46:00.000Z","key":1486446360000,"doc_count":9},{"key_as_string":"2017-02-07T05:47:00.000Z","key":1486446420000,"doc_count":7},{"key_as_string":"2017-02-07T05:48:00.000Z","key":1486446480000,"doc_count":10},{"key_as_string":"2017-02-07T05:49:00.000Z","key":1486446540000,"doc_count":17},{"key_as_string":"2017-02-07T05:50:00.000Z","key":1486446600000,"doc_count":8},{"key_as_string":"2017-02-07T05:51:00.000Z","key":1486446660000,"doc_count":10},{"key_as_string":"2017-02-07T05:52:00.000Z","key":1486446720000,"doc_count":10},{"key_as_string":"2017-02-07T05:53:00.000Z","key":1486446780000,"doc_count":13},{"key_as_string":"2017-02-07T05:54:00.000Z","key":1486446840000,"doc_count":9},{"key_as_string":"2017-02-07T05:55:00.000Z","key":1486446900000,"doc_count":11},{"key_as_string":"2017-02-07T05:56:00.000Z","key":1486446960000,"doc_count":14},{"key_as_string":"2017-02-07T05:57:00.000Z","key":1486447020000,"doc_count":15},{"key_as_string":"2017-02-07T05:58:00.000Z","key":1486447080000,"doc_count":14},{"key_as_string":"2017-02-07T05:59:00.000Z","key":1486447140000,"doc_count":6},{"key_as_string":"2017-02-07T06:00:00.000Z","key":1486447200000,"doc_count":14},{"key_as_string":"2017-02-07T06:01:00.000Z","key":1486447260000,"doc_count":18},{"key_as_string":"2017-02-07T06:02:00.000Z","key":1486447320000,"doc_count":9},{"key_as_string":"2017-02-07T06:03:00.000Z","key":1486447380000,"doc_count":14},{"key_as_string":"2017-02-07T06:04:00.000Z","key":1486447440000,"doc_count":12},{"key_as_string":"2017-02-07T06:05:00.000Z","key":1486447500000,"doc_count":14},{"key_as_string":"2017-02-07T06:06:00.000Z","key":1486447560000,"doc_count":11},{"key_as_string":"2017-02-07T06:07:00.000Z","key":1486447620000,"doc_count":12},{"key_as_string":"2017-02-07T06:08:00.000Z","key":1486447680000,"doc_count":20},{"key_as_string":"2017-02-07T06:09:00.000Z","key":1486447740000,"doc_count":9},{"key_as_string":"2017-02-07T06:10:00.000Z","key":1486447800000,"doc_count":10},{"key_as_string":"2017-02-07T06:11:00.000Z","key":1486447860000,"doc_count":13},{"key_as_string":"2017-02-07T06:12:00.000Z","key":1486447920000,"doc_count":10},{"key_as_string":"2017-02-07T06:13:00.000Z","key":1486447980000,"doc_count":13},{"key_as_string":"2017-02-07T06:14:00.000Z","key":1486448040000,"doc_count":10},{"key_as_string":"2017-02-07T06:15:00.000Z","key":1486448100000,"doc_count":17},{"key_as_string":"2017-02-07T06:16:00.000Z","key":1486448160000,"doc_count":7},{"key_as_string":"2017-02-07T06:17:00.000Z","key":1486448220000,"doc_count":9},{"key_as_string":"2017-02-07T06:18:00.000Z","key":1486448280000,"doc_count":12},{"key_as_string":"2017-02-07T06:19:00.000Z","key":1486448340000,"doc_count":8},{"key_as_string":"2017-02-07T06:20:00.000Z","key":1486448400000,"doc_count":21},{"key_as_string":"2017-02-07T06:21:00.000Z","key":1486448460000,"doc_count":10},{"key_as_string":"2017-02-07T06:22:00.000Z","key":1486448520000,"doc_count":13},{"key_as_string":"2017-02-07T06:23:00.000Z","key":1486448580000,"doc_count":12},{"key_as_string":"2017-02-07T06:24:00.000Z","key":1486448640000,"doc_count":13},{"key_as_string":"2017-02-07T06:25:00.000Z","key":1486448700000,"doc_count":11},{"key_as_string":"2017-02-07T06:26:00.000Z","key":1486448760000,"doc_count":16},{"key_as_string":"2017-02-07T06:27:00.000Z","key":1486448820000,"doc_count":5},{"key_as_string":"2017-02-07T06:28:00.000Z","key":1486448880000,"doc_count":11},{"key_as_string":"2017-02-07T06:29:00.000Z","key":1486448940000,"doc_count":14},{"key_as_string":"2017-02-07T06:30:00.000Z","key":1486449000000,"doc_count":8},{"key_as_string":"2017-02-07T06:31:00.000Z","key":1486449060000,"doc_count":13},{"key_as_string":"2017-02-07T06:32:00.000Z","key":1486449120000,"doc_count":17},{"key_as_string":"2017-02-07T06:33:00.000Z","key":1486449180000,"doc_count":15},{"key_as_string":"2017-02-07T06:34:00.000Z","key":1486449240000,"doc_count":6},{"key_as_string":"2017-02-07T06:35:00.000Z","key":1486449300000,"doc_count":13},{"key_as_string":"2017-02-07T06:36:00.000Z","key":1486449360000,"doc_count":8},{"key_as_string":"2017-02-07T06:37:00.000Z","key":1486449420000,"doc_count":12},{"key_as_string":"2017-02-07T06:38:00.000Z","key":1486449480000,"doc_count":13},{"key_as_string":"2017-02-07T06:39:00.000Z","key":1486449540000,"doc_count":12},{"key_as_string":"2017-02-07T06:40:00.000Z","key":1486449600000,"doc_count":11},{"key_as_string":"2017-02-07T06:41:00.000Z","key":1486449660000,"doc_count":14},{"key_as_string":"2017-02-07T06:42:00.000Z","key":1486449720000,"doc_count":6},{"key_as_string":"2017-02-07T06:43:00.000Z","key":1486449780000,"doc_count":13},{"key_as_string":"2017-02-07T06:44:00.000Z","key":1486449840000,"doc_count":11},{"key_as_string":"2017-02-07T06:45:00.000Z","key":1486449900000,"doc_count":11},{"key_as_string":"2017-02-07T06:46:00.000Z","key":1486449960000,"doc_count":21},{"key_as_string":"2017-02-07T06:47:00.000Z","key":1486450020000,"doc_count":9},{"key_as_string":"2017-02-07T06:48:00.000Z","key":1486450080000,"doc_count":11},{"key_as_string":"2017-02-07T06:49:00.000Z","key":1486450140000,"doc_count":10},{"key_as_string":"2017-02-07T06:50:00.000Z","key":1486450200000,"doc_count":11},{"key_as_string":"2017-02-07T06:51:00.000Z","key":1486450260000,"doc_count":11},{"key_as_string":"2017-02-07T06:52:00.000Z","key":1486450320000,"doc_count":12},{"key_as_string":"2017-02-07T06:53:00.000Z","key":1486450380000,"doc_count":8},{"key_as_string":"2017-02-07T06:54:00.000Z","key":1486450440000,"doc_count":13},{"key_as_string":"2017-02-07T06:55:00.000Z","key":1486450500000,"doc_count":7},{"key_as_string":"2017-02-07T06:56:00.000Z","key":1486450560000,"doc_count":9},{"key_as_string":"2017-02-07T06:57:00.000Z","key":1486450620000,"doc_count":7},{"key_as_string":"2017-02-07T06:58:00.000Z","key":1486450680000,"doc_count":10},{"key_as_string":"2017-02-07T06:59:00.000Z","key":1486450740000,"doc_count":19},{"key_as_string":"2017-02-07T07:00:00.000Z","key":1486450800000,"doc_count":11},{"key_as_string":"2017-02-07T07:01:00.000Z","key":1486450860000,"doc_count":9},{"key_as_string":"2017-02-07T07:02:00.000Z","key":1486450920000,"doc_count":14},{"key_as_string":"2017-02-07T07:03:00.000Z","key":1486450980000,"doc_count":13},{"key_as_string":"2017-02-07T07:04:00.000Z","key":1486451040000,"doc_count":14},{"key_as_string":"2017-02-07T07:05:00.000Z","key":1486451100000,"doc_count":10},{"key_as_string":"2017-02-07T07:06:00.000Z","key":1486451160000,"doc_count":14},{"key_as_string":"2017-02-07T07:07:00.000Z","key":1486451220000,"doc_count":8},{"key_as_string":"2017-02-07T07:08:00.000Z","key":1486451280000,"doc_count":10},{"key_as_string":"2017-02-07T07:09:00.000Z","key":1486451340000,"doc_count":15},{"key_as_string":"2017-02-07T07:10:00.000Z","key":1486451400000,"doc_count":17},{"key_as_string":"2017-02-07T07:11:00.000Z","key":1486451460000,"doc_count":10},{"key_as_string":"2017-02-07T07:12:00.000Z","key":1486451520000,"doc_count":10},{"key_as_string":"2017-02-07T07:13:00.000Z","key":1486451580000,"doc_count":10},{"key_as_string":"2017-02-07T07:14:00.000Z","key":1486451640000,"doc_count":10},{"key_as_string":"2017-02-07T07:15:00.000Z","key":1486451700000,"doc_count":12},{"key_as_string":"2017-02-07T07:16:00.000Z","key":1486451760000,"doc_count":12},{"key_as_string":"2017-02-07T07:17:00.000Z","key":1486451820000,"doc_count":17},{"key_as_string":"2017-02-07T07:18:00.000Z","key":1486451880000,"doc_count":10},{"key_as_string":"2017-02-07T07:19:00.000Z","key":1486451940000,"doc_count":13},{"key_as_string":"2017-02-07T07:20:00.000Z","key":1486452000000,"doc_count":12},{"key_as_string":"2017-02-07T07:21:00.000Z","key":1486452060000,"doc_count":13},{"key_as_string":"2017-02-07T07:22:00.000Z","key":1486452120000,"doc_count":15},{"key_as_string":"2017-02-07T07:23:00.000Z","key":1486452180000,"doc_count":13},{"key_as_string":"2017-02-07T07:24:00.000Z","key":1486452240000,"doc_count":12},{"key_as_string":"2017-02-07T07:25:00.000Z","key":1486452300000,"doc_count":12},{"key_as_string":"2017-02-07T07:26:00.000Z","key":1486452360000,"doc_count":11},{"key_as_string":"2017-02-07T07:27:00.000Z","key":1486452420000,"doc_count":11},{"key_as_string":"2017-02-07T07:28:00.000Z","key":1486452480000,"doc_count":14},{"key_as_string":"2017-02-07T07:29:00.000Z","key":1486452540000,"doc_count":16},{"key_as_string":"2017-02-07T07:30:00.000Z","key":1486452600000,"doc_count":9},{"key_as_string":"2017-02-07T07:31:00.000Z","key":1486452660000,"doc_count":12},{"key_as_string":"2017-02-07T07:32:00.000Z","key":1486452720000,"doc_count":17},{"key_as_string":"2017-02-07T07:33:00.000Z","key":1486452780000,"doc_count":9},{"key_as_string":"2017-02-07T07:34:00.000Z","key":1486452840000,"doc_count":15},{"key_as_string":"2017-02-07T07:35:00.000Z","key":1486452900000,"doc_count":13},{"key_as_string":"2017-02-07T07:36:00.000Z","key":1486452960000,"doc_count":14},{"key_as_string":"2017-02-07T07:37:00.000Z","key":1486453020000,"doc_count":14},{"key_as_string":"2017-02-07T07:38:00.000Z","key":1486453080000,"doc_count":9},{"key_as_string":"2017-02-07T07:39:00.000Z","key":1486453140000,"doc_count":10},{"key_as_string":"2017-02-07T07:40:00.000Z","key":1486453200000,"doc_count":12},{"key_as_string":"2017-02-07T07:41:00.000Z","key":1486453260000,"doc_count":11},{"key_as_string":"2017-02-07T07:42:00.000Z","key":1486453320000,"doc_count":18},{"key_as_string":"2017-02-07T07:43:00.000Z","key":1486453380000,"doc_count":7},{"key_as_string":"2017-02-07T07:44:00.000Z","key":1486453440000,"doc_count":13},{"key_as_string":"2017-02-07T07:45:00.000Z","key":1486453500000,"doc_count":14},{"key_as_string":"2017-02-07T07:46:00.000Z","key":1486453560000,"doc_count":13},{"key_as_string":"2017-02-07T07:47:00.000Z","key":1486453620000,"doc_count":15},{"key_as_string":"2017-02-07T07:48:00.000Z","key":1486453680000,"doc_count":13},{"key_as_string":"2017-02-07T07:49:00.000Z","key":1486453740000,"doc_count":15},{"key_as_string":"2017-02-07T07:50:00.000Z","key":1486453800000,"doc_count":12},{"key_as_string":"2017-02-07T07:51:00.000Z","key":1486453860000,"doc_count":15},{"key_as_string":"2017-02-07T07:52:00.000Z","key":1486453920000,"doc_count":12},{"key_as_string":"2017-02-07T07:53:00.000Z","key":1486453980000,"doc_count":9},{"key_as_string":"2017-02-07T07:54:00.000Z","key":1486454040000,"doc_count":12},{"key_as_string":"2017-02-07T07:55:00.000Z","key":1486454100000,"doc_count":13},{"key_as_string":"2017-02-07T07:56:00.000Z","key":1486454160000,"doc_count":12},{"key_as_string":"2017-02-07T07:57:00.000Z","key":1486454220000,"doc_count":9},{"key_as_string":"2017-02-07T07:58:00.000Z","key":1486454280000,"doc_count":12},{"key_as_string":"2017-02-07T07:59:00.000Z","key":1486454340000,"doc_count":12},{"key_as_string":"2017-02-07T08:00:00.000Z","key":1486454400000,"doc_count":14},{"key_as_string":"2017-02-07T08:01:00.000Z","key":1486454460000,"doc_count":13},{"key_as_string":"2017-02-07T08:02:00.000Z","key":1486454520000,"doc_count":11},{"key_as_string":"2017-02-07T08:03:00.000Z","key":1486454580000,"doc_count":14},{"key_as_string":"2017-02-07T08:04:00.000Z","key":1486454640000,"doc_count":6},{"key_as_string":"2017-02-07T08:05:00.000Z","key":1486454700000,"doc_count":13},{"key_as_string":"2017-02-07T08:06:00.000Z","key":1486454760000,"doc_count":11},{"key_as_string":"2017-02-07T08:07:00.000Z","key":1486454820000,"doc_count":14},{"key_as_string":"2017-02-07T08:08:00.000Z","key":1486454880000,"doc_count":14},{"key_as_string":"2017-02-07T08:09:00.000Z","key":1486454940000,"doc_count":9},{"key_as_string":"2017-02-07T08:10:00.000Z","key":1486455000000,"doc_count":11},{"key_as_string":"2017-02-07T08:11:00.000Z","key":1486455060000,"doc_count":8},{"key_as_string":"2017-02-07T08:12:00.000Z","key":1486455120000,"doc_count":14},{"key_as_string":"2017-02-07T08:13:00.000Z","key":1486455180000,"doc_count":9},{"key_as_string":"2017-02-07T08:14:00.000Z","key":1486455240000,"doc_count":10},{"key_as_string":"2017-02-07T08:15:00.000Z","key":1486455300000,"doc_count":14},{"key_as_string":"2017-02-07T08:16:00.000Z","key":1486455360000,"doc_count":12},{"key_as_string":"2017-02-07T08:17:00.000Z","key":1486455420000,"doc_count":11},{"key_as_string":"2017-02-07T08:18:00.000Z","key":1486455480000,"doc_count":18},{"key_as_string":"2017-02-07T08:19:00.000Z","key":1486455540000,"doc_count":13},{"key_as_string":"2017-02-07T08:20:00.000Z","key":1486455600000,"doc_count":13},{"key_as_string":"2017-02-07T08:21:00.000Z","key":1486455660000,"doc_count":10},{"key_as_string":"2017-02-07T08:22:00.000Z","key":1486455720000,"doc_count":10},{"key_as_string":"2017-02-07T08:23:00.000Z","key":1486455780000,"doc_count":13},{"key_as_string":"2017-02-07T08:24:00.000Z","key":1486455840000,"doc_count":11},{"key_as_string":"2017-02-07T08:25:00.000Z","key":1486455900000,"doc_count":12},{"key_as_string":"2017-02-07T08:26:00.000Z","key":1486455960000,"doc_count":20},{"key_as_string":"2017-02-07T08:27:00.000Z","key":1486456020000,"doc_count":15},{"key_as_string":"2017-02-07T08:28:00.000Z","key":1486456080000,"doc_count":12},{"key_as_string":"2017-02-07T08:29:00.000Z","key":1486456140000,"doc_count":14},{"key_as_string":"2017-02-07T08:30:00.000Z","key":1486456200000,"doc_count":9},{"key_as_string":"2017-02-07T08:31:00.000Z","key":1486456260000,"doc_count":17},{"key_as_string":"2017-02-07T08:32:00.000Z","key":1486456320000,"doc_count":11},{"key_as_string":"2017-02-07T08:33:00.000Z","key":1486456380000,"doc_count":15},{"key_as_string":"2017-02-07T08:34:00.000Z","key":1486456440000,"doc_count":11},{"key_as_string":"2017-02-07T08:35:00.000Z","key":1486456500000,"doc_count":15},{"key_as_string":"2017-02-07T08:36:00.000Z","key":1486456560000,"doc_count":17},{"key_as_string":"2017-02-07T08:37:00.000Z","key":1486456620000,"doc_count":8},{"key_as_string":"2017-02-07T08:38:00.000Z","key":1486456680000,"doc_count":13},{"key_as_string":"2017-02-07T08:39:00.000Z","key":1486456740000,"doc_count":10},{"key_as_string":"2017-02-07T08:40:00.000Z","key":1486456800000,"doc_count":11},{"key_as_string":"2017-02-07T08:41:00.000Z","key":1486456860000,"doc_count":12},{"key_as_string":"2017-02-07T08:42:00.000Z","key":1486456920000,"doc_count":15},{"key_as_string":"2017-02-07T08:43:00.000Z","key":1486456980000,"doc_count":12},{"key_as_string":"2017-02-07T08:44:00.000Z","key":1486457040000,"doc_count":14},{"key_as_string":"2017-02-07T08:45:00.000Z","key":1486457100000,"doc_count":14},{"key_as_string":"2017-02-07T08:46:00.000Z","key":1486457160000,"doc_count":15},{"key_as_string":"2017-02-07T08:47:00.000Z","key":1486457220000,"doc_count":13},{"key_as_string":"2017-02-07T08:48:00.000Z","key":1486457280000,"doc_count":10},{"key_as_string":"2017-02-07T08:49:00.000Z","key":1486457340000,"doc_count":12},{"key_as_string":"2017-02-07T08:50:00.000Z","key":1486457400000,"doc_count":14},{"key_as_string":"2017-02-07T08:51:00.000Z","key":1486457460000,"doc_count":13},{"key_as_string":"2017-02-07T08:52:00.000Z","key":1486457520000,"doc_count":11},{"key_as_string":"2017-02-07T08:53:00.000Z","key":1486457580000,"doc_count":14},{"key_as_string":"2017-02-07T08:54:00.000Z","key":1486457640000,"doc_count":14},{"key_as_string":"2017-02-07T08:55:00.000Z","key":1486457700000,"doc_count":9},{"key_as_string":"2017-02-07T08:56:00.000Z","key":1486457760000,"doc_count":13},{"key_as_string":"2017-02-07T08:57:00.000Z","key":1486457820000,"doc_count":16},{"key_as_string":"2017-02-07T08:58:00.000Z","key":1486457880000,"doc_count":14},{"key_as_string":"2017-02-07T08:59:00.000Z","key":1486457940000,"doc_count":9},{"key_as_string":"2017-02-07T09:00:00.000Z","key":1486458000000,"doc_count":15},{"key_as_string":"2017-02-07T09:01:00.000Z","key":1486458060000,"doc_count":13},{"key_as_string":"2017-02-07T09:02:00.000Z","key":1486458120000,"doc_count":11},{"key_as_string":"2017-02-07T09:03:00.000Z","key":1486458180000,"doc_count":23},{"key_as_string":"2017-02-07T09:04:00.000Z","key":1486458240000,"doc_count":14},{"key_as_string":"2017-02-07T09:05:00.000Z","key":1486458300000,"doc_count":6},{"key_as_string":"2017-02-07T09:06:00.000Z","key":1486458360000,"doc_count":14},{"key_as_string":"2017-02-07T09:07:00.000Z","key":1486458420000,"doc_count":15},{"key_as_string":"2017-02-07T09:08:00.000Z","key":1486458480000,"doc_count":15},{"key_as_string":"2017-02-07T09:09:00.000Z","key":1486458540000,"doc_count":11},{"key_as_string":"2017-02-07T09:10:00.000Z","key":1486458600000,"doc_count":17},{"key_as_string":"2017-02-07T09:11:00.000Z","key":1486458660000,"doc_count":16},{"key_as_string":"2017-02-07T09:12:00.000Z","key":1486458720000,"doc_count":15},{"key_as_string":"2017-02-07T09:13:00.000Z","key":1486458780000,"doc_count":15},{"key_as_string":"2017-02-07T09:14:00.000Z","key":1486458840000,"doc_count":18},{"key_as_string":"2017-02-07T09:15:00.000Z","key":1486458900000,"doc_count":13},{"key_as_string":"2017-02-07T09:16:00.000Z","key":1486458960000,"doc_count":9},{"key_as_string":"2017-02-07T09:17:00.000Z","key":1486459020000,"doc_count":19},{"key_as_string":"2017-02-07T09:18:00.000Z","key":1486459080000,"doc_count":11},{"key_as_string":"2017-02-07T09:19:00.000Z","key":1486459140000,"doc_count":8},{"key_as_string":"2017-02-07T09:20:00.000Z","key":1486459200000,"doc_count":18},{"key_as_string":"2017-02-07T09:21:00.000Z","key":1486459260000,"doc_count":16},{"key_as_string":"2017-02-07T09:22:00.000Z","key":1486459320000,"doc_count":13},{"key_as_string":"2017-02-07T09:23:00.000Z","key":1486459380000,"doc_count":14},{"key_as_string":"2017-02-07T09:24:00.000Z","key":1486459440000,"doc_count":16},{"key_as_string":"2017-02-07T09:25:00.000Z","key":1486459500000,"doc_count":13},{"key_as_string":"2017-02-07T09:26:00.000Z","key":1486459560000,"doc_count":13},{"key_as_string":"2017-02-07T09:27:00.000Z","key":1486459620000,"doc_count":16},{"key_as_string":"2017-02-07T09:28:00.000Z","key":1486459680000,"doc_count":13},{"key_as_string":"2017-02-07T09:29:00.000Z","key":1486459740000,"doc_count":14},{"key_as_string":"2017-02-07T09:30:00.000Z","key":1486459800000,"doc_count":13},{"key_as_string":"2017-02-07T09:31:00.000Z","key":1486459860000,"doc_count":16},{"key_as_string":"2017-02-07T09:32:00.000Z","key":1486459920000,"doc_count":18},{"key_as_string":"2017-02-07T09:33:00.000Z","key":1486459980000,"doc_count":7},{"key_as_string":"2017-02-07T09:34:00.000Z","key":1486460040000,"doc_count":14},{"key_as_string":"2017-02-07T09:35:00.000Z","key":1486460100000,"doc_count":15},{"key_as_string":"2017-02-07T09:36:00.000Z","key":1486460160000,"doc_count":13},{"key_as_string":"2017-02-07T09:37:00.000Z","key":1486460220000,"doc_count":16},{"key_as_string":"2017-02-07T09:38:00.000Z","key":1486460280000,"doc_count":15},{"key_as_string":"2017-02-07T09:39:00.000Z","key":1486460340000,"doc_count":12},{"key_as_string":"2017-02-07T09:40:00.000Z","key":1486460400000,"doc_count":15},{"key_as_string":"2017-02-07T09:41:00.000Z","key":1486460460000,"doc_count":13},{"key_as_string":"2017-02-07T09:42:00.000Z","key":1486460520000,"doc_count":10},{"key_as_string":"2017-02-07T09:43:00.000Z","key":1486460580000,"doc_count":17},{"key_as_string":"2017-02-07T09:44:00.000Z","key":1486460640000,"doc_count":16},{"key_as_string":"2017-02-07T09:45:00.000Z","key":1486460700000,"doc_count":16},{"key_as_string":"2017-02-07T09:46:00.000Z","key":1486460760000,"doc_count":12},{"key_as_string":"2017-02-07T09:47:00.000Z","key":1486460820000,"doc_count":14},{"key_as_string":"2017-02-07T09:48:00.000Z","key":1486460880000,"doc_count":14},{"key_as_string":"2017-02-07T09:49:00.000Z","key":1486460940000,"doc_count":10},{"key_as_string":"2017-02-07T09:50:00.000Z","key":1486461000000,"doc_count":14},{"key_as_string":"2017-02-07T09:51:00.000Z","key":1486461060000,"doc_count":16},{"key_as_string":"2017-02-07T09:52:00.000Z","key":1486461120000,"doc_count":14},{"key_as_string":"2017-02-07T09:53:00.000Z","key":1486461180000,"doc_count":11},{"key_as_string":"2017-02-07T09:54:00.000Z","key":1486461240000,"doc_count":11},{"key_as_string":"2017-02-07T09:55:00.000Z","key":1486461300000,"doc_count":14},{"key_as_string":"2017-02-07T09:56:00.000Z","key":1486461360000,"doc_count":14},{"key_as_string":"2017-02-07T09:57:00.000Z","key":1486461420000,"doc_count":13},{"key_as_string":"2017-02-07T09:58:00.000Z","key":1486461480000,"doc_count":14},{"key_as_string":"2017-02-07T09:59:00.000Z","key":1486461540000,"doc_count":13},{"key_as_string":"2017-02-07T10:00:00.000Z","key":1486461600000,"doc_count":17},{"key_as_string":"2017-02-07T10:01:00.000Z","key":1486461660000,"doc_count":17},{"key_as_string":"2017-02-07T10:02:00.000Z","key":1486461720000,"doc_count":6},{"key_as_string":"2017-02-07T10:03:00.000Z","key":1486461780000,"doc_count":13},{"key_as_string":"2017-02-07T10:04:00.000Z","key":1486461840000,"doc_count":15},{"key_as_string":"2017-02-07T10:05:00.000Z","key":1486461900000,"doc_count":10},{"key_as_string":"2017-02-07T10:06:00.000Z","key":1486461960000,"doc_count":11},{"key_as_string":"2017-02-07T10:07:00.000Z","key":1486462020000,"doc_count":15},{"key_as_string":"2017-02-07T10:08:00.000Z","key":1486462080000,"doc_count":14},{"key_as_string":"2017-02-07T10:09:00.000Z","key":1486462140000,"doc_count":11},{"key_as_string":"2017-02-07T10:10:00.000Z","key":1486462200000,"doc_count":19},{"key_as_string":"2017-02-07T10:11:00.000Z","key":1486462260000,"doc_count":10},{"key_as_string":"2017-02-07T10:12:00.000Z","key":1486462320000,"doc_count":12},{"key_as_string":"2017-02-07T10:13:00.000Z","key":1486462380000,"doc_count":12},{"key_as_string":"2017-02-07T10:14:00.000Z","key":1486462440000,"doc_count":20},{"key_as_string":"2017-02-07T10:15:00.000Z","key":1486462500000,"doc_count":15},{"key_as_string":"2017-02-07T10:16:00.000Z","key":1486462560000,"doc_count":13},{"key_as_string":"2017-02-07T10:17:00.000Z","key":1486462620000,"doc_count":16},{"key_as_string":"2017-02-07T10:18:00.000Z","key":1486462680000,"doc_count":14},{"key_as_string":"2017-02-07T10:19:00.000Z","key":1486462740000,"doc_count":11},{"key_as_string":"2017-02-07T10:20:00.000Z","key":1486462800000,"doc_count":15},{"key_as_string":"2017-02-07T10:21:00.000Z","key":1486462860000,"doc_count":14},{"key_as_string":"2017-02-07T10:22:00.000Z","key":1486462920000,"doc_count":11},{"key_as_string":"2017-02-07T10:23:00.000Z","key":1486462980000,"doc_count":12},{"key_as_string":"2017-02-07T10:24:00.000Z","key":1486463040000,"doc_count":16},{"key_as_string":"2017-02-07T10:25:00.000Z","key":1486463100000,"doc_count":14},{"key_as_string":"2017-02-07T10:26:00.000Z","key":1486463160000,"doc_count":18},{"key_as_string":"2017-02-07T10:27:00.000Z","key":1486463220000,"doc_count":13},{"key_as_string":"2017-02-07T10:28:00.000Z","key":1486463280000,"doc_count":18},{"key_as_string":"2017-02-07T10:29:00.000Z","key":1486463340000,"doc_count":14},{"key_as_string":"2017-02-07T10:30:00.000Z","key":1486463400000,"doc_count":15},{"key_as_string":"2017-02-07T10:31:00.000Z","key":1486463460000,"doc_count":16},{"key_as_string":"2017-02-07T10:32:00.000Z","key":1486463520000,"doc_count":16},{"key_as_string":"2017-02-07T10:33:00.000Z","key":1486463580000,"doc_count":16},{"key_as_string":"2017-02-07T10:34:00.000Z","key":1486463640000,"doc_count":9},{"key_as_string":"2017-02-07T10:35:00.000Z","key":1486463700000,"doc_count":13},{"key_as_string":"2017-02-07T10:36:00.000Z","key":1486463760000,"doc_count":16},{"key_as_string":"2017-02-07T10:37:00.000Z","key":1486463820000,"doc_count":14},{"key_as_string":"2017-02-07T10:38:00.000Z","key":1486463880000,"doc_count":14},{"key_as_string":"2017-02-07T10:39:00.000Z","key":1486463940000,"doc_count":12},{"key_as_string":"2017-02-07T10:40:00.000Z","key":1486464000000,"doc_count":12},{"key_as_string":"2017-02-07T10:41:00.000Z","key":1486464060000,"doc_count":15},{"key_as_string":"2017-02-07T10:42:00.000Z","key":1486464120000,"doc_count":10},{"key_as_string":"2017-02-07T10:43:00.000Z","key":1486464180000,"doc_count":11},{"key_as_string":"2017-02-07T10:44:00.000Z","key":1486464240000,"doc_count":14},{"key_as_string":"2017-02-07T10:45:00.000Z","key":1486464300000,"doc_count":11},{"key_as_string":"2017-02-07T10:46:00.000Z","key":1486464360000,"doc_count":16},{"key_as_string":"2017-02-07T10:47:00.000Z","key":1486464420000,"doc_count":17},{"key_as_string":"2017-02-07T10:48:00.000Z","key":1486464480000,"doc_count":14},{"key_as_string":"2017-02-07T10:49:00.000Z","key":1486464540000,"doc_count":17},{"key_as_string":"2017-02-07T10:50:00.000Z","key":1486464600000,"doc_count":14},{"key_as_string":"2017-02-07T10:51:00.000Z","key":1486464660000,"doc_count":14},{"key_as_string":"2017-02-07T10:52:00.000Z","key":1486464720000,"doc_count":13},{"key_as_string":"2017-02-07T10:53:00.000Z","key":1486464780000,"doc_count":14},{"key_as_string":"2017-02-07T10:54:00.000Z","key":1486464840000,"doc_count":13},{"key_as_string":"2017-02-07T10:55:00.000Z","key":1486464900000,"doc_count":10},{"key_as_string":"2017-02-07T10:56:00.000Z","key":1486464960000,"doc_count":14},{"key_as_string":"2017-02-07T10:57:00.000Z","key":1486465020000,"doc_count":9},{"key_as_string":"2017-02-07T10:58:00.000Z","key":1486465080000,"doc_count":19},{"key_as_string":"2017-02-07T10:59:00.000Z","key":1486465140000,"doc_count":12},{"key_as_string":"2017-02-07T11:00:00.000Z","key":1486465200000,"doc_count":20},{"key_as_string":"2017-02-07T11:01:00.000Z","key":1486465260000,"doc_count":11},{"key_as_string":"2017-02-07T11:02:00.000Z","key":1486465320000,"doc_count":16},{"key_as_string":"2017-02-07T11:03:00.000Z","key":1486465380000,"doc_count":14},{"key_as_string":"2017-02-07T11:04:00.000Z","key":1486465440000,"doc_count":14},{"key_as_string":"2017-02-07T11:05:00.000Z","key":1486465500000,"doc_count":11},{"key_as_string":"2017-02-07T11:06:00.000Z","key":1486465560000,"doc_count":11},{"key_as_string":"2017-02-07T11:07:00.000Z","key":1486465620000,"doc_count":18},{"key_as_string":"2017-02-07T11:08:00.000Z","key":1486465680000,"doc_count":14},{"key_as_string":"2017-02-07T11:09:00.000Z","key":1486465740000,"doc_count":14},{"key_as_string":"2017-02-07T11:10:00.000Z","key":1486465800000,"doc_count":9},{"key_as_string":"2017-02-07T11:11:00.000Z","key":1486465860000,"doc_count":18},{"key_as_string":"2017-02-07T11:12:00.000Z","key":1486465920000,"doc_count":18},{"key_as_string":"2017-02-07T11:13:00.000Z","key":1486465980000,"doc_count":10},{"key_as_string":"2017-02-07T11:14:00.000Z","key":1486466040000,"doc_count":10},{"key_as_string":"2017-02-07T11:15:00.000Z","key":1486466100000,"doc_count":17},{"key_as_string":"2017-02-07T11:16:00.000Z","key":1486466160000,"doc_count":16},{"key_as_string":"2017-02-07T11:17:00.000Z","key":1486466220000,"doc_count":15},{"key_as_string":"2017-02-07T11:18:00.000Z","key":1486466280000,"doc_count":17},{"key_as_string":"2017-02-07T11:19:00.000Z","key":1486466340000,"doc_count":18},{"key_as_string":"2017-02-07T11:20:00.000Z","key":1486466400000,"doc_count":11},{"key_as_string":"2017-02-07T11:21:00.000Z","key":1486466460000,"doc_count":18},{"key_as_string":"2017-02-07T11:22:00.000Z","key":1486466520000,"doc_count":13},{"key_as_string":"2017-02-07T11:23:00.000Z","key":1486466580000,"doc_count":10},{"key_as_string":"2017-02-07T11:24:00.000Z","key":1486466640000,"doc_count":12},{"key_as_string":"2017-02-07T11:25:00.000Z","key":1486466700000,"doc_count":13},{"key_as_string":"2017-02-07T11:26:00.000Z","key":1486466760000,"doc_count":16},{"key_as_string":"2017-02-07T11:27:00.000Z","key":1486466820000,"doc_count":12},{"key_as_string":"2017-02-07T11:28:00.000Z","key":1486466880000,"doc_count":12},{"key_as_string":"2017-02-07T11:29:00.000Z","key":1486466940000,"doc_count":18},{"key_as_string":"2017-02-07T11:30:00.000Z","key":1486467000000,"doc_count":11},{"key_as_string":"2017-02-07T11:31:00.000Z","key":1486467060000,"doc_count":13},{"key_as_string":"2017-02-07T11:32:00.000Z","key":1486467120000,"doc_count":13},{"key_as_string":"2017-02-07T11:33:00.000Z","key":1486467180000,"doc_count":24},{"key_as_string":"2017-02-07T11:34:00.000Z","key":1486467240000,"doc_count":12},{"key_as_string":"2017-02-07T11:35:00.000Z","key":1486467300000,"doc_count":13},{"key_as_string":"2017-02-07T11:36:00.000Z","key":1486467360000,"doc_count":16},{"key_as_string":"2017-02-07T11:37:00.000Z","key":1486467420000,"doc_count":16},{"key_as_string":"2017-02-07T11:38:00.000Z","key":1486467480000,"doc_count":14},{"key_as_string":"2017-02-07T11:39:00.000Z","key":1486467540000,"doc_count":12},{"key_as_string":"2017-02-07T11:40:00.000Z","key":1486467600000,"doc_count":14},{"key_as_string":"2017-02-07T11:41:00.000Z","key":1486467660000,"doc_count":14},{"key_as_string":"2017-02-07T11:42:00.000Z","key":1486467720000,"doc_count":16},{"key_as_string":"2017-02-07T11:43:00.000Z","key":1486467780000,"doc_count":19},{"key_as_string":"2017-02-07T11:44:00.000Z","key":1486467840000,"doc_count":9},{"key_as_string":"2017-02-07T11:45:00.000Z","key":1486467900000,"doc_count":14},{"key_as_string":"2017-02-07T11:46:00.000Z","key":1486467960000,"doc_count":8},{"key_as_string":"2017-02-07T11:47:00.000Z","key":1486468020000,"doc_count":14},{"key_as_string":"2017-02-07T11:48:00.000Z","key":1486468080000,"doc_count":11},{"key_as_string":"2017-02-07T11:49:00.000Z","key":1486468140000,"doc_count":10},{"key_as_string":"2017-02-07T11:50:00.000Z","key":1486468200000,"doc_count":13},{"key_as_string":"2017-02-07T11:51:00.000Z","key":1486468260000,"doc_count":14},{"key_as_string":"2017-02-07T11:52:00.000Z","key":1486468320000,"doc_count":13},{"key_as_string":"2017-02-07T11:53:00.000Z","key":1486468380000,"doc_count":15},{"key_as_string":"2017-02-07T11:54:00.000Z","key":1486468440000,"doc_count":11},{"key_as_string":"2017-02-07T11:55:00.000Z","key":1486468500000,"doc_count":11},{"key_as_string":"2017-02-07T11:56:00.000Z","key":1486468560000,"doc_count":13},{"key_as_string":"2017-02-07T11:57:00.000Z","key":1486468620000,"doc_count":17},{"key_as_string":"2017-02-07T11:58:00.000Z","key":1486468680000,"doc_count":19},{"key_as_string":"2017-02-07T11:59:00.000Z","key":1486468740000,"doc_count":15},{"key_as_string":"2017-02-07T12:00:00.000Z","key":1486468800000,"doc_count":14},{"key_as_string":"2017-02-07T12:01:00.000Z","key":1486468860000,"doc_count":14},{"key_as_string":"2017-02-07T12:02:00.000Z","key":1486468920000,"doc_count":19},{"key_as_string":"2017-02-07T12:03:00.000Z","key":1486468980000,"doc_count":16},{"key_as_string":"2017-02-07T12:04:00.000Z","key":1486469040000,"doc_count":13},{"key_as_string":"2017-02-07T12:05:00.000Z","key":1486469100000,"doc_count":20},{"key_as_string":"2017-02-07T12:06:00.000Z","key":1486469160000,"doc_count":9},{"key_as_string":"2017-02-07T12:07:00.000Z","key":1486469220000,"doc_count":12},{"key_as_string":"2017-02-07T12:08:00.000Z","key":1486469280000,"doc_count":14},{"key_as_string":"2017-02-07T12:09:00.000Z","key":1486469340000,"doc_count":18},{"key_as_string":"2017-02-07T12:10:00.000Z","key":1486469400000,"doc_count":10},{"key_as_string":"2017-02-07T12:11:00.000Z","key":1486469460000,"doc_count":11},{"key_as_string":"2017-02-07T12:12:00.000Z","key":1486469520000,"doc_count":15},{"key_as_string":"2017-02-07T12:13:00.000Z","key":1486469580000,"doc_count":10},{"key_as_string":"2017-02-07T12:14:00.000Z","key":1486469640000,"doc_count":14},{"key_as_string":"2017-02-07T12:15:00.000Z","key":1486469700000,"doc_count":18},{"key_as_string":"2017-02-07T12:16:00.000Z","key":1486469760000,"doc_count":15},{"key_as_string":"2017-02-07T12:17:00.000Z","key":1486469820000,"doc_count":12},{"key_as_string":"2017-02-07T12:18:00.000Z","key":1486469880000,"doc_count":7},{"key_as_string":"2017-02-07T12:19:00.000Z","key":1486469940000,"doc_count":17},{"key_as_string":"2017-02-07T12:20:00.000Z","key":1486470000000,"doc_count":14},{"key_as_string":"2017-02-07T12:21:00.000Z","key":1486470060000,"doc_count":15},{"key_as_string":"2017-02-07T12:22:00.000Z","key":1486470120000,"doc_count":17},{"key_as_string":"2017-02-07T12:23:00.000Z","key":1486470180000,"doc_count":17},{"key_as_string":"2017-02-07T12:24:00.000Z","key":1486470240000,"doc_count":12},{"key_as_string":"2017-02-07T12:25:00.000Z","key":1486470300000,"doc_count":15},{"key_as_string":"2017-02-07T12:26:00.000Z","key":1486470360000,"doc_count":8},{"key_as_string":"2017-02-07T12:27:00.000Z","key":1486470420000,"doc_count":19},{"key_as_string":"2017-02-07T12:28:00.000Z","key":1486470480000,"doc_count":10},{"key_as_string":"2017-02-07T12:29:00.000Z","key":1486470540000,"doc_count":13},{"key_as_string":"2017-02-07T12:30:00.000Z","key":1486470600000,"doc_count":14},{"key_as_string":"2017-02-07T12:31:00.000Z","key":1486470660000,"doc_count":17},{"key_as_string":"2017-02-07T12:32:00.000Z","key":1486470720000,"doc_count":12},{"key_as_string":"2017-02-07T12:33:00.000Z","key":1486470780000,"doc_count":11},{"key_as_string":"2017-02-07T12:34:00.000Z","key":1486470840000,"doc_count":18},{"key_as_string":"2017-02-07T12:35:00.000Z","key":1486470900000,"doc_count":16},{"key_as_string":"2017-02-07T12:36:00.000Z","key":1486470960000,"doc_count":17},{"key_as_string":"2017-02-07T12:37:00.000Z","key":1486471020000,"doc_count":18},{"key_as_string":"2017-02-07T12:38:00.000Z","key":1486471080000,"doc_count":16},{"key_as_string":"2017-02-07T12:39:00.000Z","key":1486471140000,"doc_count":15},{"key_as_string":"2017-02-07T12:40:00.000Z","key":1486471200000,"doc_count":16},{"key_as_string":"2017-02-07T12:41:00.000Z","key":1486471260000,"doc_count":22},{"key_as_string":"2017-02-07T12:42:00.000Z","key":1486471320000,"doc_count":14},{"key_as_string":"2017-02-07T12:43:00.000Z","key":1486471380000,"doc_count":13},{"key_as_string":"2017-02-07T12:44:00.000Z","key":1486471440000,"doc_count":10},{"key_as_string":"2017-02-07T12:45:00.000Z","key":1486471500000,"doc_count":13},{"key_as_string":"2017-02-07T12:46:00.000Z","key":1486471560000,"doc_count":19},{"key_as_string":"2017-02-07T12:47:00.000Z","key":1486471620000,"doc_count":12},{"key_as_string":"2017-02-07T12:48:00.000Z","key":1486471680000,"doc_count":12},{"key_as_string":"2017-02-07T12:49:00.000Z","key":1486471740000,"doc_count":14},{"key_as_string":"2017-02-07T12:50:00.000Z","key":1486471800000,"doc_count":17},{"key_as_string":"2017-02-07T12:51:00.000Z","key":1486471860000,"doc_count":14},{"key_as_string":"2017-02-07T12:52:00.000Z","key":1486471920000,"doc_count":9},{"key_as_string":"2017-02-07T12:53:00.000Z","key":1486471980000,"doc_count":21},{"key_as_string":"2017-02-07T12:54:00.000Z","key":1486472040000,"doc_count":14},{"key_as_string":"2017-02-07T12:55:00.000Z","key":1486472100000,"doc_count":11},{"key_as_string":"2017-02-07T12:56:00.000Z","key":1486472160000,"doc_count":13},{"key_as_string":"2017-02-07T12:57:00.000Z","key":1486472220000,"doc_count":13},{"key_as_string":"2017-02-07T12:58:00.000Z","key":1486472280000,"doc_count":12},{"key_as_string":"2017-02-07T12:59:00.000Z","key":1486472340000,"doc_count":19},{"key_as_string":"2017-02-07T13:00:00.000Z","key":1486472400000,"doc_count":10},{"key_as_string":"2017-02-07T13:01:00.000Z","key":1486472460000,"doc_count":13},{"key_as_string":"2017-02-07T13:02:00.000Z","key":1486472520000,"doc_count":12},{"key_as_string":"2017-02-07T13:03:00.000Z","key":1486472580000,"doc_count":9},{"key_as_string":"2017-02-07T13:04:00.000Z","key":1486472640000,"doc_count":17},{"key_as_string":"2017-02-07T13:05:00.000Z","key":1486472700000,"doc_count":14},{"key_as_string":"2017-02-07T13:06:00.000Z","key":1486472760000,"doc_count":13},{"key_as_string":"2017-02-07T13:07:00.000Z","key":1486472820000,"doc_count":18},{"key_as_string":"2017-02-07T13:08:00.000Z","key":1486472880000,"doc_count":16},{"key_as_string":"2017-02-07T13:09:00.000Z","key":1486472940000,"doc_count":11},{"key_as_string":"2017-02-07T13:10:00.000Z","key":1486473000000,"doc_count":9},{"key_as_string":"2017-02-07T13:11:00.000Z","key":1486473060000,"doc_count":14},{"key_as_string":"2017-02-07T13:12:00.000Z","key":1486473120000,"doc_count":11},{"key_as_string":"2017-02-07T13:13:00.000Z","key":1486473180000,"doc_count":17},{"key_as_string":"2017-02-07T13:14:00.000Z","key":1486473240000,"doc_count":14},{"key_as_string":"2017-02-07T13:15:00.000Z","key":1486473300000,"doc_count":17},{"key_as_string":"2017-02-07T13:16:00.000Z","key":1486473360000,"doc_count":14},{"key_as_string":"2017-02-07T13:17:00.000Z","key":1486473420000,"doc_count":18},{"key_as_string":"2017-02-07T13:18:00.000Z","key":1486473480000,"doc_count":13},{"key_as_string":"2017-02-07T13:19:00.000Z","key":1486473540000,"doc_count":24},{"key_as_string":"2017-02-07T13:20:00.000Z","key":1486473600000,"doc_count":6},{"key_as_string":"2017-02-07T13:21:00.000Z","key":1486473660000,"doc_count":16},{"key_as_string":"2017-02-07T13:22:00.000Z","key":1486473720000,"doc_count":19},{"key_as_string":"2017-02-07T13:23:00.000Z","key":1486473780000,"doc_count":12},{"key_as_string":"2017-02-07T13:24:00.000Z","key":1486473840000,"doc_count":18},{"key_as_string":"2017-02-07T13:25:00.000Z","key":1486473900000,"doc_count":11},{"key_as_string":"2017-02-07T13:26:00.000Z","key":1486473960000,"doc_count":14},{"key_as_string":"2017-02-07T13:27:00.000Z","key":1486474020000,"doc_count":13},{"key_as_string":"2017-02-07T13:28:00.000Z","key":1486474080000,"doc_count":13},{"key_as_string":"2017-02-07T13:29:00.000Z","key":1486474140000,"doc_count":12},{"key_as_string":"2017-02-07T13:30:00.000Z","key":1486474200000,"doc_count":12},{"key_as_string":"2017-02-07T13:31:00.000Z","key":1486474260000,"doc_count":10},{"key_as_string":"2017-02-07T13:32:00.000Z","key":1486474320000,"doc_count":15},{"key_as_string":"2017-02-07T13:33:00.000Z","key":1486474380000,"doc_count":11},{"key_as_string":"2017-02-07T13:34:00.000Z","key":1486474440000,"doc_count":15},{"key_as_string":"2017-02-07T13:35:00.000Z","key":1486474500000,"doc_count":6},{"key_as_string":"2017-02-07T13:36:00.000Z","key":1486474560000,"doc_count":20},{"key_as_string":"2017-02-07T13:37:00.000Z","key":1486474620000,"doc_count":9},{"key_as_string":"2017-02-07T13:38:00.000Z","key":1486474680000,"doc_count":15},{"key_as_string":"2017-02-07T13:39:00.000Z","key":1486474740000,"doc_count":18},{"key_as_string":"2017-02-07T13:40:00.000Z","key":1486474800000,"doc_count":14},{"key_as_string":"2017-02-07T13:41:00.000Z","key":1486474860000,"doc_count":11},{"key_as_string":"2017-02-07T13:42:00.000Z","key":1486474920000,"doc_count":15},{"key_as_string":"2017-02-07T13:43:00.000Z","key":1486474980000,"doc_count":10},{"key_as_string":"2017-02-07T13:44:00.000Z","key":1486475040000,"doc_count":14},{"key_as_string":"2017-02-07T13:45:00.000Z","key":1486475100000,"doc_count":13},{"key_as_string":"2017-02-07T13:46:00.000Z","key":1486475160000,"doc_count":16},{"key_as_string":"2017-02-07T13:47:00.000Z","key":1486475220000,"doc_count":10},{"key_as_string":"2017-02-07T13:48:00.000Z","key":1486475280000,"doc_count":13},{"key_as_string":"2017-02-07T13:49:00.000Z","key":1486475340000,"doc_count":14},{"key_as_string":"2017-02-07T13:50:00.000Z","key":1486475400000,"doc_count":12},{"key_as_string":"2017-02-07T13:51:00.000Z","key":1486475460000,"doc_count":12},{"key_as_string":"2017-02-07T13:52:00.000Z","key":1486475520000,"doc_count":11},{"key_as_string":"2017-02-07T13:53:00.000Z","key":1486475580000,"doc_count":15},{"key_as_string":"2017-02-07T13:54:00.000Z","key":1486475640000,"doc_count":13},{"key_as_string":"2017-02-07T13:55:00.000Z","key":1486475700000,"doc_count":14},{"key_as_string":"2017-02-07T13:56:00.000Z","key":1486475760000,"doc_count":18},{"key_as_string":"2017-02-07T13:57:00.000Z","key":1486475820000,"doc_count":6},{"key_as_string":"2017-02-07T13:58:00.000Z","key":1486475880000,"doc_count":19},{"key_as_string":"2017-02-07T13:59:00.000Z","key":1486475940000,"doc_count":13},{"key_as_string":"2017-02-07T14:00:00.000Z","key":1486476000000,"doc_count":9},{"key_as_string":"2017-02-07T14:01:00.000Z","key":1486476060000,"doc_count":10},{"key_as_string":"2017-02-07T14:02:00.000Z","key":1486476120000,"doc_count":14},{"key_as_string":"2017-02-07T14:03:00.000Z","key":1486476180000,"doc_count":13},{"key_as_string":"2017-02-07T14:04:00.000Z","key":1486476240000,"doc_count":19},{"key_as_string":"2017-02-07T14:05:00.000Z","key":1486476300000,"doc_count":15},{"key_as_string":"2017-02-07T14:06:00.000Z","key":1486476360000,"doc_count":12},{"key_as_string":"2017-02-07T14:07:00.000Z","key":1486476420000,"doc_count":11},{"key_as_string":"2017-02-07T14:08:00.000Z","key":1486476480000,"doc_count":15},{"key_as_string":"2017-02-07T14:09:00.000Z","key":1486476540000,"doc_count":10},{"key_as_string":"2017-02-07T14:10:00.000Z","key":1486476600000,"doc_count":13},{"key_as_string":"2017-02-07T14:11:00.000Z","key":1486476660000,"doc_count":18},{"key_as_string":"2017-02-07T14:12:00.000Z","key":1486476720000,"doc_count":10},{"key_as_string":"2017-02-07T14:13:00.000Z","key":1486476780000,"doc_count":13},{"key_as_string":"2017-02-07T14:14:00.000Z","key":1486476840000,"doc_count":15},{"key_as_string":"2017-02-07T14:15:00.000Z","key":1486476900000,"doc_count":18},{"key_as_string":"2017-02-07T14:16:00.000Z","key":1486476960000,"doc_count":14},{"key_as_string":"2017-02-07T14:17:00.000Z","key":1486477020000,"doc_count":13},{"key_as_string":"2017-02-07T14:18:00.000Z","key":1486477080000,"doc_count":12},{"key_as_string":"2017-02-07T14:19:00.000Z","key":1486477140000,"doc_count":18},{"key_as_string":"2017-02-07T14:20:00.000Z","key":1486477200000,"doc_count":13},{"key_as_string":"2017-02-07T14:21:00.000Z","key":1486477260000,"doc_count":14},{"key_as_string":"2017-02-07T14:22:00.000Z","key":1486477320000,"doc_count":8},{"key_as_string":"2017-02-07T14:23:00.000Z","key":1486477380000,"doc_count":16},{"key_as_string":"2017-02-07T14:24:00.000Z","key":1486477440000,"doc_count":12},{"key_as_string":"2017-02-07T14:25:00.000Z","key":1486477500000,"doc_count":17},{"key_as_string":"2017-02-07T14:26:00.000Z","key":1486477560000,"doc_count":11},{"key_as_string":"2017-02-07T14:27:00.000Z","key":1486477620000,"doc_count":17},{"key_as_string":"2017-02-07T14:28:00.000Z","key":1486477680000,"doc_count":17},{"key_as_string":"2017-02-07T14:29:00.000Z","key":1486477740000,"doc_count":14},{"key_as_string":"2017-02-07T14:30:00.000Z","key":1486477800000,"doc_count":15},{"key_as_string":"2017-02-07T14:31:00.000Z","key":1486477860000,"doc_count":13},{"key_as_string":"2017-02-07T14:32:00.000Z","key":1486477920000,"doc_count":9},{"key_as_string":"2017-02-07T14:33:00.000Z","key":1486477980000,"doc_count":17},{"key_as_string":"2017-02-07T14:34:00.000Z","key":1486478040000,"doc_count":12},{"key_as_string":"2017-02-07T14:35:00.000Z","key":1486478100000,"doc_count":14},{"key_as_string":"2017-02-07T14:36:00.000Z","key":1486478160000,"doc_count":14},{"key_as_string":"2017-02-07T14:37:00.000Z","key":1486478220000,"doc_count":13},{"key_as_string":"2017-02-07T14:38:00.000Z","key":1486478280000,"doc_count":13},{"key_as_string":"2017-02-07T14:39:00.000Z","key":1486478340000,"doc_count":11},{"key_as_string":"2017-02-07T14:40:00.000Z","key":1486478400000,"doc_count":20},{"key_as_string":"2017-02-07T14:41:00.000Z","key":1486478460000,"doc_count":12},{"key_as_string":"2017-02-07T14:42:00.000Z","key":1486478520000,"doc_count":14},{"key_as_string":"2017-02-07T14:43:00.000Z","key":1486478580000,"doc_count":14},{"key_as_string":"2017-02-07T14:44:00.000Z","key":1486478640000,"doc_count":13},{"key_as_string":"2017-02-07T14:45:00.000Z","key":1486478700000,"doc_count":16},{"key_as_string":"2017-02-07T14:46:00.000Z","key":1486478760000,"doc_count":16},{"key_as_string":"2017-02-07T14:47:00.000Z","key":1486478820000,"doc_count":12},{"key_as_string":"2017-02-07T14:48:00.000Z","key":1486478880000,"doc_count":11},{"key_as_string":"2017-02-07T14:49:00.000Z","key":1486478940000,"doc_count":16},{"key_as_string":"2017-02-07T14:50:00.000Z","key":1486479000000,"doc_count":12},{"key_as_string":"2017-02-07T14:51:00.000Z","key":1486479060000,"doc_count":9},{"key_as_string":"2017-02-07T14:52:00.000Z","key":1486479120000,"doc_count":13},{"key_as_string":"2017-02-07T14:53:00.000Z","key":1486479180000,"doc_count":12},{"key_as_string":"2017-02-07T14:54:00.000Z","key":1486479240000,"doc_count":19},{"key_as_string":"2017-02-07T14:55:00.000Z","key":1486479300000,"doc_count":10},{"key_as_string":"2017-02-07T14:56:00.000Z","key":1486479360000,"doc_count":15},{"key_as_string":"2017-02-07T14:57:00.000Z","key":1486479420000,"doc_count":16},{"key_as_string":"2017-02-07T14:58:00.000Z","key":1486479480000,"doc_count":14},{"key_as_string":"2017-02-07T14:59:00.000Z","key":1486479540000,"doc_count":12},{"key_as_string":"2017-02-07T15:00:00.000Z","key":1486479600000,"doc_count":14},{"key_as_string":"2017-02-07T15:01:00.000Z","key":1486479660000,"doc_count":12},{"key_as_string":"2017-02-07T15:02:00.000Z","key":1486479720000,"doc_count":11},{"key_as_string":"2017-02-07T15:03:00.000Z","key":1486479780000,"doc_count":13},{"key_as_string":"2017-02-07T15:04:00.000Z","key":1486479840000,"doc_count":14},{"key_as_string":"2017-02-07T15:05:00.000Z","key":1486479900000,"doc_count":10},{"key_as_string":"2017-02-07T15:06:00.000Z","key":1486479960000,"doc_count":9},{"key_as_string":"2017-02-07T15:07:00.000Z","key":1486480020000,"doc_count":10},{"key_as_string":"2017-02-07T15:08:00.000Z","key":1486480080000,"doc_count":15},{"key_as_string":"2017-02-07T15:09:00.000Z","key":1486480140000,"doc_count":13},{"key_as_string":"2017-02-07T15:10:00.000Z","key":1486480200000,"doc_count":12},{"key_as_string":"2017-02-07T15:11:00.000Z","key":1486480260000,"doc_count":13},{"key_as_string":"2017-02-07T15:12:00.000Z","key":1486480320000,"doc_count":12},{"key_as_string":"2017-02-07T15:13:00.000Z","key":1486480380000,"doc_count":13},{"key_as_string":"2017-02-07T15:14:00.000Z","key":1486480440000,"doc_count":15},{"key_as_string":"2017-02-07T15:15:00.000Z","key":1486480500000,"doc_count":15},{"key_as_string":"2017-02-07T15:16:00.000Z","key":1486480560000,"doc_count":14},{"key_as_string":"2017-02-07T15:17:00.000Z","key":1486480620000,"doc_count":7},{"key_as_string":"2017-02-07T15:18:00.000Z","key":1486480680000,"doc_count":16},{"key_as_string":"2017-02-07T15:19:00.000Z","key":1486480740000,"doc_count":13},{"key_as_string":"2017-02-07T15:20:00.000Z","key":1486480800000,"doc_count":14},{"key_as_string":"2017-02-07T15:21:00.000Z","key":1486480860000,"doc_count":13},{"key_as_string":"2017-02-07T15:22:00.000Z","key":1486480920000,"doc_count":12},{"key_as_string":"2017-02-07T15:23:00.000Z","key":1486480980000,"doc_count":16},{"key_as_string":"2017-02-07T15:24:00.000Z","key":1486481040000,"doc_count":15},{"key_as_string":"2017-02-07T15:25:00.000Z","key":1486481100000,"doc_count":8},{"key_as_string":"2017-02-07T15:26:00.000Z","key":1486481160000,"doc_count":15},{"key_as_string":"2017-02-07T15:27:00.000Z","key":1486481220000,"doc_count":11},{"key_as_string":"2017-02-07T15:28:00.000Z","key":1486481280000,"doc_count":14},{"key_as_string":"2017-02-07T15:29:00.000Z","key":1486481340000,"doc_count":14},{"key_as_string":"2017-02-07T15:30:00.000Z","key":1486481400000,"doc_count":12},{"key_as_string":"2017-02-07T15:31:00.000Z","key":1486481460000,"doc_count":19},{"key_as_string":"2017-02-07T15:32:00.000Z","key":1486481520000,"doc_count":15},{"key_as_string":"2017-02-07T15:33:00.000Z","key":1486481580000,"doc_count":15},{"key_as_string":"2017-02-07T15:34:00.000Z","key":1486481640000,"doc_count":11},{"key_as_string":"2017-02-07T15:35:00.000Z","key":1486481700000,"doc_count":14},{"key_as_string":"2017-02-07T15:36:00.000Z","key":1486481760000,"doc_count":13},{"key_as_string":"2017-02-07T15:37:00.000Z","key":1486481820000,"doc_count":18},{"key_as_string":"2017-02-07T15:38:00.000Z","key":1486481880000,"doc_count":8},{"key_as_string":"2017-02-07T15:39:00.000Z","key":1486481940000,"doc_count":14},{"key_as_string":"2017-02-07T15:40:00.000Z","key":1486482000000,"doc_count":11},{"key_as_string":"2017-02-07T15:41:00.000Z","key":1486482060000,"doc_count":10},{"key_as_string":"2017-02-07T15:42:00.000Z","key":1486482120000,"doc_count":10},{"key_as_string":"2017-02-07T15:43:00.000Z","key":1486482180000,"doc_count":14},{"key_as_string":"2017-02-07T15:44:00.000Z","key":1486482240000,"doc_count":10},{"key_as_string":"2017-02-07T15:45:00.000Z","key":1486482300000,"doc_count":13},{"key_as_string":"2017-02-07T15:46:00.000Z","key":1486482360000,"doc_count":15},{"key_as_string":"2017-02-07T15:47:00.000Z","key":1486482420000,"doc_count":11},{"key_as_string":"2017-02-07T15:48:00.000Z","key":1486482480000,"doc_count":13},{"key_as_string":"2017-02-07T15:49:00.000Z","key":1486482540000,"doc_count":10},{"key_as_string":"2017-02-07T15:50:00.000Z","key":1486482600000,"doc_count":17},{"key_as_string":"2017-02-07T15:51:00.000Z","key":1486482660000,"doc_count":11},{"key_as_string":"2017-02-07T15:52:00.000Z","key":1486482720000,"doc_count":15},{"key_as_string":"2017-02-07T15:53:00.000Z","key":1486482780000,"doc_count":10},{"key_as_string":"2017-02-07T15:54:00.000Z","key":1486482840000,"doc_count":11},{"key_as_string":"2017-02-07T15:55:00.000Z","key":1486482900000,"doc_count":14},{"key_as_string":"2017-02-07T15:56:00.000Z","key":1486482960000,"doc_count":16},{"key_as_string":"2017-02-07T15:57:00.000Z","key":1486483020000,"doc_count":15},{"key_as_string":"2017-02-07T15:58:00.000Z","key":1486483080000,"doc_count":13},{"key_as_string":"2017-02-07T15:59:00.000Z","key":1486483140000,"doc_count":10},{"key_as_string":"2017-02-07T16:00:00.000Z","key":1486483200000,"doc_count":18},{"key_as_string":"2017-02-07T16:01:00.000Z","key":1486483260000,"doc_count":11},{"key_as_string":"2017-02-07T16:02:00.000Z","key":1486483320000,"doc_count":14},{"key_as_string":"2017-02-07T16:03:00.000Z","key":1486483380000,"doc_count":16},{"key_as_string":"2017-02-07T16:04:00.000Z","key":1486483440000,"doc_count":10},{"key_as_string":"2017-02-07T16:05:00.000Z","key":1486483500000,"doc_count":12},{"key_as_string":"2017-02-07T16:06:00.000Z","key":1486483560000,"doc_count":11},{"key_as_string":"2017-02-07T16:07:00.000Z","key":1486483620000,"doc_count":14},{"key_as_string":"2017-02-07T16:08:00.000Z","key":1486483680000,"doc_count":12},{"key_as_string":"2017-02-07T16:09:00.000Z","key":1486483740000,"doc_count":8},{"key_as_string":"2017-02-07T16:10:00.000Z","key":1486483800000,"doc_count":13},{"key_as_string":"2017-02-07T16:11:00.000Z","key":1486483860000,"doc_count":15},{"key_as_string":"2017-02-07T16:12:00.000Z","key":1486483920000,"doc_count":9},{"key_as_string":"2017-02-07T16:13:00.000Z","key":1486483980000,"doc_count":14},{"key_as_string":"2017-02-07T16:14:00.000Z","key":1486484040000,"doc_count":11},{"key_as_string":"2017-02-07T16:15:00.000Z","key":1486484100000,"doc_count":10},{"key_as_string":"2017-02-07T16:16:00.000Z","key":1486484160000,"doc_count":14},{"key_as_string":"2017-02-07T16:17:00.000Z","key":1486484220000,"doc_count":10},{"key_as_string":"2017-02-07T16:18:00.000Z","key":1486484280000,"doc_count":10},{"key_as_string":"2017-02-07T16:19:00.000Z","key":1486484340000,"doc_count":17},{"key_as_string":"2017-02-07T16:20:00.000Z","key":1486484400000,"doc_count":11},{"key_as_string":"2017-02-07T16:21:00.000Z","key":1486484460000,"doc_count":14},{"key_as_string":"2017-02-07T16:22:00.000Z","key":1486484520000,"doc_count":15},{"key_as_string":"2017-02-07T16:23:00.000Z","key":1486484580000,"doc_count":8},{"key_as_string":"2017-02-07T16:24:00.000Z","key":1486484640000,"doc_count":14},{"key_as_string":"2017-02-07T16:25:00.000Z","key":1486484700000,"doc_count":10},{"key_as_string":"2017-02-07T16:26:00.000Z","key":1486484760000,"doc_count":10},{"key_as_string":"2017-02-07T16:27:00.000Z","key":1486484820000,"doc_count":14},{"key_as_string":"2017-02-07T16:28:00.000Z","key":1486484880000,"doc_count":19},{"key_as_string":"2017-02-07T16:29:00.000Z","key":1486484940000,"doc_count":9},{"key_as_string":"2017-02-07T16:30:00.000Z","key":1486485000000,"doc_count":15},{"key_as_string":"2017-02-07T16:31:00.000Z","key":1486485060000,"doc_count":13},{"key_as_string":"2017-02-07T16:32:00.000Z","key":1486485120000,"doc_count":10},{"key_as_string":"2017-02-07T16:33:00.000Z","key":1486485180000,"doc_count":12},{"key_as_string":"2017-02-07T16:34:00.000Z","key":1486485240000,"doc_count":12},{"key_as_string":"2017-02-07T16:35:00.000Z","key":1486485300000,"doc_count":17},{"key_as_string":"2017-02-07T16:36:00.000Z","key":1486485360000,"doc_count":8},{"key_as_string":"2017-02-07T16:37:00.000Z","key":1486485420000,"doc_count":17},{"key_as_string":"2017-02-07T16:38:00.000Z","key":1486485480000,"doc_count":13},{"key_as_string":"2017-02-07T16:39:00.000Z","key":1486485540000,"doc_count":7},{"key_as_string":"2017-02-07T16:40:00.000Z","key":1486485600000,"doc_count":11},{"key_as_string":"2017-02-07T16:41:00.000Z","key":1486485660000,"doc_count":14},{"key_as_string":"2017-02-07T16:42:00.000Z","key":1486485720000,"doc_count":16},{"key_as_string":"2017-02-07T16:43:00.000Z","key":1486485780000,"doc_count":11},{"key_as_string":"2017-02-07T16:44:00.000Z","key":1486485840000,"doc_count":15},{"key_as_string":"2017-02-07T16:45:00.000Z","key":1486485900000,"doc_count":15},{"key_as_string":"2017-02-07T16:46:00.000Z","key":1486485960000,"doc_count":11},{"key_as_string":"2017-02-07T16:47:00.000Z","key":1486486020000,"doc_count":11},{"key_as_string":"2017-02-07T16:48:00.000Z","key":1486486080000,"doc_count":12},{"key_as_string":"2017-02-07T16:49:00.000Z","key":1486486140000,"doc_count":12},{"key_as_string":"2017-02-07T16:50:00.000Z","key":1486486200000,"doc_count":11},{"key_as_string":"2017-02-07T16:51:00.000Z","key":1486486260000,"doc_count":12},{"key_as_string":"2017-02-07T16:52:00.000Z","key":1486486320000,"doc_count":12},{"key_as_string":"2017-02-07T16:53:00.000Z","key":1486486380000,"doc_count":14},{"key_as_string":"2017-02-07T16:54:00.000Z","key":1486486440000,"doc_count":10},{"key_as_string":"2017-02-07T16:55:00.000Z","key":1486486500000,"doc_count":12},{"key_as_string":"2017-02-07T16:56:00.000Z","key":1486486560000,"doc_count":15},{"key_as_string":"2017-02-07T16:57:00.000Z","key":1486486620000,"doc_count":14},{"key_as_string":"2017-02-07T16:58:00.000Z","key":1486486680000,"doc_count":9},{"key_as_string":"2017-02-07T16:59:00.000Z","key":1486486740000,"doc_count":15},{"key_as_string":"2017-02-07T17:00:00.000Z","key":1486486800000,"doc_count":10},{"key_as_string":"2017-02-07T17:01:00.000Z","key":1486486860000,"doc_count":9},{"key_as_string":"2017-02-07T17:02:00.000Z","key":1486486920000,"doc_count":15},{"key_as_string":"2017-02-07T17:03:00.000Z","key":1486486980000,"doc_count":11},{"key_as_string":"2017-02-07T17:04:00.000Z","key":1486487040000,"doc_count":11},{"key_as_string":"2017-02-07T17:05:00.000Z","key":1486487100000,"doc_count":6},{"key_as_string":"2017-02-07T17:06:00.000Z","key":1486487160000,"doc_count":20},{"key_as_string":"2017-02-07T17:07:00.000Z","key":1486487220000,"doc_count":12},{"key_as_string":"2017-02-07T17:08:00.000Z","key":1486487280000,"doc_count":10},{"key_as_string":"2017-02-07T17:09:00.000Z","key":1486487340000,"doc_count":15},{"key_as_string":"2017-02-07T17:10:00.000Z","key":1486487400000,"doc_count":15},{"key_as_string":"2017-02-07T17:11:00.000Z","key":1486487460000,"doc_count":12},{"key_as_string":"2017-02-07T17:12:00.000Z","key":1486487520000,"doc_count":10},{"key_as_string":"2017-02-07T17:13:00.000Z","key":1486487580000,"doc_count":14},{"key_as_string":"2017-02-07T17:14:00.000Z","key":1486487640000,"doc_count":11},{"key_as_string":"2017-02-07T17:15:00.000Z","key":1486487700000,"doc_count":15},{"key_as_string":"2017-02-07T17:16:00.000Z","key":1486487760000,"doc_count":9},{"key_as_string":"2017-02-07T17:17:00.000Z","key":1486487820000,"doc_count":15},{"key_as_string":"2017-02-07T17:18:00.000Z","key":1486487880000,"doc_count":10},{"key_as_string":"2017-02-07T17:19:00.000Z","key":1486487940000,"doc_count":15},{"key_as_string":"2017-02-07T17:20:00.000Z","key":1486488000000,"doc_count":8},{"key_as_string":"2017-02-07T17:21:00.000Z","key":1486488060000,"doc_count":9},{"key_as_string":"2017-02-07T17:22:00.000Z","key":1486488120000,"doc_count":13},{"key_as_string":"2017-02-07T17:23:00.000Z","key":1486488180000,"doc_count":12},{"key_as_string":"2017-02-07T17:24:00.000Z","key":1486488240000,"doc_count":12},{"key_as_string":"2017-02-07T17:25:00.000Z","key":1486488300000,"doc_count":13},{"key_as_string":"2017-02-07T17:26:00.000Z","key":1486488360000,"doc_count":12},{"key_as_string":"2017-02-07T17:27:00.000Z","key":1486488420000,"doc_count":13},{"key_as_string":"2017-02-07T17:28:00.000Z","key":1486488480000,"doc_count":13},{"key_as_string":"2017-02-07T17:29:00.000Z","key":1486488540000,"doc_count":13},{"key_as_string":"2017-02-07T17:30:00.000Z","key":1486488600000,"doc_count":15},{"key_as_string":"2017-02-07T17:31:00.000Z","key":1486488660000,"doc_count":11},{"key_as_string":"2017-02-07T17:32:00.000Z","key":1486488720000,"doc_count":10},{"key_as_string":"2017-02-07T17:33:00.000Z","key":1486488780000,"doc_count":10},{"key_as_string":"2017-02-07T17:34:00.000Z","key":1486488840000,"doc_count":10},{"key_as_string":"2017-02-07T17:35:00.000Z","key":1486488900000,"doc_count":12},{"key_as_string":"2017-02-07T17:36:00.000Z","key":1486488960000,"doc_count":15},{"key_as_string":"2017-02-07T17:37:00.000Z","key":1486489020000,"doc_count":8},{"key_as_string":"2017-02-07T17:38:00.000Z","key":1486489080000,"doc_count":15},{"key_as_string":"2017-02-07T17:39:00.000Z","key":1486489140000,"doc_count":11},{"key_as_string":"2017-02-07T17:40:00.000Z","key":1486489200000,"doc_count":8},{"key_as_string":"2017-02-07T17:41:00.000Z","key":1486489260000,"doc_count":17},{"key_as_string":"2017-02-07T17:42:00.000Z","key":1486489320000,"doc_count":16},{"key_as_string":"2017-02-07T17:43:00.000Z","key":1486489380000,"doc_count":12},{"key_as_string":"2017-02-07T17:44:00.000Z","key":1486489440000,"doc_count":8},{"key_as_string":"2017-02-07T17:45:00.000Z","key":1486489500000,"doc_count":12},{"key_as_string":"2017-02-07T17:46:00.000Z","key":1486489560000,"doc_count":13},{"key_as_string":"2017-02-07T17:47:00.000Z","key":1486489620000,"doc_count":8},{"key_as_string":"2017-02-07T17:48:00.000Z","key":1486489680000,"doc_count":7},{"key_as_string":"2017-02-07T17:49:00.000Z","key":1486489740000,"doc_count":16},{"key_as_string":"2017-02-07T17:50:00.000Z","key":1486489800000,"doc_count":13},{"key_as_string":"2017-02-07T17:51:00.000Z","key":1486489860000,"doc_count":11},{"key_as_string":"2017-02-07T17:52:00.000Z","key":1486489920000,"doc_count":14},{"key_as_string":"2017-02-07T17:53:00.000Z","key":1486489980000,"doc_count":8},{"key_as_string":"2017-02-07T17:54:00.000Z","key":1486490040000,"doc_count":10},{"key_as_string":"2017-02-07T17:55:00.000Z","key":1486490100000,"doc_count":12},{"key_as_string":"2017-02-07T17:56:00.000Z","key":1486490160000,"doc_count":11},{"key_as_string":"2017-02-07T17:57:00.000Z","key":1486490220000,"doc_count":9},{"key_as_string":"2017-02-07T17:58:00.000Z","key":1486490280000,"doc_count":10},{"key_as_string":"2017-02-07T17:59:00.000Z","key":1486490340000,"doc_count":11},{"key_as_string":"2017-02-07T18:00:00.000Z","key":1486490400000,"doc_count":8},{"key_as_string":"2017-02-07T18:01:00.000Z","key":1486490460000,"doc_count":13},{"key_as_string":"2017-02-07T18:02:00.000Z","key":1486490520000,"doc_count":19},{"key_as_string":"2017-02-07T18:03:00.000Z","key":1486490580000,"doc_count":7},{"key_as_string":"2017-02-07T18:04:00.000Z","key":1486490640000,"doc_count":11},{"key_as_string":"2017-02-07T18:05:00.000Z","key":1486490700000,"doc_count":11},{"key_as_string":"2017-02-07T18:06:00.000Z","key":1486490760000,"doc_count":9},{"key_as_string":"2017-02-07T18:07:00.000Z","key":1486490820000,"doc_count":16},{"key_as_string":"2017-02-07T18:08:00.000Z","key":1486490880000,"doc_count":13},{"key_as_string":"2017-02-07T18:09:00.000Z","key":1486490940000,"doc_count":9},{"key_as_string":"2017-02-07T18:10:00.000Z","key":1486491000000,"doc_count":14},{"key_as_string":"2017-02-07T18:11:00.000Z","key":1486491060000,"doc_count":9},{"key_as_string":"2017-02-07T18:12:00.000Z","key":1486491120000,"doc_count":12},{"key_as_string":"2017-02-07T18:13:00.000Z","key":1486491180000,"doc_count":12},{"key_as_string":"2017-02-07T18:14:00.000Z","key":1486491240000,"doc_count":10},{"key_as_string":"2017-02-07T18:15:00.000Z","key":1486491300000,"doc_count":13},{"key_as_string":"2017-02-07T18:16:00.000Z","key":1486491360000,"doc_count":11},{"key_as_string":"2017-02-07T18:17:00.000Z","key":1486491420000,"doc_count":13},{"key_as_string":"2017-02-07T18:18:00.000Z","key":1486491480000,"doc_count":10},{"key_as_string":"2017-02-07T18:19:00.000Z","key":1486491540000,"doc_count":10},{"key_as_string":"2017-02-07T18:20:00.000Z","key":1486491600000,"doc_count":8},{"key_as_string":"2017-02-07T18:21:00.000Z","key":1486491660000,"doc_count":15},{"key_as_string":"2017-02-07T18:22:00.000Z","key":1486491720000,"doc_count":16},{"key_as_string":"2017-02-07T18:23:00.000Z","key":1486491780000,"doc_count":14},{"key_as_string":"2017-02-07T18:24:00.000Z","key":1486491840000,"doc_count":17},{"key_as_string":"2017-02-07T18:25:00.000Z","key":1486491900000,"doc_count":11},{"key_as_string":"2017-02-07T18:26:00.000Z","key":1486491960000,"doc_count":13},{"key_as_string":"2017-02-07T18:27:00.000Z","key":1486492020000,"doc_count":15},{"key_as_string":"2017-02-07T18:28:00.000Z","key":1486492080000,"doc_count":13},{"key_as_string":"2017-02-07T18:29:00.000Z","key":1486492140000,"doc_count":13},{"key_as_string":"2017-02-07T18:30:00.000Z","key":1486492200000,"doc_count":12},{"key_as_string":"2017-02-07T18:31:00.000Z","key":1486492260000,"doc_count":14},{"key_as_string":"2017-02-07T18:32:00.000Z","key":1486492320000,"doc_count":13},{"key_as_string":"2017-02-07T18:33:00.000Z","key":1486492380000,"doc_count":6},{"key_as_string":"2017-02-07T18:34:00.000Z","key":1486492440000,"doc_count":14},{"key_as_string":"2017-02-07T18:35:00.000Z","key":1486492500000,"doc_count":14},{"key_as_string":"2017-02-07T18:36:00.000Z","key":1486492560000,"doc_count":9},{"key_as_string":"2017-02-07T18:37:00.000Z","key":1486492620000,"doc_count":12},{"key_as_string":"2017-02-07T18:38:00.000Z","key":1486492680000,"doc_count":8},{"key_as_string":"2017-02-07T18:39:00.000Z","key":1486492740000,"doc_count":13},{"key_as_string":"2017-02-07T18:40:00.000Z","key":1486492800000,"doc_count":6},{"key_as_string":"2017-02-07T18:41:00.000Z","key":1486492860000,"doc_count":16},{"key_as_string":"2017-02-07T18:42:00.000Z","key":1486492920000,"doc_count":12},{"key_as_string":"2017-02-07T18:43:00.000Z","key":1486492980000,"doc_count":9},{"key_as_string":"2017-02-07T18:44:00.000Z","key":1486493040000,"doc_count":14},{"key_as_string":"2017-02-07T18:45:00.000Z","key":1486493100000,"doc_count":14},{"key_as_string":"2017-02-07T18:46:00.000Z","key":1486493160000,"doc_count":8},{"key_as_string":"2017-02-07T18:47:00.000Z","key":1486493220000,"doc_count":12},{"key_as_string":"2017-02-07T18:48:00.000Z","key":1486493280000,"doc_count":11},{"key_as_string":"2017-02-07T18:49:00.000Z","key":1486493340000,"doc_count":14},{"key_as_string":"2017-02-07T18:50:00.000Z","key":1486493400000,"doc_count":10},{"key_as_string":"2017-02-07T18:51:00.000Z","key":1486493460000,"doc_count":11},{"key_as_string":"2017-02-07T18:52:00.000Z","key":1486493520000,"doc_count":17},{"key_as_string":"2017-02-07T18:53:00.000Z","key":1486493580000,"doc_count":11},{"key_as_string":"2017-02-07T18:54:00.000Z","key":1486493640000,"doc_count":12},{"key_as_string":"2017-02-07T18:55:00.000Z","key":1486493700000,"doc_count":10},{"key_as_string":"2017-02-07T18:56:00.000Z","key":1486493760000,"doc_count":10},{"key_as_string":"2017-02-07T18:57:00.000Z","key":1486493820000,"doc_count":11},{"key_as_string":"2017-02-07T18:58:00.000Z","key":1486493880000,"doc_count":11},{"key_as_string":"2017-02-07T18:59:00.000Z","key":1486493940000,"doc_count":13},{"key_as_string":"2017-02-07T19:00:00.000Z","key":1486494000000,"doc_count":9},{"key_as_string":"2017-02-07T19:01:00.000Z","key":1486494060000,"doc_count":15},{"key_as_string":"2017-02-07T19:02:00.000Z","key":1486494120000,"doc_count":10},{"key_as_string":"2017-02-07T19:03:00.000Z","key":1486494180000,"doc_count":11},{"key_as_string":"2017-02-07T19:04:00.000Z","key":1486494240000,"doc_count":10},{"key_as_string":"2017-02-07T19:05:00.000Z","key":1486494300000,"doc_count":10},{"key_as_string":"2017-02-07T19:06:00.000Z","key":1486494360000,"doc_count":15},{"key_as_string":"2017-02-07T19:07:00.000Z","key":1486494420000,"doc_count":14},{"key_as_string":"2017-02-07T19:08:00.000Z","key":1486494480000,"doc_count":7},{"key_as_string":"2017-02-07T19:09:00.000Z","key":1486494540000,"doc_count":14},{"key_as_string":"2017-02-07T19:10:00.000Z","key":1486494600000,"doc_count":8},{"key_as_string":"2017-02-07T19:11:00.000Z","key":1486494660000,"doc_count":11},{"key_as_string":"2017-02-07T19:12:00.000Z","key":1486494720000,"doc_count":10},{"key_as_string":"2017-02-07T19:13:00.000Z","key":1486494780000,"doc_count":11},{"key_as_string":"2017-02-07T19:14:00.000Z","key":1486494840000,"doc_count":13},{"key_as_string":"2017-02-07T19:15:00.000Z","key":1486494900000,"doc_count":16},{"key_as_string":"2017-02-07T19:16:00.000Z","key":1486494960000,"doc_count":5},{"key_as_string":"2017-02-07T19:17:00.000Z","key":1486495020000,"doc_count":14},{"key_as_string":"2017-02-07T19:18:00.000Z","key":1486495080000,"doc_count":13},{"key_as_string":"2017-02-07T19:19:00.000Z","key":1486495140000,"doc_count":10},{"key_as_string":"2017-02-07T19:20:00.000Z","key":1486495200000,"doc_count":13},{"key_as_string":"2017-02-07T19:21:00.000Z","key":1486495260000,"doc_count":10},{"key_as_string":"2017-02-07T19:22:00.000Z","key":1486495320000,"doc_count":12},{"key_as_string":"2017-02-07T19:23:00.000Z","key":1486495380000,"doc_count":13},{"key_as_string":"2017-02-07T19:24:00.000Z","key":1486495440000,"doc_count":10},{"key_as_string":"2017-02-07T19:25:00.000Z","key":1486495500000,"doc_count":10},{"key_as_string":"2017-02-07T19:26:00.000Z","key":1486495560000,"doc_count":11},{"key_as_string":"2017-02-07T19:27:00.000Z","key":1486495620000,"doc_count":9},{"key_as_string":"2017-02-07T19:28:00.000Z","key":1486495680000,"doc_count":10},{"key_as_string":"2017-02-07T19:29:00.000Z","key":1486495740000,"doc_count":14},{"key_as_string":"2017-02-07T19:30:00.000Z","key":1486495800000,"doc_count":14},{"key_as_string":"2017-02-07T19:31:00.000Z","key":1486495860000,"doc_count":12},{"key_as_string":"2017-02-07T19:32:00.000Z","key":1486495920000,"doc_count":6},{"key_as_string":"2017-02-07T19:33:00.000Z","key":1486495980000,"doc_count":11},{"key_as_string":"2017-02-07T19:34:00.000Z","key":1486496040000,"doc_count":12},{"key_as_string":"2017-02-07T19:35:00.000Z","key":1486496100000,"doc_count":9},{"key_as_string":"2017-02-07T19:36:00.000Z","key":1486496160000,"doc_count":11},{"key_as_string":"2017-02-07T19:37:00.000Z","key":1486496220000,"doc_count":14},{"key_as_string":"2017-02-07T19:38:00.000Z","key":1486496280000,"doc_count":9},{"key_as_string":"2017-02-07T19:39:00.000Z","key":1486496340000,"doc_count":10},{"key_as_string":"2017-02-07T19:40:00.000Z","key":1486496400000,"doc_count":10},{"key_as_string":"2017-02-07T19:41:00.000Z","key":1486496460000,"doc_count":9},{"key_as_string":"2017-02-07T19:42:00.000Z","key":1486496520000,"doc_count":12},{"key_as_string":"2017-02-07T19:43:00.000Z","key":1486496580000,"doc_count":10},{"key_as_string":"2017-02-07T19:44:00.000Z","key":1486496640000,"doc_count":10},{"key_as_string":"2017-02-07T19:45:00.000Z","key":1486496700000,"doc_count":14},{"key_as_string":"2017-02-07T19:46:00.000Z","key":1486496760000,"doc_count":7},{"key_as_string":"2017-02-07T19:47:00.000Z","key":1486496820000,"doc_count":14},{"key_as_string":"2017-02-07T19:48:00.000Z","key":1486496880000,"doc_count":12},{"key_as_string":"2017-02-07T19:49:00.000Z","key":1486496940000,"doc_count":8},{"key_as_string":"2017-02-07T19:50:00.000Z","key":1486497000000,"doc_count":14},{"key_as_string":"2017-02-07T19:51:00.000Z","key":1486497060000,"doc_count":7},{"key_as_string":"2017-02-07T19:52:00.000Z","key":1486497120000,"doc_count":14},{"key_as_string":"2017-02-07T19:53:00.000Z","key":1486497180000,"doc_count":10},{"key_as_string":"2017-02-07T19:54:00.000Z","key":1486497240000,"doc_count":9},{"key_as_string":"2017-02-07T19:55:00.000Z","key":1486497300000,"doc_count":13},{"key_as_string":"2017-02-07T19:56:00.000Z","key":1486497360000,"doc_count":6},{"key_as_string":"2017-02-07T19:57:00.000Z","key":1486497420000,"doc_count":7},{"key_as_string":"2017-02-07T19:58:00.000Z","key":1486497480000,"doc_count":13},{"key_as_string":"2017-02-07T19:59:00.000Z","key":1486497540000,"doc_count":14},{"key_as_string":"2017-02-07T20:00:00.000Z","key":1486497600000,"doc_count":9},{"key_as_string":"2017-02-07T20:01:00.000Z","key":1486497660000,"doc_count":12},{"key_as_string":"2017-02-07T20:02:00.000Z","key":1486497720000,"doc_count":9},{"key_as_string":"2017-02-07T20:03:00.000Z","key":1486497780000,"doc_count":11},{"key_as_string":"2017-02-07T20:04:00.000Z","key":1486497840000,"doc_count":12},{"key_as_string":"2017-02-07T20:05:00.000Z","key":1486497900000,"doc_count":15},{"key_as_string":"2017-02-07T20:06:00.000Z","key":1486497960000,"doc_count":8},{"key_as_string":"2017-02-07T20:07:00.000Z","key":1486498020000,"doc_count":3},{"key_as_string":"2017-02-07T20:08:00.000Z","key":1486498080000,"doc_count":12},{"key_as_string":"2017-02-07T20:09:00.000Z","key":1486498140000,"doc_count":12},{"key_as_string":"2017-02-07T20:10:00.000Z","key":1486498200000,"doc_count":10},{"key_as_string":"2017-02-07T20:11:00.000Z","key":1486498260000,"doc_count":16},{"key_as_string":"2017-02-07T20:12:00.000Z","key":1486498320000,"doc_count":12},{"key_as_string":"2017-02-07T20:13:00.000Z","key":1486498380000,"doc_count":6},{"key_as_string":"2017-02-07T20:14:00.000Z","key":1486498440000,"doc_count":12},{"key_as_string":"2017-02-07T20:15:00.000Z","key":1486498500000,"doc_count":7},{"key_as_string":"2017-02-07T20:16:00.000Z","key":1486498560000,"doc_count":12},{"key_as_string":"2017-02-07T20:17:00.000Z","key":1486498620000,"doc_count":9},{"key_as_string":"2017-02-07T20:18:00.000Z","key":1486498680000,"doc_count":13},{"key_as_string":"2017-02-07T20:19:00.000Z","key":1486498740000,"doc_count":11},{"key_as_string":"2017-02-07T20:20:00.000Z","key":1486498800000,"doc_count":8},{"key_as_string":"2017-02-07T20:21:00.000Z","key":1486498860000,"doc_count":11},{"key_as_string":"2017-02-07T20:22:00.000Z","key":1486498920000,"doc_count":7},{"key_as_string":"2017-02-07T20:23:00.000Z","key":1486498980000,"doc_count":11},{"key_as_string":"2017-02-07T20:24:00.000Z","key":1486499040000,"doc_count":11},{"key_as_string":"2017-02-07T20:25:00.000Z","key":1486499100000,"doc_count":7},{"key_as_string":"2017-02-07T20:26:00.000Z","key":1486499160000,"doc_count":11},{"key_as_string":"2017-02-07T20:27:00.000Z","key":1486499220000,"doc_count":12},{"key_as_string":"2017-02-07T20:28:00.000Z","key":1486499280000,"doc_count":13},{"key_as_string":"2017-02-07T20:29:00.000Z","key":1486499340000,"doc_count":11},{"key_as_string":"2017-02-07T20:30:00.000Z","key":1486499400000,"doc_count":6},{"key_as_string":"2017-02-07T20:31:00.000Z","key":1486499460000,"doc_count":12},{"key_as_string":"2017-02-07T20:32:00.000Z","key":1486499520000,"doc_count":9},{"key_as_string":"2017-02-07T20:33:00.000Z","key":1486499580000,"doc_count":13},{"key_as_string":"2017-02-07T20:34:00.000Z","key":1486499640000,"doc_count":11},{"key_as_string":"2017-02-07T20:35:00.000Z","key":1486499700000,"doc_count":16},{"key_as_string":"2017-02-07T20:36:00.000Z","key":1486499760000,"doc_count":9},{"key_as_string":"2017-02-07T20:37:00.000Z","key":1486499820000,"doc_count":7},{"key_as_string":"2017-02-07T20:38:00.000Z","key":1486499880000,"doc_count":14},{"key_as_string":"2017-02-07T20:39:00.000Z","key":1486499940000,"doc_count":10},{"key_as_string":"2017-02-07T20:40:00.000Z","key":1486500000000,"doc_count":9},{"key_as_string":"2017-02-07T20:41:00.000Z","key":1486500060000,"doc_count":11},{"key_as_string":"2017-02-07T20:42:00.000Z","key":1486500120000,"doc_count":15},{"key_as_string":"2017-02-07T20:43:00.000Z","key":1486500180000,"doc_count":8},{"key_as_string":"2017-02-07T20:44:00.000Z","key":1486500240000,"doc_count":16},{"key_as_string":"2017-02-07T20:45:00.000Z","key":1486500300000,"doc_count":8},{"key_as_string":"2017-02-07T20:46:00.000Z","key":1486500360000,"doc_count":12},{"key_as_string":"2017-02-07T20:47:00.000Z","key":1486500420000,"doc_count":11},{"key_as_string":"2017-02-07T20:48:00.000Z","key":1486500480000,"doc_count":6},{"key_as_string":"2017-02-07T20:49:00.000Z","key":1486500540000,"doc_count":10},{"key_as_string":"2017-02-07T20:50:00.000Z","key":1486500600000,"doc_count":8},{"key_as_string":"2017-02-07T20:51:00.000Z","key":1486500660000,"doc_count":15},{"key_as_string":"2017-02-07T20:52:00.000Z","key":1486500720000,"doc_count":9},{"key_as_string":"2017-02-07T20:53:00.000Z","key":1486500780000,"doc_count":11},{"key_as_string":"2017-02-07T20:54:00.000Z","key":1486500840000,"doc_count":13},{"key_as_string":"2017-02-07T20:55:00.000Z","key":1486500900000,"doc_count":9},{"key_as_string":"2017-02-07T20:56:00.000Z","key":1486500960000,"doc_count":5},{"key_as_string":"2017-02-07T20:57:00.000Z","key":1486501020000,"doc_count":10},{"key_as_string":"2017-02-07T20:58:00.000Z","key":1486501080000,"doc_count":13},{"key_as_string":"2017-02-07T20:59:00.000Z","key":1486501140000,"doc_count":14},{"key_as_string":"2017-02-07T21:00:00.000Z","key":1486501200000,"doc_count":13},{"key_as_string":"2017-02-07T21:01:00.000Z","key":1486501260000,"doc_count":8},{"key_as_string":"2017-02-07T21:02:00.000Z","key":1486501320000,"doc_count":11},{"key_as_string":"2017-02-07T21:03:00.000Z","key":1486501380000,"doc_count":9},{"key_as_string":"2017-02-07T21:04:00.000Z","key":1486501440000,"doc_count":16},{"key_as_string":"2017-02-07T21:05:00.000Z","key":1486501500000,"doc_count":10},{"key_as_string":"2017-02-07T21:06:00.000Z","key":1486501560000,"doc_count":8},{"key_as_string":"2017-02-07T21:07:00.000Z","key":1486501620000,"doc_count":16},{"key_as_string":"2017-02-07T21:08:00.000Z","key":1486501680000,"doc_count":13},{"key_as_string":"2017-02-07T21:09:00.000Z","key":1486501740000,"doc_count":10},{"key_as_string":"2017-02-07T21:10:00.000Z","key":1486501800000,"doc_count":11},{"key_as_string":"2017-02-07T21:11:00.000Z","key":1486501860000,"doc_count":8},{"key_as_string":"2017-02-07T21:12:00.000Z","key":1486501920000,"doc_count":12},{"key_as_string":"2017-02-07T21:13:00.000Z","key":1486501980000,"doc_count":17},{"key_as_string":"2017-02-07T21:14:00.000Z","key":1486502040000,"doc_count":6},{"key_as_string":"2017-02-07T21:15:00.000Z","key":1486502100000,"doc_count":12},{"key_as_string":"2017-02-07T21:16:00.000Z","key":1486502160000,"doc_count":9},{"key_as_string":"2017-02-07T21:17:00.000Z","key":1486502220000,"doc_count":8},{"key_as_string":"2017-02-07T21:18:00.000Z","key":1486502280000,"doc_count":8},{"key_as_string":"2017-02-07T21:19:00.000Z","key":1486502340000,"doc_count":11},{"key_as_string":"2017-02-07T21:20:00.000Z","key":1486502400000,"doc_count":11},{"key_as_string":"2017-02-07T21:21:00.000Z","key":1486502460000,"doc_count":10},{"key_as_string":"2017-02-07T21:22:00.000Z","key":1486502520000,"doc_count":10},{"key_as_string":"2017-02-07T21:23:00.000Z","key":1486502580000,"doc_count":13},{"key_as_string":"2017-02-07T21:24:00.000Z","key":1486502640000,"doc_count":10},{"key_as_string":"2017-02-07T21:25:00.000Z","key":1486502700000,"doc_count":11},{"key_as_string":"2017-02-07T21:26:00.000Z","key":1486502760000,"doc_count":7},{"key_as_string":"2017-02-07T21:27:00.000Z","key":1486502820000,"doc_count":15},{"key_as_string":"2017-02-07T21:28:00.000Z","key":1486502880000,"doc_count":14},{"key_as_string":"2017-02-07T21:29:00.000Z","key":1486502940000,"doc_count":8},{"key_as_string":"2017-02-07T21:30:00.000Z","key":1486503000000,"doc_count":7},{"key_as_string":"2017-02-07T21:31:00.000Z","key":1486503060000,"doc_count":12},{"key_as_string":"2017-02-07T21:32:00.000Z","key":1486503120000,"doc_count":10},{"key_as_string":"2017-02-07T21:33:00.000Z","key":1486503180000,"doc_count":10},{"key_as_string":"2017-02-07T21:34:00.000Z","key":1486503240000,"doc_count":14},{"key_as_string":"2017-02-07T21:35:00.000Z","key":1486503300000,"doc_count":9},{"key_as_string":"2017-02-07T21:36:00.000Z","key":1486503360000,"doc_count":9},{"key_as_string":"2017-02-07T21:37:00.000Z","key":1486503420000,"doc_count":13},{"key_as_string":"2017-02-07T21:38:00.000Z","key":1486503480000,"doc_count":7},{"key_as_string":"2017-02-07T21:39:00.000Z","key":1486503540000,"doc_count":6},{"key_as_string":"2017-02-07T21:40:00.000Z","key":1486503600000,"doc_count":16},{"key_as_string":"2017-02-07T21:41:00.000Z","key":1486503660000,"doc_count":13},{"key_as_string":"2017-02-07T21:42:00.000Z","key":1486503720000,"doc_count":9},{"key_as_string":"2017-02-07T21:43:00.000Z","key":1486503780000,"doc_count":10},{"key_as_string":"2017-02-07T21:44:00.000Z","key":1486503840000,"doc_count":9},{"key_as_string":"2017-02-07T21:45:00.000Z","key":1486503900000,"doc_count":10},{"key_as_string":"2017-02-07T21:46:00.000Z","key":1486503960000,"doc_count":13},{"key_as_string":"2017-02-07T21:47:00.000Z","key":1486504020000,"doc_count":11},{"key_as_string":"2017-02-07T21:48:00.000Z","key":1486504080000,"doc_count":8},{"key_as_string":"2017-02-07T21:49:00.000Z","key":1486504140000,"doc_count":11},{"key_as_string":"2017-02-07T21:50:00.000Z","key":1486504200000,"doc_count":14},{"key_as_string":"2017-02-07T21:51:00.000Z","key":1486504260000,"doc_count":7},{"key_as_string":"2017-02-07T21:52:00.000Z","key":1486504320000,"doc_count":11},{"key_as_string":"2017-02-07T21:53:00.000Z","key":1486504380000,"doc_count":9},{"key_as_string":"2017-02-07T21:54:00.000Z","key":1486504440000,"doc_count":10},{"key_as_string":"2017-02-07T21:55:00.000Z","key":1486504500000,"doc_count":11},{"key_as_string":"2017-02-07T21:56:00.000Z","key":1486504560000,"doc_count":12},{"key_as_string":"2017-02-07T21:57:00.000Z","key":1486504620000,"doc_count":14},{"key_as_string":"2017-02-07T21:58:00.000Z","key":1486504680000,"doc_count":9},{"key_as_string":"2017-02-07T21:59:00.000Z","key":1486504740000,"doc_count":7},{"key_as_string":"2017-02-07T22:00:00.000Z","key":1486504800000,"doc_count":14},{"key_as_string":"2017-02-07T22:01:00.000Z","key":1486504860000,"doc_count":12},{"key_as_string":"2017-02-07T22:02:00.000Z","key":1486504920000,"doc_count":7},{"key_as_string":"2017-02-07T22:03:00.000Z","key":1486504980000,"doc_count":16},{"key_as_string":"2017-02-07T22:04:00.000Z","key":1486505040000,"doc_count":9},{"key_as_string":"2017-02-07T22:05:00.000Z","key":1486505100000,"doc_count":10},{"key_as_string":"2017-02-07T22:06:00.000Z","key":1486505160000,"doc_count":11},{"key_as_string":"2017-02-07T22:07:00.000Z","key":1486505220000,"doc_count":16},{"key_as_string":"2017-02-07T22:08:00.000Z","key":1486505280000,"doc_count":9},{"key_as_string":"2017-02-07T22:09:00.000Z","key":1486505340000,"doc_count":7},{"key_as_string":"2017-02-07T22:10:00.000Z","key":1486505400000,"doc_count":15},{"key_as_string":"2017-02-07T22:11:00.000Z","key":1486505460000,"doc_count":12},{"key_as_string":"2017-02-07T22:12:00.000Z","key":1486505520000,"doc_count":8},{"key_as_string":"2017-02-07T22:13:00.000Z","key":1486505580000,"doc_count":12},{"key_as_string":"2017-02-07T22:14:00.000Z","key":1486505640000,"doc_count":15},{"key_as_string":"2017-02-07T22:15:00.000Z","key":1486505700000,"doc_count":10},{"key_as_string":"2017-02-07T22:16:00.000Z","key":1486505760000,"doc_count":11},{"key_as_string":"2017-02-07T22:17:00.000Z","key":1486505820000,"doc_count":6},{"key_as_string":"2017-02-07T22:18:00.000Z","key":1486505880000,"doc_count":13},{"key_as_string":"2017-02-07T22:19:00.000Z","key":1486505940000,"doc_count":8},{"key_as_string":"2017-02-07T22:20:00.000Z","key":1486506000000,"doc_count":5},{"key_as_string":"2017-02-07T22:21:00.000Z","key":1486506060000,"doc_count":15},{"key_as_string":"2017-02-07T22:22:00.000Z","key":1486506120000,"doc_count":10},{"key_as_string":"2017-02-07T22:23:00.000Z","key":1486506180000,"doc_count":10},{"key_as_string":"2017-02-07T22:24:00.000Z","key":1486506240000,"doc_count":14},{"key_as_string":"2017-02-07T22:25:00.000Z","key":1486506300000,"doc_count":8},{"key_as_string":"2017-02-07T22:26:00.000Z","key":1486506360000,"doc_count":12},{"key_as_string":"2017-02-07T22:27:00.000Z","key":1486506420000,"doc_count":6},{"key_as_string":"2017-02-07T22:28:00.000Z","key":1486506480000,"doc_count":12},{"key_as_string":"2017-02-07T22:29:00.000Z","key":1486506540000,"doc_count":14},{"key_as_string":"2017-02-07T22:30:00.000Z","key":1486506600000,"doc_count":8},{"key_as_string":"2017-02-07T22:31:00.000Z","key":1486506660000,"doc_count":10},{"key_as_string":"2017-02-07T22:32:00.000Z","key":1486506720000,"doc_count":15},{"key_as_string":"2017-02-07T22:33:00.000Z","key":1486506780000,"doc_count":9},{"key_as_string":"2017-02-07T22:34:00.000Z","key":1486506840000,"doc_count":7},{"key_as_string":"2017-02-07T22:35:00.000Z","key":1486506900000,"doc_count":12},{"key_as_string":"2017-02-07T22:36:00.000Z","key":1486506960000,"doc_count":9},{"key_as_string":"2017-02-07T22:37:00.000Z","key":1486507020000,"doc_count":15},{"key_as_string":"2017-02-07T22:38:00.000Z","key":1486507080000,"doc_count":6},{"key_as_string":"2017-02-07T22:39:00.000Z","key":1486507140000,"doc_count":12},{"key_as_string":"2017-02-07T22:40:00.000Z","key":1486507200000,"doc_count":13},{"key_as_string":"2017-02-07T22:41:00.000Z","key":1486507260000,"doc_count":8},{"key_as_string":"2017-02-07T22:42:00.000Z","key":1486507320000,"doc_count":13},{"key_as_string":"2017-02-07T22:43:00.000Z","key":1486507380000,"doc_count":7},{"key_as_string":"2017-02-07T22:44:00.000Z","key":1486507440000,"doc_count":10},{"key_as_string":"2017-02-07T22:45:00.000Z","key":1486507500000,"doc_count":7},{"key_as_string":"2017-02-07T22:46:00.000Z","key":1486507560000,"doc_count":9},{"key_as_string":"2017-02-07T22:47:00.000Z","key":1486507620000,"doc_count":12},{"key_as_string":"2017-02-07T22:48:00.000Z","key":1486507680000,"doc_count":9},{"key_as_string":"2017-02-07T22:49:00.000Z","key":1486507740000,"doc_count":6},{"key_as_string":"2017-02-07T22:50:00.000Z","key":1486507800000,"doc_count":12},{"key_as_string":"2017-02-07T22:51:00.000Z","key":1486507860000,"doc_count":12},{"key_as_string":"2017-02-07T22:52:00.000Z","key":1486507920000,"doc_count":9},{"key_as_string":"2017-02-07T22:53:00.000Z","key":1486507980000,"doc_count":10},{"key_as_string":"2017-02-07T22:54:00.000Z","key":1486508040000,"doc_count":12},{"key_as_string":"2017-02-07T22:55:00.000Z","key":1486508100000,"doc_count":8},{"key_as_string":"2017-02-07T22:56:00.000Z","key":1486508160000,"doc_count":9},{"key_as_string":"2017-02-07T22:57:00.000Z","key":1486508220000,"doc_count":10},{"key_as_string":"2017-02-07T22:58:00.000Z","key":1486508280000,"doc_count":13},{"key_as_string":"2017-02-07T22:59:00.000Z","key":1486508340000,"doc_count":12},{"key_as_string":"2017-02-07T23:00:00.000Z","key":1486508400000,"doc_count":9},{"key_as_string":"2017-02-07T23:01:00.000Z","key":1486508460000,"doc_count":9},{"key_as_string":"2017-02-07T23:02:00.000Z","key":1486508520000,"doc_count":10},{"key_as_string":"2017-02-07T23:03:00.000Z","key":1486508580000,"doc_count":14},{"key_as_string":"2017-02-07T23:04:00.000Z","key":1486508640000,"doc_count":9},{"key_as_string":"2017-02-07T23:05:00.000Z","key":1486508700000,"doc_count":11},{"key_as_string":"2017-02-07T23:06:00.000Z","key":1486508760000,"doc_count":10},{"key_as_string":"2017-02-07T23:07:00.000Z","key":1486508820000,"doc_count":16},{"key_as_string":"2017-02-07T23:08:00.000Z","key":1486508880000,"doc_count":12},{"key_as_string":"2017-02-07T23:09:00.000Z","key":1486508940000,"doc_count":11},{"key_as_string":"2017-02-07T23:10:00.000Z","key":1486509000000,"doc_count":9},{"key_as_string":"2017-02-07T23:11:00.000Z","key":1486509060000,"doc_count":13},{"key_as_string":"2017-02-07T23:12:00.000Z","key":1486509120000,"doc_count":5},{"key_as_string":"2017-02-07T23:13:00.000Z","key":1486509180000,"doc_count":9},{"key_as_string":"2017-02-07T23:14:00.000Z","key":1486509240000,"doc_count":11},{"key_as_string":"2017-02-07T23:15:00.000Z","key":1486509300000,"doc_count":14},{"key_as_string":"2017-02-07T23:16:00.000Z","key":1486509360000,"doc_count":11},{"key_as_string":"2017-02-07T23:17:00.000Z","key":1486509420000,"doc_count":8},{"key_as_string":"2017-02-07T23:18:00.000Z","key":1486509480000,"doc_count":12},{"key_as_string":"2017-02-07T23:19:00.000Z","key":1486509540000,"doc_count":8},{"key_as_string":"2017-02-07T23:20:00.000Z","key":1486509600000,"doc_count":7},{"key_as_string":"2017-02-07T23:21:00.000Z","key":1486509660000,"doc_count":13},{"key_as_string":"2017-02-07T23:22:00.000Z","key":1486509720000,"doc_count":13},{"key_as_string":"2017-02-07T23:23:00.000Z","key":1486509780000,"doc_count":5},{"key_as_string":"2017-02-07T23:24:00.000Z","key":1486509840000,"doc_count":13},{"key_as_string":"2017-02-07T23:25:00.000Z","key":1486509900000,"doc_count":5},{"key_as_string":"2017-02-07T23:26:00.000Z","key":1486509960000,"doc_count":11},{"key_as_string":"2017-02-07T23:27:00.000Z","key":1486510020000,"doc_count":11},{"key_as_string":"2017-02-07T23:28:00.000Z","key":1486510080000,"doc_count":12},{"key_as_string":"2017-02-07T23:29:00.000Z","key":1486510140000,"doc_count":5},{"key_as_string":"2017-02-07T23:30:00.000Z","key":1486510200000,"doc_count":11},{"key_as_string":"2017-02-07T23:31:00.000Z","key":1486510260000,"doc_count":6},{"key_as_string":"2017-02-07T23:32:00.000Z","key":1486510320000,"doc_count":15},{"key_as_string":"2017-02-07T23:33:00.000Z","key":1486510380000,"doc_count":13},{"key_as_string":"2017-02-07T23:34:00.000Z","key":1486510440000,"doc_count":15},{"key_as_string":"2017-02-07T23:35:00.000Z","key":1486510500000,"doc_count":8},{"key_as_string":"2017-02-07T23:36:00.000Z","key":1486510560000,"doc_count":11},{"key_as_string":"2017-02-07T23:37:00.000Z","key":1486510620000,"doc_count":15},{"key_as_string":"2017-02-07T23:38:00.000Z","key":1486510680000,"doc_count":7},{"key_as_string":"2017-02-07T23:39:00.000Z","key":1486510740000,"doc_count":11},{"key_as_string":"2017-02-07T23:40:00.000Z","key":1486510800000,"doc_count":7},{"key_as_string":"2017-02-07T23:41:00.000Z","key":1486510860000,"doc_count":9},{"key_as_string":"2017-02-07T23:42:00.000Z","key":1486510920000,"doc_count":11},{"key_as_string":"2017-02-07T23:43:00.000Z","key":1486510980000,"doc_count":12},{"key_as_string":"2017-02-07T23:44:00.000Z","key":1486511040000,"doc_count":19},{"key_as_string":"2017-02-07T23:45:00.000Z","key":1486511100000,"doc_count":6},{"key_as_string":"2017-02-07T23:46:00.000Z","key":1486511160000,"doc_count":12},{"key_as_string":"2017-02-07T23:47:00.000Z","key":1486511220000,"doc_count":7},{"key_as_string":"2017-02-07T23:48:00.000Z","key":1486511280000,"doc_count":19},{"key_as_string":"2017-02-07T23:49:00.000Z","key":1486511340000,"doc_count":4},{"key_as_string":"2017-02-07T23:50:00.000Z","key":1486511400000,"doc_count":15},{"key_as_string":"2017-02-07T23:51:00.000Z","key":1486511460000,"doc_count":9},{"key_as_string":"2017-02-07T23:52:00.000Z","key":1486511520000,"doc_count":7},{"key_as_string":"2017-02-07T23:53:00.000Z","key":1486511580000,"doc_count":11},{"key_as_string":"2017-02-07T23:54:00.000Z","key":1486511640000,"doc_count":11},{"key_as_string":"2017-02-07T23:55:00.000Z","key":1486511700000,"doc_count":10},{"key_as_string":"2017-02-07T23:56:00.000Z","key":1486511760000,"doc_count":7},{"key_as_string":"2017-02-07T23:57:00.000Z","key":1486511820000,"doc_count":16},{"key_as_string":"2017-02-07T23:58:00.000Z","key":1486511880000,"doc_count":9},{"key_as_string":"2017-02-07T23:59:00.000Z","key":1486511940000,"doc_count":13},{"key_as_string":"2017-02-08T00:00:00.000Z","key":1486512000000,"doc_count":11},{"key_as_string":"2017-02-08T00:01:00.000Z","key":1486512060000,"doc_count":8},{"key_as_string":"2017-02-08T00:02:00.000Z","key":1486512120000,"doc_count":11},{"key_as_string":"2017-02-08T00:03:00.000Z","key":1486512180000,"doc_count":11},{"key_as_string":"2017-02-08T00:04:00.000Z","key":1486512240000,"doc_count":10},{"key_as_string":"2017-02-08T00:05:00.000Z","key":1486512300000,"doc_count":11},{"key_as_string":"2017-02-08T00:06:00.000Z","key":1486512360000,"doc_count":9},{"key_as_string":"2017-02-08T00:07:00.000Z","key":1486512420000,"doc_count":10},{"key_as_string":"2017-02-08T00:08:00.000Z","key":1486512480000,"doc_count":9},{"key_as_string":"2017-02-08T00:09:00.000Z","key":1486512540000,"doc_count":12},{"key_as_string":"2017-02-08T00:10:00.000Z","key":1486512600000,"doc_count":7},{"key_as_string":"2017-02-08T00:11:00.000Z","key":1486512660000,"doc_count":14},{"key_as_string":"2017-02-08T00:12:00.000Z","key":1486512720000,"doc_count":10},{"key_as_string":"2017-02-08T00:13:00.000Z","key":1486512780000,"doc_count":8},{"key_as_string":"2017-02-08T00:14:00.000Z","key":1486512840000,"doc_count":7},{"key_as_string":"2017-02-08T00:15:00.000Z","key":1486512900000,"doc_count":10},{"key_as_string":"2017-02-08T00:16:00.000Z","key":1486512960000,"doc_count":12},{"key_as_string":"2017-02-08T00:17:00.000Z","key":1486513020000,"doc_count":13},{"key_as_string":"2017-02-08T00:18:00.000Z","key":1486513080000,"doc_count":7},{"key_as_string":"2017-02-08T00:19:00.000Z","key":1486513140000,"doc_count":7},{"key_as_string":"2017-02-08T00:20:00.000Z","key":1486513200000,"doc_count":7},{"key_as_string":"2017-02-08T00:21:00.000Z","key":1486513260000,"doc_count":9},{"key_as_string":"2017-02-08T00:22:00.000Z","key":1486513320000,"doc_count":13},{"key_as_string":"2017-02-08T00:23:00.000Z","key":1486513380000,"doc_count":10},{"key_as_string":"2017-02-08T00:24:00.000Z","key":1486513440000,"doc_count":8},{"key_as_string":"2017-02-08T00:25:00.000Z","key":1486513500000,"doc_count":13},{"key_as_string":"2017-02-08T00:26:00.000Z","key":1486513560000,"doc_count":10},{"key_as_string":"2017-02-08T00:27:00.000Z","key":1486513620000,"doc_count":8},{"key_as_string":"2017-02-08T00:28:00.000Z","key":1486513680000,"doc_count":15},{"key_as_string":"2017-02-08T00:29:00.000Z","key":1486513740000,"doc_count":7},{"key_as_string":"2017-02-08T00:30:00.000Z","key":1486513800000,"doc_count":9},{"key_as_string":"2017-02-08T00:31:00.000Z","key":1486513860000,"doc_count":13},{"key_as_string":"2017-02-08T00:32:00.000Z","key":1486513920000,"doc_count":8},{"key_as_string":"2017-02-08T00:33:00.000Z","key":1486513980000,"doc_count":12},{"key_as_string":"2017-02-08T00:34:00.000Z","key":1486514040000,"doc_count":13},{"key_as_string":"2017-02-08T00:35:00.000Z","key":1486514100000,"doc_count":9},{"key_as_string":"2017-02-08T00:36:00.000Z","key":1486514160000,"doc_count":12},{"key_as_string":"2017-02-08T00:37:00.000Z","key":1486514220000,"doc_count":9},{"key_as_string":"2017-02-08T00:38:00.000Z","key":1486514280000,"doc_count":9},{"key_as_string":"2017-02-08T00:39:00.000Z","key":1486514340000,"doc_count":16},{"key_as_string":"2017-02-08T00:40:00.000Z","key":1486514400000,"doc_count":10},{"key_as_string":"2017-02-08T00:41:00.000Z","key":1486514460000,"doc_count":13},{"key_as_string":"2017-02-08T00:42:00.000Z","key":1486514520000,"doc_count":6},{"key_as_string":"2017-02-08T00:43:00.000Z","key":1486514580000,"doc_count":16},{"key_as_string":"2017-02-08T00:44:00.000Z","key":1486514640000,"doc_count":16},{"key_as_string":"2017-02-08T00:45:00.000Z","key":1486514700000,"doc_count":6},{"key_as_string":"2017-02-08T00:46:00.000Z","key":1486514760000,"doc_count":11},{"key_as_string":"2017-02-08T00:47:00.000Z","key":1486514820000,"doc_count":11},{"key_as_string":"2017-02-08T00:48:00.000Z","key":1486514880000,"doc_count":9},{"key_as_string":"2017-02-08T00:49:00.000Z","key":1486514940000,"doc_count":11},{"key_as_string":"2017-02-08T00:50:00.000Z","key":1486515000000,"doc_count":9},{"key_as_string":"2017-02-08T00:51:00.000Z","key":1486515060000,"doc_count":12},{"key_as_string":"2017-02-08T00:52:00.000Z","key":1486515120000,"doc_count":10},{"key_as_string":"2017-02-08T00:53:00.000Z","key":1486515180000,"doc_count":13},{"key_as_string":"2017-02-08T00:54:00.000Z","key":1486515240000,"doc_count":9},{"key_as_string":"2017-02-08T00:55:00.000Z","key":1486515300000,"doc_count":15},{"key_as_string":"2017-02-08T00:56:00.000Z","key":1486515360000,"doc_count":11},{"key_as_string":"2017-02-08T00:57:00.000Z","key":1486515420000,"doc_count":12},{"key_as_string":"2017-02-08T00:58:00.000Z","key":1486515480000,"doc_count":17},{"key_as_string":"2017-02-08T00:59:00.000Z","key":1486515540000,"doc_count":11},{"key_as_string":"2017-02-08T01:00:00.000Z","key":1486515600000,"doc_count":14},{"key_as_string":"2017-02-08T01:01:00.000Z","key":1486515660000,"doc_count":12},{"key_as_string":"2017-02-08T01:02:00.000Z","key":1486515720000,"doc_count":8},{"key_as_string":"2017-02-08T01:03:00.000Z","key":1486515780000,"doc_count":12},{"key_as_string":"2017-02-08T01:04:00.000Z","key":1486515840000,"doc_count":8},{"key_as_string":"2017-02-08T01:05:00.000Z","key":1486515900000,"doc_count":13},{"key_as_string":"2017-02-08T01:06:00.000Z","key":1486515960000,"doc_count":9},{"key_as_string":"2017-02-08T01:07:00.000Z","key":1486516020000,"doc_count":7},{"key_as_string":"2017-02-08T01:08:00.000Z","key":1486516080000,"doc_count":14},{"key_as_string":"2017-02-08T01:09:00.000Z","key":1486516140000,"doc_count":9},{"key_as_string":"2017-02-08T01:10:00.000Z","key":1486516200000,"doc_count":10},{"key_as_string":"2017-02-08T01:11:00.000Z","key":1486516260000,"doc_count":12},{"key_as_string":"2017-02-08T01:12:00.000Z","key":1486516320000,"doc_count":8},{"key_as_string":"2017-02-08T01:13:00.000Z","key":1486516380000,"doc_count":9},{"key_as_string":"2017-02-08T01:14:00.000Z","key":1486516440000,"doc_count":11},{"key_as_string":"2017-02-08T01:15:00.000Z","key":1486516500000,"doc_count":10},{"key_as_string":"2017-02-08T01:16:00.000Z","key":1486516560000,"doc_count":16},{"key_as_string":"2017-02-08T01:17:00.000Z","key":1486516620000,"doc_count":12},{"key_as_string":"2017-02-08T01:18:00.000Z","key":1486516680000,"doc_count":7},{"key_as_string":"2017-02-08T01:19:00.000Z","key":1486516740000,"doc_count":7},{"key_as_string":"2017-02-08T01:20:00.000Z","key":1486516800000,"doc_count":12},{"key_as_string":"2017-02-08T01:21:00.000Z","key":1486516860000,"doc_count":10},{"key_as_string":"2017-02-08T01:22:00.000Z","key":1486516920000,"doc_count":15},{"key_as_string":"2017-02-08T01:23:00.000Z","key":1486516980000,"doc_count":9},{"key_as_string":"2017-02-08T01:24:00.000Z","key":1486517040000,"doc_count":15},{"key_as_string":"2017-02-08T01:25:00.000Z","key":1486517100000,"doc_count":9},{"key_as_string":"2017-02-08T01:26:00.000Z","key":1486517160000,"doc_count":15},{"key_as_string":"2017-02-08T01:27:00.000Z","key":1486517220000,"doc_count":11},{"key_as_string":"2017-02-08T01:28:00.000Z","key":1486517280000,"doc_count":15},{"key_as_string":"2017-02-08T01:29:00.000Z","key":1486517340000,"doc_count":17},{"key_as_string":"2017-02-08T01:30:00.000Z","key":1486517400000,"doc_count":5},{"key_as_string":"2017-02-08T01:31:00.000Z","key":1486517460000,"doc_count":10},{"key_as_string":"2017-02-08T01:32:00.000Z","key":1486517520000,"doc_count":9},{"key_as_string":"2017-02-08T01:33:00.000Z","key":1486517580000,"doc_count":13},{"key_as_string":"2017-02-08T01:34:00.000Z","key":1486517640000,"doc_count":17},{"key_as_string":"2017-02-08T01:35:00.000Z","key":1486517700000,"doc_count":8},{"key_as_string":"2017-02-08T01:36:00.000Z","key":1486517760000,"doc_count":7},{"key_as_string":"2017-02-08T01:37:00.000Z","key":1486517820000,"doc_count":14},{"key_as_string":"2017-02-08T01:38:00.000Z","key":1486517880000,"doc_count":11},{"key_as_string":"2017-02-08T01:39:00.000Z","key":1486517940000,"doc_count":4},{"key_as_string":"2017-02-08T01:40:00.000Z","key":1486518000000,"doc_count":13},{"key_as_string":"2017-02-08T01:41:00.000Z","key":1486518060000,"doc_count":13},{"key_as_string":"2017-02-08T01:42:00.000Z","key":1486518120000,"doc_count":6},{"key_as_string":"2017-02-08T01:43:00.000Z","key":1486518180000,"doc_count":12},{"key_as_string":"2017-02-08T01:44:00.000Z","key":1486518240000,"doc_count":13},{"key_as_string":"2017-02-08T01:45:00.000Z","key":1486518300000,"doc_count":9},{"key_as_string":"2017-02-08T01:46:00.000Z","key":1486518360000,"doc_count":15},{"key_as_string":"2017-02-08T01:47:00.000Z","key":1486518420000,"doc_count":14},{"key_as_string":"2017-02-08T01:48:00.000Z","key":1486518480000,"doc_count":7},{"key_as_string":"2017-02-08T01:49:00.000Z","key":1486518540000,"doc_count":15},{"key_as_string":"2017-02-08T01:50:00.000Z","key":1486518600000,"doc_count":10},{"key_as_string":"2017-02-08T01:51:00.000Z","key":1486518660000,"doc_count":9},{"key_as_string":"2017-02-08T01:52:00.000Z","key":1486518720000,"doc_count":7},{"key_as_string":"2017-02-08T01:53:00.000Z","key":1486518780000,"doc_count":14},{"key_as_string":"2017-02-08T01:54:00.000Z","key":1486518840000,"doc_count":10},{"key_as_string":"2017-02-08T01:55:00.000Z","key":1486518900000,"doc_count":9},{"key_as_string":"2017-02-08T01:56:00.000Z","key":1486518960000,"doc_count":11},{"key_as_string":"2017-02-08T01:57:00.000Z","key":1486519020000,"doc_count":7},{"key_as_string":"2017-02-08T01:58:00.000Z","key":1486519080000,"doc_count":12},{"key_as_string":"2017-02-08T01:59:00.000Z","key":1486519140000,"doc_count":7},{"key_as_string":"2017-02-08T02:00:00.000Z","key":1486519200000,"doc_count":16},{"key_as_string":"2017-02-08T02:01:00.000Z","key":1486519260000,"doc_count":8},{"key_as_string":"2017-02-08T02:02:00.000Z","key":1486519320000,"doc_count":5},{"key_as_string":"2017-02-08T02:03:00.000Z","key":1486519380000,"doc_count":12},{"key_as_string":"2017-02-08T02:04:00.000Z","key":1486519440000,"doc_count":11},{"key_as_string":"2017-02-08T02:05:00.000Z","key":1486519500000,"doc_count":12},{"key_as_string":"2017-02-08T02:06:00.000Z","key":1486519560000,"doc_count":12},{"key_as_string":"2017-02-08T02:07:00.000Z","key":1486519620000,"doc_count":9},{"key_as_string":"2017-02-08T02:08:00.000Z","key":1486519680000,"doc_count":11},{"key_as_string":"2017-02-08T02:09:00.000Z","key":1486519740000,"doc_count":16},{"key_as_string":"2017-02-08T02:10:00.000Z","key":1486519800000,"doc_count":9},{"key_as_string":"2017-02-08T02:11:00.000Z","key":1486519860000,"doc_count":8},{"key_as_string":"2017-02-08T02:12:00.000Z","key":1486519920000,"doc_count":9},{"key_as_string":"2017-02-08T02:13:00.000Z","key":1486519980000,"doc_count":8},{"key_as_string":"2017-02-08T02:14:00.000Z","key":1486520040000,"doc_count":12},{"key_as_string":"2017-02-08T02:15:00.000Z","key":1486520100000,"doc_count":8},{"key_as_string":"2017-02-08T02:16:00.000Z","key":1486520160000,"doc_count":9},{"key_as_string":"2017-02-08T02:17:00.000Z","key":1486520220000,"doc_count":11},{"key_as_string":"2017-02-08T02:18:00.000Z","key":1486520280000,"doc_count":8},{"key_as_string":"2017-02-08T02:19:00.000Z","key":1486520340000,"doc_count":9},{"key_as_string":"2017-02-08T02:20:00.000Z","key":1486520400000,"doc_count":7},{"key_as_string":"2017-02-08T02:21:00.000Z","key":1486520460000,"doc_count":15},{"key_as_string":"2017-02-08T02:22:00.000Z","key":1486520520000,"doc_count":6},{"key_as_string":"2017-02-08T02:23:00.000Z","key":1486520580000,"doc_count":11},{"key_as_string":"2017-02-08T02:24:00.000Z","key":1486520640000,"doc_count":14},{"key_as_string":"2017-02-08T02:25:00.000Z","key":1486520700000,"doc_count":8},{"key_as_string":"2017-02-08T02:26:00.000Z","key":1486520760000,"doc_count":11},{"key_as_string":"2017-02-08T02:27:00.000Z","key":1486520820000,"doc_count":15},{"key_as_string":"2017-02-08T02:28:00.000Z","key":1486520880000,"doc_count":6},{"key_as_string":"2017-02-08T02:29:00.000Z","key":1486520940000,"doc_count":5},{"key_as_string":"2017-02-08T02:30:00.000Z","key":1486521000000,"doc_count":18},{"key_as_string":"2017-02-08T02:31:00.000Z","key":1486521060000,"doc_count":7},{"key_as_string":"2017-02-08T02:32:00.000Z","key":1486521120000,"doc_count":6},{"key_as_string":"2017-02-08T02:33:00.000Z","key":1486521180000,"doc_count":15},{"key_as_string":"2017-02-08T02:34:00.000Z","key":1486521240000,"doc_count":11},{"key_as_string":"2017-02-08T02:35:00.000Z","key":1486521300000,"doc_count":13},{"key_as_string":"2017-02-08T02:36:00.000Z","key":1486521360000,"doc_count":12},{"key_as_string":"2017-02-08T02:37:00.000Z","key":1486521420000,"doc_count":9},{"key_as_string":"2017-02-08T02:38:00.000Z","key":1486521480000,"doc_count":14},{"key_as_string":"2017-02-08T02:39:00.000Z","key":1486521540000,"doc_count":9},{"key_as_string":"2017-02-08T02:40:00.000Z","key":1486521600000,"doc_count":12},{"key_as_string":"2017-02-08T02:41:00.000Z","key":1486521660000,"doc_count":10},{"key_as_string":"2017-02-08T02:42:00.000Z","key":1486521720000,"doc_count":12},{"key_as_string":"2017-02-08T02:43:00.000Z","key":1486521780000,"doc_count":8},{"key_as_string":"2017-02-08T02:44:00.000Z","key":1486521840000,"doc_count":15},{"key_as_string":"2017-02-08T02:45:00.000Z","key":1486521900000,"doc_count":6},{"key_as_string":"2017-02-08T02:46:00.000Z","key":1486521960000,"doc_count":12},{"key_as_string":"2017-02-08T02:47:00.000Z","key":1486522020000,"doc_count":10},{"key_as_string":"2017-02-08T02:48:00.000Z","key":1486522080000,"doc_count":13},{"key_as_string":"2017-02-08T02:49:00.000Z","key":1486522140000,"doc_count":14},{"key_as_string":"2017-02-08T02:50:00.000Z","key":1486522200000,"doc_count":8},{"key_as_string":"2017-02-08T02:51:00.000Z","key":1486522260000,"doc_count":12},{"key_as_string":"2017-02-08T02:52:00.000Z","key":1486522320000,"doc_count":6},{"key_as_string":"2017-02-08T02:53:00.000Z","key":1486522380000,"doc_count":14},{"key_as_string":"2017-02-08T02:54:00.000Z","key":1486522440000,"doc_count":12},{"key_as_string":"2017-02-08T02:55:00.000Z","key":1486522500000,"doc_count":11},{"key_as_string":"2017-02-08T02:56:00.000Z","key":1486522560000,"doc_count":14},{"key_as_string":"2017-02-08T02:57:00.000Z","key":1486522620000,"doc_count":13},{"key_as_string":"2017-02-08T02:58:00.000Z","key":1486522680000,"doc_count":7},{"key_as_string":"2017-02-08T02:59:00.000Z","key":1486522740000,"doc_count":9},{"key_as_string":"2017-02-08T03:00:00.000Z","key":1486522800000,"doc_count":9},{"key_as_string":"2017-02-08T03:01:00.000Z","key":1486522860000,"doc_count":13},{"key_as_string":"2017-02-08T03:02:00.000Z","key":1486522920000,"doc_count":10},{"key_as_string":"2017-02-08T03:03:00.000Z","key":1486522980000,"doc_count":12},{"key_as_string":"2017-02-08T03:04:00.000Z","key":1486523040000,"doc_count":4},{"key_as_string":"2017-02-08T03:05:00.000Z","key":1486523100000,"doc_count":12},{"key_as_string":"2017-02-08T03:06:00.000Z","key":1486523160000,"doc_count":10},{"key_as_string":"2017-02-08T03:07:00.000Z","key":1486523220000,"doc_count":8},{"key_as_string":"2017-02-08T03:08:00.000Z","key":1486523280000,"doc_count":12},{"key_as_string":"2017-02-08T03:09:00.000Z","key":1486523340000,"doc_count":12},{"key_as_string":"2017-02-08T03:10:00.000Z","key":1486523400000,"doc_count":9},{"key_as_string":"2017-02-08T03:11:00.000Z","key":1486523460000,"doc_count":12},{"key_as_string":"2017-02-08T03:12:00.000Z","key":1486523520000,"doc_count":6},{"key_as_string":"2017-02-08T03:13:00.000Z","key":1486523580000,"doc_count":11},{"key_as_string":"2017-02-08T03:14:00.000Z","key":1486523640000,"doc_count":8},{"key_as_string":"2017-02-08T03:15:00.000Z","key":1486523700000,"doc_count":11},{"key_as_string":"2017-02-08T03:16:00.000Z","key":1486523760000,"doc_count":9},{"key_as_string":"2017-02-08T03:17:00.000Z","key":1486523820000,"doc_count":9},{"key_as_string":"2017-02-08T03:18:00.000Z","key":1486523880000,"doc_count":16},{"key_as_string":"2017-02-08T03:19:00.000Z","key":1486523940000,"doc_count":6},{"key_as_string":"2017-02-08T03:20:00.000Z","key":1486524000000,"doc_count":10},{"key_as_string":"2017-02-08T03:21:00.000Z","key":1486524060000,"doc_count":14},{"key_as_string":"2017-02-08T03:22:00.000Z","key":1486524120000,"doc_count":3},{"key_as_string":"2017-02-08T03:23:00.000Z","key":1486524180000,"doc_count":11},{"key_as_string":"2017-02-08T03:24:00.000Z","key":1486524240000,"doc_count":13},{"key_as_string":"2017-02-08T03:25:00.000Z","key":1486524300000,"doc_count":11},{"key_as_string":"2017-02-08T03:26:00.000Z","key":1486524360000,"doc_count":18},{"key_as_string":"2017-02-08T03:27:00.000Z","key":1486524420000,"doc_count":12},{"key_as_string":"2017-02-08T03:28:00.000Z","key":1486524480000,"doc_count":11},{"key_as_string":"2017-02-08T03:29:00.000Z","key":1486524540000,"doc_count":8},{"key_as_string":"2017-02-08T03:30:00.000Z","key":1486524600000,"doc_count":18},{"key_as_string":"2017-02-08T03:31:00.000Z","key":1486524660000,"doc_count":12},{"key_as_string":"2017-02-08T03:32:00.000Z","key":1486524720000,"doc_count":13},{"key_as_string":"2017-02-08T03:33:00.000Z","key":1486524780000,"doc_count":12},{"key_as_string":"2017-02-08T03:34:00.000Z","key":1486524840000,"doc_count":10},{"key_as_string":"2017-02-08T03:35:00.000Z","key":1486524900000,"doc_count":10},{"key_as_string":"2017-02-08T03:36:00.000Z","key":1486524960000,"doc_count":10},{"key_as_string":"2017-02-08T03:37:00.000Z","key":1486525020000,"doc_count":9},{"key_as_string":"2017-02-08T03:38:00.000Z","key":1486525080000,"doc_count":20},{"key_as_string":"2017-02-08T03:39:00.000Z","key":1486525140000,"doc_count":8},{"key_as_string":"2017-02-08T03:40:00.000Z","key":1486525200000,"doc_count":7},{"key_as_string":"2017-02-08T03:41:00.000Z","key":1486525260000,"doc_count":10},{"key_as_string":"2017-02-08T03:42:00.000Z","key":1486525320000,"doc_count":12},{"key_as_string":"2017-02-08T03:43:00.000Z","key":1486525380000,"doc_count":9},{"key_as_string":"2017-02-08T03:44:00.000Z","key":1486525440000,"doc_count":7},{"key_as_string":"2017-02-08T03:45:00.000Z","key":1486525500000,"doc_count":8},{"key_as_string":"2017-02-08T03:46:00.000Z","key":1486525560000,"doc_count":10},{"key_as_string":"2017-02-08T03:47:00.000Z","key":1486525620000,"doc_count":10},{"key_as_string":"2017-02-08T03:48:00.000Z","key":1486525680000,"doc_count":7},{"key_as_string":"2017-02-08T03:49:00.000Z","key":1486525740000,"doc_count":9},{"key_as_string":"2017-02-08T03:50:00.000Z","key":1486525800000,"doc_count":11},{"key_as_string":"2017-02-08T03:51:00.000Z","key":1486525860000,"doc_count":8},{"key_as_string":"2017-02-08T03:52:00.000Z","key":1486525920000,"doc_count":10},{"key_as_string":"2017-02-08T03:53:00.000Z","key":1486525980000,"doc_count":12},{"key_as_string":"2017-02-08T03:54:00.000Z","key":1486526040000,"doc_count":14},{"key_as_string":"2017-02-08T03:55:00.000Z","key":1486526100000,"doc_count":9},{"key_as_string":"2017-02-08T03:56:00.000Z","key":1486526160000,"doc_count":11},{"key_as_string":"2017-02-08T03:57:00.000Z","key":1486526220000,"doc_count":7},{"key_as_string":"2017-02-08T03:58:00.000Z","key":1486526280000,"doc_count":11},{"key_as_string":"2017-02-08T03:59:00.000Z","key":1486526340000,"doc_count":14},{"key_as_string":"2017-02-08T04:00:00.000Z","key":1486526400000,"doc_count":10},{"key_as_string":"2017-02-08T04:01:00.000Z","key":1486526460000,"doc_count":11},{"key_as_string":"2017-02-08T04:02:00.000Z","key":1486526520000,"doc_count":12},{"key_as_string":"2017-02-08T04:03:00.000Z","key":1486526580000,"doc_count":8},{"key_as_string":"2017-02-08T04:04:00.000Z","key":1486526640000,"doc_count":11},{"key_as_string":"2017-02-08T04:05:00.000Z","key":1486526700000,"doc_count":15},{"key_as_string":"2017-02-08T04:06:00.000Z","key":1486526760000,"doc_count":8},{"key_as_string":"2017-02-08T04:07:00.000Z","key":1486526820000,"doc_count":13},{"key_as_string":"2017-02-08T04:08:00.000Z","key":1486526880000,"doc_count":12},{"key_as_string":"2017-02-08T04:09:00.000Z","key":1486526940000,"doc_count":11},{"key_as_string":"2017-02-08T04:10:00.000Z","key":1486527000000,"doc_count":12},{"key_as_string":"2017-02-08T04:11:00.000Z","key":1486527060000,"doc_count":10},{"key_as_string":"2017-02-08T04:12:00.000Z","key":1486527120000,"doc_count":13},{"key_as_string":"2017-02-08T04:13:00.000Z","key":1486527180000,"doc_count":5},{"key_as_string":"2017-02-08T04:14:00.000Z","key":1486527240000,"doc_count":6},{"key_as_string":"2017-02-08T04:15:00.000Z","key":1486527300000,"doc_count":16},{"key_as_string":"2017-02-08T04:16:00.000Z","key":1486527360000,"doc_count":12},{"key_as_string":"2017-02-08T04:17:00.000Z","key":1486527420000,"doc_count":17},{"key_as_string":"2017-02-08T04:18:00.000Z","key":1486527480000,"doc_count":11},{"key_as_string":"2017-02-08T04:19:00.000Z","key":1486527540000,"doc_count":16},{"key_as_string":"2017-02-08T04:20:00.000Z","key":1486527600000,"doc_count":8},{"key_as_string":"2017-02-08T04:21:00.000Z","key":1486527660000,"doc_count":10},{"key_as_string":"2017-02-08T04:22:00.000Z","key":1486527720000,"doc_count":14},{"key_as_string":"2017-02-08T04:23:00.000Z","key":1486527780000,"doc_count":10},{"key_as_string":"2017-02-08T04:24:00.000Z","key":1486527840000,"doc_count":9},{"key_as_string":"2017-02-08T04:25:00.000Z","key":1486527900000,"doc_count":13},{"key_as_string":"2017-02-08T04:26:00.000Z","key":1486527960000,"doc_count":8},{"key_as_string":"2017-02-08T04:27:00.000Z","key":1486528020000,"doc_count":13},{"key_as_string":"2017-02-08T04:28:00.000Z","key":1486528080000,"doc_count":16},{"key_as_string":"2017-02-08T04:29:00.000Z","key":1486528140000,"doc_count":8},{"key_as_string":"2017-02-08T04:30:00.000Z","key":1486528200000,"doc_count":13},{"key_as_string":"2017-02-08T04:31:00.000Z","key":1486528260000,"doc_count":12},{"key_as_string":"2017-02-08T04:32:00.000Z","key":1486528320000,"doc_count":9},{"key_as_string":"2017-02-08T04:33:00.000Z","key":1486528380000,"doc_count":14},{"key_as_string":"2017-02-08T04:34:00.000Z","key":1486528440000,"doc_count":8},{"key_as_string":"2017-02-08T04:35:00.000Z","key":1486528500000,"doc_count":9},{"key_as_string":"2017-02-08T04:36:00.000Z","key":1486528560000,"doc_count":19},{"key_as_string":"2017-02-08T04:37:00.000Z","key":1486528620000,"doc_count":8},{"key_as_string":"2017-02-08T04:38:00.000Z","key":1486528680000,"doc_count":12},{"key_as_string":"2017-02-08T04:39:00.000Z","key":1486528740000,"doc_count":13},{"key_as_string":"2017-02-08T04:40:00.000Z","key":1486528800000,"doc_count":13},{"key_as_string":"2017-02-08T04:41:00.000Z","key":1486528860000,"doc_count":9},{"key_as_string":"2017-02-08T04:42:00.000Z","key":1486528920000,"doc_count":8},{"key_as_string":"2017-02-08T04:43:00.000Z","key":1486528980000,"doc_count":8},{"key_as_string":"2017-02-08T04:44:00.000Z","key":1486529040000,"doc_count":11},{"key_as_string":"2017-02-08T04:45:00.000Z","key":1486529100000,"doc_count":11},{"key_as_string":"2017-02-08T04:46:00.000Z","key":1486529160000,"doc_count":14},{"key_as_string":"2017-02-08T04:47:00.000Z","key":1486529220000,"doc_count":9},{"key_as_string":"2017-02-08T04:48:00.000Z","key":1486529280000,"doc_count":11},{"key_as_string":"2017-02-08T04:49:00.000Z","key":1486529340000,"doc_count":16},{"key_as_string":"2017-02-08T04:50:00.000Z","key":1486529400000,"doc_count":6},{"key_as_string":"2017-02-08T04:51:00.000Z","key":1486529460000,"doc_count":14},{"key_as_string":"2017-02-08T04:52:00.000Z","key":1486529520000,"doc_count":12},{"key_as_string":"2017-02-08T04:53:00.000Z","key":1486529580000,"doc_count":13},{"key_as_string":"2017-02-08T04:54:00.000Z","key":1486529640000,"doc_count":12},{"key_as_string":"2017-02-08T04:55:00.000Z","key":1486529700000,"doc_count":12},{"key_as_string":"2017-02-08T04:56:00.000Z","key":1486529760000,"doc_count":15},{"key_as_string":"2017-02-08T04:57:00.000Z","key":1486529820000,"doc_count":12},{"key_as_string":"2017-02-08T04:58:00.000Z","key":1486529880000,"doc_count":11},{"key_as_string":"2017-02-08T04:59:00.000Z","key":1486529940000,"doc_count":7},{"key_as_string":"2017-02-08T05:00:00.000Z","key":1486530000000,"doc_count":10},{"key_as_string":"2017-02-08T05:01:00.000Z","key":1486530060000,"doc_count":15},{"key_as_string":"2017-02-08T05:02:00.000Z","key":1486530120000,"doc_count":15},{"key_as_string":"2017-02-08T05:03:00.000Z","key":1486530180000,"doc_count":11},{"key_as_string":"2017-02-08T05:04:00.000Z","key":1486530240000,"doc_count":11},{"key_as_string":"2017-02-08T05:05:00.000Z","key":1486530300000,"doc_count":14},{"key_as_string":"2017-02-08T05:06:00.000Z","key":1486530360000,"doc_count":9},{"key_as_string":"2017-02-08T05:07:00.000Z","key":1486530420000,"doc_count":15},{"key_as_string":"2017-02-08T05:08:00.000Z","key":1486530480000,"doc_count":8},{"key_as_string":"2017-02-08T05:09:00.000Z","key":1486530540000,"doc_count":13},{"key_as_string":"2017-02-08T05:10:00.000Z","key":1486530600000,"doc_count":12},{"key_as_string":"2017-02-08T05:11:00.000Z","key":1486530660000,"doc_count":7},{"key_as_string":"2017-02-08T05:12:00.000Z","key":1486530720000,"doc_count":9},{"key_as_string":"2017-02-08T05:13:00.000Z","key":1486530780000,"doc_count":12},{"key_as_string":"2017-02-08T05:14:00.000Z","key":1486530840000,"doc_count":14},{"key_as_string":"2017-02-08T05:15:00.000Z","key":1486530900000,"doc_count":15},{"key_as_string":"2017-02-08T05:16:00.000Z","key":1486530960000,"doc_count":7},{"key_as_string":"2017-02-08T05:17:00.000Z","key":1486531020000,"doc_count":10},{"key_as_string":"2017-02-08T05:18:00.000Z","key":1486531080000,"doc_count":10},{"key_as_string":"2017-02-08T05:19:00.000Z","key":1486531140000,"doc_count":12},{"key_as_string":"2017-02-08T05:20:00.000Z","key":1486531200000,"doc_count":7},{"key_as_string":"2017-02-08T05:21:00.000Z","key":1486531260000,"doc_count":9},{"key_as_string":"2017-02-08T05:22:00.000Z","key":1486531320000,"doc_count":13},{"key_as_string":"2017-02-08T05:23:00.000Z","key":1486531380000,"doc_count":9},{"key_as_string":"2017-02-08T05:24:00.000Z","key":1486531440000,"doc_count":10},{"key_as_string":"2017-02-08T05:25:00.000Z","key":1486531500000,"doc_count":12},{"key_as_string":"2017-02-08T05:26:00.000Z","key":1486531560000,"doc_count":7},{"key_as_string":"2017-02-08T05:27:00.000Z","key":1486531620000,"doc_count":16},{"key_as_string":"2017-02-08T05:28:00.000Z","key":1486531680000,"doc_count":12},{"key_as_string":"2017-02-08T05:29:00.000Z","key":1486531740000,"doc_count":13},{"key_as_string":"2017-02-08T05:30:00.000Z","key":1486531800000,"doc_count":10},{"key_as_string":"2017-02-08T05:31:00.000Z","key":1486531860000,"doc_count":12},{"key_as_string":"2017-02-08T05:32:00.000Z","key":1486531920000,"doc_count":6},{"key_as_string":"2017-02-08T05:33:00.000Z","key":1486531980000,"doc_count":16},{"key_as_string":"2017-02-08T05:34:00.000Z","key":1486532040000,"doc_count":11},{"key_as_string":"2017-02-08T05:35:00.000Z","key":1486532100000,"doc_count":13},{"key_as_string":"2017-02-08T05:36:00.000Z","key":1486532160000,"doc_count":12},{"key_as_string":"2017-02-08T05:37:00.000Z","key":1486532220000,"doc_count":14},{"key_as_string":"2017-02-08T05:38:00.000Z","key":1486532280000,"doc_count":9},{"key_as_string":"2017-02-08T05:39:00.000Z","key":1486532340000,"doc_count":9},{"key_as_string":"2017-02-08T05:40:00.000Z","key":1486532400000,"doc_count":11},{"key_as_string":"2017-02-08T05:41:00.000Z","key":1486532460000,"doc_count":17},{"key_as_string":"2017-02-08T05:42:00.000Z","key":1486532520000,"doc_count":13},{"key_as_string":"2017-02-08T05:43:00.000Z","key":1486532580000,"doc_count":10},{"key_as_string":"2017-02-08T05:44:00.000Z","key":1486532640000,"doc_count":12},{"key_as_string":"2017-02-08T05:45:00.000Z","key":1486532700000,"doc_count":17},{"key_as_string":"2017-02-08T05:46:00.000Z","key":1486532760000,"doc_count":7},{"key_as_string":"2017-02-08T05:47:00.000Z","key":1486532820000,"doc_count":11},{"key_as_string":"2017-02-08T05:48:00.000Z","key":1486532880000,"doc_count":10},{"key_as_string":"2017-02-08T05:49:00.000Z","key":1486532940000,"doc_count":7},{"key_as_string":"2017-02-08T05:50:00.000Z","key":1486533000000,"doc_count":12},{"key_as_string":"2017-02-08T05:51:00.000Z","key":1486533060000,"doc_count":12},{"key_as_string":"2017-02-08T05:52:00.000Z","key":1486533120000,"doc_count":6},{"key_as_string":"2017-02-08T05:53:00.000Z","key":1486533180000,"doc_count":11},{"key_as_string":"2017-02-08T05:54:00.000Z","key":1486533240000,"doc_count":14},{"key_as_string":"2017-02-08T05:55:00.000Z","key":1486533300000,"doc_count":10},{"key_as_string":"2017-02-08T05:56:00.000Z","key":1486533360000,"doc_count":11},{"key_as_string":"2017-02-08T05:57:00.000Z","key":1486533420000,"doc_count":14},{"key_as_string":"2017-02-08T05:58:00.000Z","key":1486533480000,"doc_count":5},{"key_as_string":"2017-02-08T05:59:00.000Z","key":1486533540000,"doc_count":16},{"key_as_string":"2017-02-08T06:00:00.000Z","key":1486533600000,"doc_count":9},{"key_as_string":"2017-02-08T06:01:00.000Z","key":1486533660000,"doc_count":12},{"key_as_string":"2017-02-08T06:02:00.000Z","key":1486533720000,"doc_count":10},{"key_as_string":"2017-02-08T06:03:00.000Z","key":1486533780000,"doc_count":13},{"key_as_string":"2017-02-08T06:04:00.000Z","key":1486533840000,"doc_count":14},{"key_as_string":"2017-02-08T06:05:00.000Z","key":1486533900000,"doc_count":11},{"key_as_string":"2017-02-08T06:06:00.000Z","key":1486533960000,"doc_count":9},{"key_as_string":"2017-02-08T06:07:00.000Z","key":1486534020000,"doc_count":10},{"key_as_string":"2017-02-08T06:08:00.000Z","key":1486534080000,"doc_count":7},{"key_as_string":"2017-02-08T06:09:00.000Z","key":1486534140000,"doc_count":14},{"key_as_string":"2017-02-08T06:10:00.000Z","key":1486534200000,"doc_count":13},{"key_as_string":"2017-02-08T06:11:00.000Z","key":1486534260000,"doc_count":15},{"key_as_string":"2017-02-08T06:12:00.000Z","key":1486534320000,"doc_count":9},{"key_as_string":"2017-02-08T06:13:00.000Z","key":1486534380000,"doc_count":8},{"key_as_string":"2017-02-08T06:14:00.000Z","key":1486534440000,"doc_count":7},{"key_as_string":"2017-02-08T06:15:00.000Z","key":1486534500000,"doc_count":15},{"key_as_string":"2017-02-08T06:16:00.000Z","key":1486534560000,"doc_count":11},{"key_as_string":"2017-02-08T06:17:00.000Z","key":1486534620000,"doc_count":15},{"key_as_string":"2017-02-08T06:18:00.000Z","key":1486534680000,"doc_count":14},{"key_as_string":"2017-02-08T06:19:00.000Z","key":1486534740000,"doc_count":12},{"key_as_string":"2017-02-08T06:20:00.000Z","key":1486534800000,"doc_count":15},{"key_as_string":"2017-02-08T06:21:00.000Z","key":1486534860000,"doc_count":13},{"key_as_string":"2017-02-08T06:22:00.000Z","key":1486534920000,"doc_count":10},{"key_as_string":"2017-02-08T06:23:00.000Z","key":1486534980000,"doc_count":14},{"key_as_string":"2017-02-08T06:24:00.000Z","key":1486535040000,"doc_count":15},{"key_as_string":"2017-02-08T06:25:00.000Z","key":1486535100000,"doc_count":14},{"key_as_string":"2017-02-08T06:26:00.000Z","key":1486535160000,"doc_count":11},{"key_as_string":"2017-02-08T06:27:00.000Z","key":1486535220000,"doc_count":13},{"key_as_string":"2017-02-08T06:28:00.000Z","key":1486535280000,"doc_count":11},{"key_as_string":"2017-02-08T06:29:00.000Z","key":1486535340000,"doc_count":16},{"key_as_string":"2017-02-08T06:30:00.000Z","key":1486535400000,"doc_count":12},{"key_as_string":"2017-02-08T06:31:00.000Z","key":1486535460000,"doc_count":11},{"key_as_string":"2017-02-08T06:32:00.000Z","key":1486535520000,"doc_count":10},{"key_as_string":"2017-02-08T06:33:00.000Z","key":1486535580000,"doc_count":8},{"key_as_string":"2017-02-08T06:34:00.000Z","key":1486535640000,"doc_count":14},{"key_as_string":"2017-02-08T06:35:00.000Z","key":1486535700000,"doc_count":9},{"key_as_string":"2017-02-08T06:36:00.000Z","key":1486535760000,"doc_count":16},{"key_as_string":"2017-02-08T06:37:00.000Z","key":1486535820000,"doc_count":13},{"key_as_string":"2017-02-08T06:38:00.000Z","key":1486535880000,"doc_count":10},{"key_as_string":"2017-02-08T06:39:00.000Z","key":1486535940000,"doc_count":13},{"key_as_string":"2017-02-08T06:40:00.000Z","key":1486536000000,"doc_count":13},{"key_as_string":"2017-02-08T06:41:00.000Z","key":1486536060000,"doc_count":10},{"key_as_string":"2017-02-08T06:42:00.000Z","key":1486536120000,"doc_count":12},{"key_as_string":"2017-02-08T06:43:00.000Z","key":1486536180000,"doc_count":10},{"key_as_string":"2017-02-08T06:44:00.000Z","key":1486536240000,"doc_count":15},{"key_as_string":"2017-02-08T06:45:00.000Z","key":1486536300000,"doc_count":10},{"key_as_string":"2017-02-08T06:46:00.000Z","key":1486536360000,"doc_count":8},{"key_as_string":"2017-02-08T06:47:00.000Z","key":1486536420000,"doc_count":15},{"key_as_string":"2017-02-08T06:48:00.000Z","key":1486536480000,"doc_count":14},{"key_as_string":"2017-02-08T06:49:00.000Z","key":1486536540000,"doc_count":8},{"key_as_string":"2017-02-08T06:50:00.000Z","key":1486536600000,"doc_count":10},{"key_as_string":"2017-02-08T06:51:00.000Z","key":1486536660000,"doc_count":12},{"key_as_string":"2017-02-08T06:52:00.000Z","key":1486536720000,"doc_count":19},{"key_as_string":"2017-02-08T06:53:00.000Z","key":1486536780000,"doc_count":10},{"key_as_string":"2017-02-08T06:54:00.000Z","key":1486536840000,"doc_count":12},{"key_as_string":"2017-02-08T06:55:00.000Z","key":1486536900000,"doc_count":13},{"key_as_string":"2017-02-08T06:56:00.000Z","key":1486536960000,"doc_count":12},{"key_as_string":"2017-02-08T06:57:00.000Z","key":1486537020000,"doc_count":10},{"key_as_string":"2017-02-08T06:58:00.000Z","key":1486537080000,"doc_count":13},{"key_as_string":"2017-02-08T06:59:00.000Z","key":1486537140000,"doc_count":9},{"key_as_string":"2017-02-08T07:00:00.000Z","key":1486537200000,"doc_count":17},{"key_as_string":"2017-02-08T07:01:00.000Z","key":1486537260000,"doc_count":14},{"key_as_string":"2017-02-08T07:02:00.000Z","key":1486537320000,"doc_count":11},{"key_as_string":"2017-02-08T07:03:00.000Z","key":1486537380000,"doc_count":13},{"key_as_string":"2017-02-08T07:04:00.000Z","key":1486537440000,"doc_count":15},{"key_as_string":"2017-02-08T07:05:00.000Z","key":1486537500000,"doc_count":16},{"key_as_string":"2017-02-08T07:06:00.000Z","key":1486537560000,"doc_count":11},{"key_as_string":"2017-02-08T07:07:00.000Z","key":1486537620000,"doc_count":7},{"key_as_string":"2017-02-08T07:08:00.000Z","key":1486537680000,"doc_count":12},{"key_as_string":"2017-02-08T07:09:00.000Z","key":1486537740000,"doc_count":14},{"key_as_string":"2017-02-08T07:10:00.000Z","key":1486537800000,"doc_count":9},{"key_as_string":"2017-02-08T07:11:00.000Z","key":1486537860000,"doc_count":11},{"key_as_string":"2017-02-08T07:12:00.000Z","key":1486537920000,"doc_count":12},{"key_as_string":"2017-02-08T07:13:00.000Z","key":1486537980000,"doc_count":7},{"key_as_string":"2017-02-08T07:14:00.000Z","key":1486538040000,"doc_count":15},{"key_as_string":"2017-02-08T07:15:00.000Z","key":1486538100000,"doc_count":13},{"key_as_string":"2017-02-08T07:16:00.000Z","key":1486538160000,"doc_count":10},{"key_as_string":"2017-02-08T07:17:00.000Z","key":1486538220000,"doc_count":8},{"key_as_string":"2017-02-08T07:18:00.000Z","key":1486538280000,"doc_count":16},{"key_as_string":"2017-02-08T07:19:00.000Z","key":1486538340000,"doc_count":12},{"key_as_string":"2017-02-08T07:20:00.000Z","key":1486538400000,"doc_count":13},{"key_as_string":"2017-02-08T07:21:00.000Z","key":1486538460000,"doc_count":15},{"key_as_string":"2017-02-08T07:22:00.000Z","key":1486538520000,"doc_count":12},{"key_as_string":"2017-02-08T07:23:00.000Z","key":1486538580000,"doc_count":17},{"key_as_string":"2017-02-08T07:24:00.000Z","key":1486538640000,"doc_count":10},{"key_as_string":"2017-02-08T07:25:00.000Z","key":1486538700000,"doc_count":11},{"key_as_string":"2017-02-08T07:26:00.000Z","key":1486538760000,"doc_count":11},{"key_as_string":"2017-02-08T07:27:00.000Z","key":1486538820000,"doc_count":13},{"key_as_string":"2017-02-08T07:28:00.000Z","key":1486538880000,"doc_count":10},{"key_as_string":"2017-02-08T07:29:00.000Z","key":1486538940000,"doc_count":13},{"key_as_string":"2017-02-08T07:30:00.000Z","key":1486539000000,"doc_count":11},{"key_as_string":"2017-02-08T07:31:00.000Z","key":1486539060000,"doc_count":17},{"key_as_string":"2017-02-08T07:32:00.000Z","key":1486539120000,"doc_count":12},{"key_as_string":"2017-02-08T07:33:00.000Z","key":1486539180000,"doc_count":11},{"key_as_string":"2017-02-08T07:34:00.000Z","key":1486539240000,"doc_count":11},{"key_as_string":"2017-02-08T07:35:00.000Z","key":1486539300000,"doc_count":17},{"key_as_string":"2017-02-08T07:36:00.000Z","key":1486539360000,"doc_count":12},{"key_as_string":"2017-02-08T07:37:00.000Z","key":1486539420000,"doc_count":15},{"key_as_string":"2017-02-08T07:38:00.000Z","key":1486539480000,"doc_count":12},{"key_as_string":"2017-02-08T07:39:00.000Z","key":1486539540000,"doc_count":19},{"key_as_string":"2017-02-08T07:40:00.000Z","key":1486539600000,"doc_count":10},{"key_as_string":"2017-02-08T07:41:00.000Z","key":1486539660000,"doc_count":10},{"key_as_string":"2017-02-08T07:42:00.000Z","key":1486539720000,"doc_count":13},{"key_as_string":"2017-02-08T07:43:00.000Z","key":1486539780000,"doc_count":17},{"key_as_string":"2017-02-08T07:44:00.000Z","key":1486539840000,"doc_count":15},{"key_as_string":"2017-02-08T07:45:00.000Z","key":1486539900000,"doc_count":8},{"key_as_string":"2017-02-08T07:46:00.000Z","key":1486539960000,"doc_count":13},{"key_as_string":"2017-02-08T07:47:00.000Z","key":1486540020000,"doc_count":9},{"key_as_string":"2017-02-08T07:48:00.000Z","key":1486540080000,"doc_count":10},{"key_as_string":"2017-02-08T07:49:00.000Z","key":1486540140000,"doc_count":10},{"key_as_string":"2017-02-08T07:50:00.000Z","key":1486540200000,"doc_count":15},{"key_as_string":"2017-02-08T07:51:00.000Z","key":1486540260000,"doc_count":13},{"key_as_string":"2017-02-08T07:52:00.000Z","key":1486540320000,"doc_count":14},{"key_as_string":"2017-02-08T07:53:00.000Z","key":1486540380000,"doc_count":13},{"key_as_string":"2017-02-08T07:54:00.000Z","key":1486540440000,"doc_count":14},{"key_as_string":"2017-02-08T07:55:00.000Z","key":1486540500000,"doc_count":11},{"key_as_string":"2017-02-08T07:56:00.000Z","key":1486540560000,"doc_count":10},{"key_as_string":"2017-02-08T07:57:00.000Z","key":1486540620000,"doc_count":9},{"key_as_string":"2017-02-08T07:58:00.000Z","key":1486540680000,"doc_count":16},{"key_as_string":"2017-02-08T07:59:00.000Z","key":1486540740000,"doc_count":17},{"key_as_string":"2017-02-08T08:00:00.000Z","key":1486540800000,"doc_count":14},{"key_as_string":"2017-02-08T08:01:00.000Z","key":1486540860000,"doc_count":12},{"key_as_string":"2017-02-08T08:02:00.000Z","key":1486540920000,"doc_count":15},{"key_as_string":"2017-02-08T08:03:00.000Z","key":1486540980000,"doc_count":15},{"key_as_string":"2017-02-08T08:04:00.000Z","key":1486541040000,"doc_count":10},{"key_as_string":"2017-02-08T08:05:00.000Z","key":1486541100000,"doc_count":12},{"key_as_string":"2017-02-08T08:06:00.000Z","key":1486541160000,"doc_count":6},{"key_as_string":"2017-02-08T08:07:00.000Z","key":1486541220000,"doc_count":17},{"key_as_string":"2017-02-08T08:08:00.000Z","key":1486541280000,"doc_count":15},{"key_as_string":"2017-02-08T08:09:00.000Z","key":1486541340000,"doc_count":14},{"key_as_string":"2017-02-08T08:10:00.000Z","key":1486541400000,"doc_count":12},{"key_as_string":"2017-02-08T08:11:00.000Z","key":1486541460000,"doc_count":18},{"key_as_string":"2017-02-08T08:12:00.000Z","key":1486541520000,"doc_count":9},{"key_as_string":"2017-02-08T08:13:00.000Z","key":1486541580000,"doc_count":12},{"key_as_string":"2017-02-08T08:14:00.000Z","key":1486541640000,"doc_count":11},{"key_as_string":"2017-02-08T08:15:00.000Z","key":1486541700000,"doc_count":19},{"key_as_string":"2017-02-08T08:16:00.000Z","key":1486541760000,"doc_count":12},{"key_as_string":"2017-02-08T08:17:00.000Z","key":1486541820000,"doc_count":10},{"key_as_string":"2017-02-08T08:18:00.000Z","key":1486541880000,"doc_count":16},{"key_as_string":"2017-02-08T08:19:00.000Z","key":1486541940000,"doc_count":12},{"key_as_string":"2017-02-08T08:20:00.000Z","key":1486542000000,"doc_count":15},{"key_as_string":"2017-02-08T08:21:00.000Z","key":1486542060000,"doc_count":14},{"key_as_string":"2017-02-08T08:22:00.000Z","key":1486542120000,"doc_count":15},{"key_as_string":"2017-02-08T08:23:00.000Z","key":1486542180000,"doc_count":12},{"key_as_string":"2017-02-08T08:24:00.000Z","key":1486542240000,"doc_count":15},{"key_as_string":"2017-02-08T08:25:00.000Z","key":1486542300000,"doc_count":7},{"key_as_string":"2017-02-08T08:26:00.000Z","key":1486542360000,"doc_count":10},{"key_as_string":"2017-02-08T08:27:00.000Z","key":1486542420000,"doc_count":12},{"key_as_string":"2017-02-08T08:28:00.000Z","key":1486542480000,"doc_count":10},{"key_as_string":"2017-02-08T08:29:00.000Z","key":1486542540000,"doc_count":12},{"key_as_string":"2017-02-08T08:30:00.000Z","key":1486542600000,"doc_count":17},{"key_as_string":"2017-02-08T08:31:00.000Z","key":1486542660000,"doc_count":13},{"key_as_string":"2017-02-08T08:32:00.000Z","key":1486542720000,"doc_count":14},{"key_as_string":"2017-02-08T08:33:00.000Z","key":1486542780000,"doc_count":10},{"key_as_string":"2017-02-08T08:34:00.000Z","key":1486542840000,"doc_count":12},{"key_as_string":"2017-02-08T08:35:00.000Z","key":1486542900000,"doc_count":14},{"key_as_string":"2017-02-08T08:36:00.000Z","key":1486542960000,"doc_count":11},{"key_as_string":"2017-02-08T08:37:00.000Z","key":1486543020000,"doc_count":16},{"key_as_string":"2017-02-08T08:38:00.000Z","key":1486543080000,"doc_count":13},{"key_as_string":"2017-02-08T08:39:00.000Z","key":1486543140000,"doc_count":11},{"key_as_string":"2017-02-08T08:40:00.000Z","key":1486543200000,"doc_count":9},{"key_as_string":"2017-02-08T08:41:00.000Z","key":1486543260000,"doc_count":15},{"key_as_string":"2017-02-08T08:42:00.000Z","key":1486543320000,"doc_count":12},{"key_as_string":"2017-02-08T08:43:00.000Z","key":1486543380000,"doc_count":9},{"key_as_string":"2017-02-08T08:44:00.000Z","key":1486543440000,"doc_count":15},{"key_as_string":"2017-02-08T08:45:00.000Z","key":1486543500000,"doc_count":17},{"key_as_string":"2017-02-08T08:46:00.000Z","key":1486543560000,"doc_count":5},{"key_as_string":"2017-02-08T08:47:00.000Z","key":1486543620000,"doc_count":19},{"key_as_string":"2017-02-08T08:48:00.000Z","key":1486543680000,"doc_count":14},{"key_as_string":"2017-02-08T08:49:00.000Z","key":1486543740000,"doc_count":13},{"key_as_string":"2017-02-08T08:50:00.000Z","key":1486543800000,"doc_count":16},{"key_as_string":"2017-02-08T08:51:00.000Z","key":1486543860000,"doc_count":14},{"key_as_string":"2017-02-08T08:52:00.000Z","key":1486543920000,"doc_count":17},{"key_as_string":"2017-02-08T08:53:00.000Z","key":1486543980000,"doc_count":12},{"key_as_string":"2017-02-08T08:54:00.000Z","key":1486544040000,"doc_count":13},{"key_as_string":"2017-02-08T08:55:00.000Z","key":1486544100000,"doc_count":17},{"key_as_string":"2017-02-08T08:56:00.000Z","key":1486544160000,"doc_count":11},{"key_as_string":"2017-02-08T08:57:00.000Z","key":1486544220000,"doc_count":10},{"key_as_string":"2017-02-08T08:58:00.000Z","key":1486544280000,"doc_count":13},{"key_as_string":"2017-02-08T08:59:00.000Z","key":1486544340000,"doc_count":11},{"key_as_string":"2017-02-08T09:00:00.000Z","key":1486544400000,"doc_count":12},{"key_as_string":"2017-02-08T09:01:00.000Z","key":1486544460000,"doc_count":15},{"key_as_string":"2017-02-08T09:02:00.000Z","key":1486544520000,"doc_count":8},{"key_as_string":"2017-02-08T09:03:00.000Z","key":1486544580000,"doc_count":14},{"key_as_string":"2017-02-08T09:04:00.000Z","key":1486544640000,"doc_count":13},{"key_as_string":"2017-02-08T09:05:00.000Z","key":1486544700000,"doc_count":12},{"key_as_string":"2017-02-08T09:06:00.000Z","key":1486544760000,"doc_count":15},{"key_as_string":"2017-02-08T09:07:00.000Z","key":1486544820000,"doc_count":13},{"key_as_string":"2017-02-08T09:08:00.000Z","key":1486544880000,"doc_count":19},{"key_as_string":"2017-02-08T09:09:00.000Z","key":1486544940000,"doc_count":14},{"key_as_string":"2017-02-08T09:10:00.000Z","key":1486545000000,"doc_count":11},{"key_as_string":"2017-02-08T09:11:00.000Z","key":1486545060000,"doc_count":15},{"key_as_string":"2017-02-08T09:12:00.000Z","key":1486545120000,"doc_count":15},{"key_as_string":"2017-02-08T09:13:00.000Z","key":1486545180000,"doc_count":10},{"key_as_string":"2017-02-08T09:14:00.000Z","key":1486545240000,"doc_count":17},{"key_as_string":"2017-02-08T09:15:00.000Z","key":1486545300000,"doc_count":18},{"key_as_string":"2017-02-08T09:16:00.000Z","key":1486545360000,"doc_count":15},{"key_as_string":"2017-02-08T09:17:00.000Z","key":1486545420000,"doc_count":13},{"key_as_string":"2017-02-08T09:18:00.000Z","key":1486545480000,"doc_count":9},{"key_as_string":"2017-02-08T09:19:00.000Z","key":1486545540000,"doc_count":15},{"key_as_string":"2017-02-08T09:20:00.000Z","key":1486545600000,"doc_count":10},{"key_as_string":"2017-02-08T09:21:00.000Z","key":1486545660000,"doc_count":9},{"key_as_string":"2017-02-08T09:22:00.000Z","key":1486545720000,"doc_count":13},{"key_as_string":"2017-02-08T09:23:00.000Z","key":1486545780000,"doc_count":18},{"key_as_string":"2017-02-08T09:24:00.000Z","key":1486545840000,"doc_count":12},{"key_as_string":"2017-02-08T09:25:00.000Z","key":1486545900000,"doc_count":15},{"key_as_string":"2017-02-08T09:26:00.000Z","key":1486545960000,"doc_count":10},{"key_as_string":"2017-02-08T09:27:00.000Z","key":1486546020000,"doc_count":16},{"key_as_string":"2017-02-08T09:28:00.000Z","key":1486546080000,"doc_count":17},{"key_as_string":"2017-02-08T09:29:00.000Z","key":1486546140000,"doc_count":14},{"key_as_string":"2017-02-08T09:30:00.000Z","key":1486546200000,"doc_count":13},{"key_as_string":"2017-02-08T09:31:00.000Z","key":1486546260000,"doc_count":14},{"key_as_string":"2017-02-08T09:32:00.000Z","key":1486546320000,"doc_count":11},{"key_as_string":"2017-02-08T09:33:00.000Z","key":1486546380000,"doc_count":13},{"key_as_string":"2017-02-08T09:34:00.000Z","key":1486546440000,"doc_count":10},{"key_as_string":"2017-02-08T09:35:00.000Z","key":1486546500000,"doc_count":15},{"key_as_string":"2017-02-08T09:36:00.000Z","key":1486546560000,"doc_count":15},{"key_as_string":"2017-02-08T09:37:00.000Z","key":1486546620000,"doc_count":11},{"key_as_string":"2017-02-08T09:38:00.000Z","key":1486546680000,"doc_count":13},{"key_as_string":"2017-02-08T09:39:00.000Z","key":1486546740000,"doc_count":12},{"key_as_string":"2017-02-08T09:40:00.000Z","key":1486546800000,"doc_count":12},{"key_as_string":"2017-02-08T09:41:00.000Z","key":1486546860000,"doc_count":11},{"key_as_string":"2017-02-08T09:42:00.000Z","key":1486546920000,"doc_count":10},{"key_as_string":"2017-02-08T09:43:00.000Z","key":1486546980000,"doc_count":15},{"key_as_string":"2017-02-08T09:44:00.000Z","key":1486547040000,"doc_count":12},{"key_as_string":"2017-02-08T09:45:00.000Z","key":1486547100000,"doc_count":11},{"key_as_string":"2017-02-08T09:46:00.000Z","key":1486547160000,"doc_count":12},{"key_as_string":"2017-02-08T09:47:00.000Z","key":1486547220000,"doc_count":12},{"key_as_string":"2017-02-08T09:48:00.000Z","key":1486547280000,"doc_count":10},{"key_as_string":"2017-02-08T09:49:00.000Z","key":1486547340000,"doc_count":18},{"key_as_string":"2017-02-08T09:50:00.000Z","key":1486547400000,"doc_count":16},{"key_as_string":"2017-02-08T09:51:00.000Z","key":1486547460000,"doc_count":20},{"key_as_string":"2017-02-08T09:52:00.000Z","key":1486547520000,"doc_count":12},{"key_as_string":"2017-02-08T09:53:00.000Z","key":1486547580000,"doc_count":13},{"key_as_string":"2017-02-08T09:54:00.000Z","key":1486547640000,"doc_count":16},{"key_as_string":"2017-02-08T09:55:00.000Z","key":1486547700000,"doc_count":14},{"key_as_string":"2017-02-08T09:56:00.000Z","key":1486547760000,"doc_count":12},{"key_as_string":"2017-02-08T09:57:00.000Z","key":1486547820000,"doc_count":18},{"key_as_string":"2017-02-08T09:58:00.000Z","key":1486547880000,"doc_count":11},{"key_as_string":"2017-02-08T09:59:00.000Z","key":1486547940000,"doc_count":17},{"key_as_string":"2017-02-08T10:00:00.000Z","key":1486548000000,"doc_count":12},{"key_as_string":"2017-02-08T10:01:00.000Z","key":1486548060000,"doc_count":13},{"key_as_string":"2017-02-08T10:02:00.000Z","key":1486548120000,"doc_count":21},{"key_as_string":"2017-02-08T10:03:00.000Z","key":1486548180000,"doc_count":17},{"key_as_string":"2017-02-08T10:04:00.000Z","key":1486548240000,"doc_count":16},{"key_as_string":"2017-02-08T10:05:00.000Z","key":1486548300000,"doc_count":11},{"key_as_string":"2017-02-08T10:06:00.000Z","key":1486548360000,"doc_count":16},{"key_as_string":"2017-02-08T10:07:00.000Z","key":1486548420000,"doc_count":11},{"key_as_string":"2017-02-08T10:08:00.000Z","key":1486548480000,"doc_count":15},{"key_as_string":"2017-02-08T10:09:00.000Z","key":1486548540000,"doc_count":12},{"key_as_string":"2017-02-08T10:10:00.000Z","key":1486548600000,"doc_count":14},{"key_as_string":"2017-02-08T10:11:00.000Z","key":1486548660000,"doc_count":11},{"key_as_string":"2017-02-08T10:12:00.000Z","key":1486548720000,"doc_count":19},{"key_as_string":"2017-02-08T10:13:00.000Z","key":1486548780000,"doc_count":13},{"key_as_string":"2017-02-08T10:14:00.000Z","key":1486548840000,"doc_count":16},{"key_as_string":"2017-02-08T10:15:00.000Z","key":1486548900000,"doc_count":10},{"key_as_string":"2017-02-08T10:16:00.000Z","key":1486548960000,"doc_count":13},{"key_as_string":"2017-02-08T10:17:00.000Z","key":1486549020000,"doc_count":8},{"key_as_string":"2017-02-08T10:18:00.000Z","key":1486549080000,"doc_count":15},{"key_as_string":"2017-02-08T10:19:00.000Z","key":1486549140000,"doc_count":14},{"key_as_string":"2017-02-08T10:20:00.000Z","key":1486549200000,"doc_count":14},{"key_as_string":"2017-02-08T10:21:00.000Z","key":1486549260000,"doc_count":10},{"key_as_string":"2017-02-08T10:22:00.000Z","key":1486549320000,"doc_count":15},{"key_as_string":"2017-02-08T10:23:00.000Z","key":1486549380000,"doc_count":10},{"key_as_string":"2017-02-08T10:24:00.000Z","key":1486549440000,"doc_count":13},{"key_as_string":"2017-02-08T10:25:00.000Z","key":1486549500000,"doc_count":14},{"key_as_string":"2017-02-08T10:26:00.000Z","key":1486549560000,"doc_count":14},{"key_as_string":"2017-02-08T10:27:00.000Z","key":1486549620000,"doc_count":17},{"key_as_string":"2017-02-08T10:28:00.000Z","key":1486549680000,"doc_count":10},{"key_as_string":"2017-02-08T10:29:00.000Z","key":1486549740000,"doc_count":13},{"key_as_string":"2017-02-08T10:30:00.000Z","key":1486549800000,"doc_count":16},{"key_as_string":"2017-02-08T10:31:00.000Z","key":1486549860000,"doc_count":14},{"key_as_string":"2017-02-08T10:32:00.000Z","key":1486549920000,"doc_count":16},{"key_as_string":"2017-02-08T10:33:00.000Z","key":1486549980000,"doc_count":15},{"key_as_string":"2017-02-08T10:34:00.000Z","key":1486550040000,"doc_count":15},{"key_as_string":"2017-02-08T10:35:00.000Z","key":1486550100000,"doc_count":15},{"key_as_string":"2017-02-08T10:36:00.000Z","key":1486550160000,"doc_count":10},{"key_as_string":"2017-02-08T10:37:00.000Z","key":1486550220000,"doc_count":15},{"key_as_string":"2017-02-08T10:38:00.000Z","key":1486550280000,"doc_count":14},{"key_as_string":"2017-02-08T10:39:00.000Z","key":1486550340000,"doc_count":17},{"key_as_string":"2017-02-08T10:40:00.000Z","key":1486550400000,"doc_count":15},{"key_as_string":"2017-02-08T10:41:00.000Z","key":1486550460000,"doc_count":19},{"key_as_string":"2017-02-08T10:42:00.000Z","key":1486550520000,"doc_count":9},{"key_as_string":"2017-02-08T10:43:00.000Z","key":1486550580000,"doc_count":14},{"key_as_string":"2017-02-08T10:44:00.000Z","key":1486550640000,"doc_count":10},{"key_as_string":"2017-02-08T10:45:00.000Z","key":1486550700000,"doc_count":20},{"key_as_string":"2017-02-08T10:46:00.000Z","key":1486550760000,"doc_count":10},{"key_as_string":"2017-02-08T10:47:00.000Z","key":1486550820000,"doc_count":16},{"key_as_string":"2017-02-08T10:48:00.000Z","key":1486550880000,"doc_count":13},{"key_as_string":"2017-02-08T10:49:00.000Z","key":1486550940000,"doc_count":14},{"key_as_string":"2017-02-08T10:50:00.000Z","key":1486551000000,"doc_count":10},{"key_as_string":"2017-02-08T10:51:00.000Z","key":1486551060000,"doc_count":17},{"key_as_string":"2017-02-08T10:52:00.000Z","key":1486551120000,"doc_count":16},{"key_as_string":"2017-02-08T10:53:00.000Z","key":1486551180000,"doc_count":9},{"key_as_string":"2017-02-08T10:54:00.000Z","key":1486551240000,"doc_count":15},{"key_as_string":"2017-02-08T10:55:00.000Z","key":1486551300000,"doc_count":16},{"key_as_string":"2017-02-08T10:56:00.000Z","key":1486551360000,"doc_count":5},{"key_as_string":"2017-02-08T10:57:00.000Z","key":1486551420000,"doc_count":15},{"key_as_string":"2017-02-08T10:58:00.000Z","key":1486551480000,"doc_count":9},{"key_as_string":"2017-02-08T10:59:00.000Z","key":1486551540000,"doc_count":14},{"key_as_string":"2017-02-08T11:00:00.000Z","key":1486551600000,"doc_count":16},{"key_as_string":"2017-02-08T11:01:00.000Z","key":1486551660000,"doc_count":9},{"key_as_string":"2017-02-08T11:02:00.000Z","key":1486551720000,"doc_count":17},{"key_as_string":"2017-02-08T11:03:00.000Z","key":1486551780000,"doc_count":9},{"key_as_string":"2017-02-08T11:04:00.000Z","key":1486551840000,"doc_count":18},{"key_as_string":"2017-02-08T11:05:00.000Z","key":1486551900000,"doc_count":12},{"key_as_string":"2017-02-08T11:06:00.000Z","key":1486551960000,"doc_count":16},{"key_as_string":"2017-02-08T11:07:00.000Z","key":1486552020000,"doc_count":13},{"key_as_string":"2017-02-08T11:08:00.000Z","key":1486552080000,"doc_count":14},{"key_as_string":"2017-02-08T11:09:00.000Z","key":1486552140000,"doc_count":13},{"key_as_string":"2017-02-08T11:10:00.000Z","key":1486552200000,"doc_count":17},{"key_as_string":"2017-02-08T11:11:00.000Z","key":1486552260000,"doc_count":12},{"key_as_string":"2017-02-08T11:12:00.000Z","key":1486552320000,"doc_count":11},{"key_as_string":"2017-02-08T11:13:00.000Z","key":1486552380000,"doc_count":15},{"key_as_string":"2017-02-08T11:14:00.000Z","key":1486552440000,"doc_count":10},{"key_as_string":"2017-02-08T11:15:00.000Z","key":1486552500000,"doc_count":9},{"key_as_string":"2017-02-08T11:16:00.000Z","key":1486552560000,"doc_count":14},{"key_as_string":"2017-02-08T11:17:00.000Z","key":1486552620000,"doc_count":16},{"key_as_string":"2017-02-08T11:18:00.000Z","key":1486552680000,"doc_count":19},{"key_as_string":"2017-02-08T11:19:00.000Z","key":1486552740000,"doc_count":14},{"key_as_string":"2017-02-08T11:20:00.000Z","key":1486552800000,"doc_count":10},{"key_as_string":"2017-02-08T11:21:00.000Z","key":1486552860000,"doc_count":14},{"key_as_string":"2017-02-08T11:22:00.000Z","key":1486552920000,"doc_count":13},{"key_as_string":"2017-02-08T11:23:00.000Z","key":1486552980000,"doc_count":16},{"key_as_string":"2017-02-08T11:24:00.000Z","key":1486553040000,"doc_count":15},{"key_as_string":"2017-02-08T11:25:00.000Z","key":1486553100000,"doc_count":16},{"key_as_string":"2017-02-08T11:26:00.000Z","key":1486553160000,"doc_count":18},{"key_as_string":"2017-02-08T11:27:00.000Z","key":1486553220000,"doc_count":13},{"key_as_string":"2017-02-08T11:28:00.000Z","key":1486553280000,"doc_count":13},{"key_as_string":"2017-02-08T11:29:00.000Z","key":1486553340000,"doc_count":16},{"key_as_string":"2017-02-08T11:30:00.000Z","key":1486553400000,"doc_count":13},{"key_as_string":"2017-02-08T11:31:00.000Z","key":1486553460000,"doc_count":16},{"key_as_string":"2017-02-08T11:32:00.000Z","key":1486553520000,"doc_count":13},{"key_as_string":"2017-02-08T11:33:00.000Z","key":1486553580000,"doc_count":18},{"key_as_string":"2017-02-08T11:34:00.000Z","key":1486553640000,"doc_count":12},{"key_as_string":"2017-02-08T11:35:00.000Z","key":1486553700000,"doc_count":11},{"key_as_string":"2017-02-08T11:36:00.000Z","key":1486553760000,"doc_count":16},{"key_as_string":"2017-02-08T11:37:00.000Z","key":1486553820000,"doc_count":18},{"key_as_string":"2017-02-08T11:38:00.000Z","key":1486553880000,"doc_count":10},{"key_as_string":"2017-02-08T11:39:00.000Z","key":1486553940000,"doc_count":17},{"key_as_string":"2017-02-08T11:40:00.000Z","key":1486554000000,"doc_count":11},{"key_as_string":"2017-02-08T11:41:00.000Z","key":1486554060000,"doc_count":20},{"key_as_string":"2017-02-08T11:42:00.000Z","key":1486554120000,"doc_count":10},{"key_as_string":"2017-02-08T11:43:00.000Z","key":1486554180000,"doc_count":17},{"key_as_string":"2017-02-08T11:44:00.000Z","key":1486554240000,"doc_count":21},{"key_as_string":"2017-02-08T11:45:00.000Z","key":1486554300000,"doc_count":13},{"key_as_string":"2017-02-08T11:46:00.000Z","key":1486554360000,"doc_count":12},{"key_as_string":"2017-02-08T11:47:00.000Z","key":1486554420000,"doc_count":11},{"key_as_string":"2017-02-08T11:48:00.000Z","key":1486554480000,"doc_count":14},{"key_as_string":"2017-02-08T11:49:00.000Z","key":1486554540000,"doc_count":11},{"key_as_string":"2017-02-08T11:50:00.000Z","key":1486554600000,"doc_count":9},{"key_as_string":"2017-02-08T11:51:00.000Z","key":1486554660000,"doc_count":14},{"key_as_string":"2017-02-08T11:52:00.000Z","key":1486554720000,"doc_count":13},{"key_as_string":"2017-02-08T11:53:00.000Z","key":1486554780000,"doc_count":12},{"key_as_string":"2017-02-08T11:54:00.000Z","key":1486554840000,"doc_count":13},{"key_as_string":"2017-02-08T11:55:00.000Z","key":1486554900000,"doc_count":18},{"key_as_string":"2017-02-08T11:56:00.000Z","key":1486554960000,"doc_count":18},{"key_as_string":"2017-02-08T11:57:00.000Z","key":1486555020000,"doc_count":13},{"key_as_string":"2017-02-08T11:58:00.000Z","key":1486555080000,"doc_count":13},{"key_as_string":"2017-02-08T11:59:00.000Z","key":1486555140000,"doc_count":14},{"key_as_string":"2017-02-08T12:00:00.000Z","key":1486555200000,"doc_count":18},{"key_as_string":"2017-02-08T12:01:00.000Z","key":1486555260000,"doc_count":14},{"key_as_string":"2017-02-08T12:02:00.000Z","key":1486555320000,"doc_count":14},{"key_as_string":"2017-02-08T12:03:00.000Z","key":1486555380000,"doc_count":13},{"key_as_string":"2017-02-08T12:04:00.000Z","key":1486555440000,"doc_count":18},{"key_as_string":"2017-02-08T12:05:00.000Z","key":1486555500000,"doc_count":13},{"key_as_string":"2017-02-08T12:06:00.000Z","key":1486555560000,"doc_count":16},{"key_as_string":"2017-02-08T12:07:00.000Z","key":1486555620000,"doc_count":13},{"key_as_string":"2017-02-08T12:08:00.000Z","key":1486555680000,"doc_count":14},{"key_as_string":"2017-02-08T12:09:00.000Z","key":1486555740000,"doc_count":16},{"key_as_string":"2017-02-08T12:10:00.000Z","key":1486555800000,"doc_count":13},{"key_as_string":"2017-02-08T12:11:00.000Z","key":1486555860000,"doc_count":13},{"key_as_string":"2017-02-08T12:12:00.000Z","key":1486555920000,"doc_count":15},{"key_as_string":"2017-02-08T12:13:00.000Z","key":1486555980000,"doc_count":11},{"key_as_string":"2017-02-08T12:14:00.000Z","key":1486556040000,"doc_count":15},{"key_as_string":"2017-02-08T12:15:00.000Z","key":1486556100000,"doc_count":12},{"key_as_string":"2017-02-08T12:16:00.000Z","key":1486556160000,"doc_count":14},{"key_as_string":"2017-02-08T12:17:00.000Z","key":1486556220000,"doc_count":13},{"key_as_string":"2017-02-08T12:18:00.000Z","key":1486556280000,"doc_count":19},{"key_as_string":"2017-02-08T12:19:00.000Z","key":1486556340000,"doc_count":13},{"key_as_string":"2017-02-08T12:20:00.000Z","key":1486556400000,"doc_count":14},{"key_as_string":"2017-02-08T12:21:00.000Z","key":1486556460000,"doc_count":12},{"key_as_string":"2017-02-08T12:22:00.000Z","key":1486556520000,"doc_count":15},{"key_as_string":"2017-02-08T12:23:00.000Z","key":1486556580000,"doc_count":17},{"key_as_string":"2017-02-08T12:24:00.000Z","key":1486556640000,"doc_count":14},{"key_as_string":"2017-02-08T12:25:00.000Z","key":1486556700000,"doc_count":10},{"key_as_string":"2017-02-08T12:26:00.000Z","key":1486556760000,"doc_count":13},{"key_as_string":"2017-02-08T12:27:00.000Z","key":1486556820000,"doc_count":14},{"key_as_string":"2017-02-08T12:28:00.000Z","key":1486556880000,"doc_count":10},{"key_as_string":"2017-02-08T12:29:00.000Z","key":1486556940000,"doc_count":13},{"key_as_string":"2017-02-08T12:30:00.000Z","key":1486557000000,"doc_count":16},{"key_as_string":"2017-02-08T12:31:00.000Z","key":1486557060000,"doc_count":15},{"key_as_string":"2017-02-08T12:32:00.000Z","key":1486557120000,"doc_count":9},{"key_as_string":"2017-02-08T12:33:00.000Z","key":1486557180000,"doc_count":16},{"key_as_string":"2017-02-08T12:34:00.000Z","key":1486557240000,"doc_count":16},{"key_as_string":"2017-02-08T12:35:00.000Z","key":1486557300000,"doc_count":9},{"key_as_string":"2017-02-08T12:36:00.000Z","key":1486557360000,"doc_count":11},{"key_as_string":"2017-02-08T12:37:00.000Z","key":1486557420000,"doc_count":12},{"key_as_string":"2017-02-08T12:38:00.000Z","key":1486557480000,"doc_count":16},{"key_as_string":"2017-02-08T12:39:00.000Z","key":1486557540000,"doc_count":13},{"key_as_string":"2017-02-08T12:40:00.000Z","key":1486557600000,"doc_count":12},{"key_as_string":"2017-02-08T12:41:00.000Z","key":1486557660000,"doc_count":15},{"key_as_string":"2017-02-08T12:42:00.000Z","key":1486557720000,"doc_count":12},{"key_as_string":"2017-02-08T12:43:00.000Z","key":1486557780000,"doc_count":13},{"key_as_string":"2017-02-08T12:44:00.000Z","key":1486557840000,"doc_count":13},{"key_as_string":"2017-02-08T12:45:00.000Z","key":1486557900000,"doc_count":15},{"key_as_string":"2017-02-08T12:46:00.000Z","key":1486557960000,"doc_count":15},{"key_as_string":"2017-02-08T12:47:00.000Z","key":1486558020000,"doc_count":12},{"key_as_string":"2017-02-08T12:48:00.000Z","key":1486558080000,"doc_count":13},{"key_as_string":"2017-02-08T12:49:00.000Z","key":1486558140000,"doc_count":15},{"key_as_string":"2017-02-08T12:50:00.000Z","key":1486558200000,"doc_count":16},{"key_as_string":"2017-02-08T12:51:00.000Z","key":1486558260000,"doc_count":14},{"key_as_string":"2017-02-08T12:52:00.000Z","key":1486558320000,"doc_count":11},{"key_as_string":"2017-02-08T12:53:00.000Z","key":1486558380000,"doc_count":10},{"key_as_string":"2017-02-08T12:54:00.000Z","key":1486558440000,"doc_count":14},{"key_as_string":"2017-02-08T12:55:00.000Z","key":1486558500000,"doc_count":11},{"key_as_string":"2017-02-08T12:56:00.000Z","key":1486558560000,"doc_count":9},{"key_as_string":"2017-02-08T12:57:00.000Z","key":1486558620000,"doc_count":13},{"key_as_string":"2017-02-08T12:58:00.000Z","key":1486558680000,"doc_count":13},{"key_as_string":"2017-02-08T12:59:00.000Z","key":1486558740000,"doc_count":13},{"key_as_string":"2017-02-08T13:00:00.000Z","key":1486558800000,"doc_count":18},{"key_as_string":"2017-02-08T13:01:00.000Z","key":1486558860000,"doc_count":13},{"key_as_string":"2017-02-08T13:02:00.000Z","key":1486558920000,"doc_count":20},{"key_as_string":"2017-02-08T13:03:00.000Z","key":1486558980000,"doc_count":10},{"key_as_string":"2017-02-08T13:04:00.000Z","key":1486559040000,"doc_count":16},{"key_as_string":"2017-02-08T13:05:00.000Z","key":1486559100000,"doc_count":11},{"key_as_string":"2017-02-08T13:06:00.000Z","key":1486559160000,"doc_count":18},{"key_as_string":"2017-02-08T13:07:00.000Z","key":1486559220000,"doc_count":10},{"key_as_string":"2017-02-08T13:08:00.000Z","key":1486559280000,"doc_count":15},{"key_as_string":"2017-02-08T13:09:00.000Z","key":1486559340000,"doc_count":17},{"key_as_string":"2017-02-08T13:10:00.000Z","key":1486559400000,"doc_count":6},{"key_as_string":"2017-02-08T13:11:00.000Z","key":1486559460000,"doc_count":19},{"key_as_string":"2017-02-08T13:12:00.000Z","key":1486559520000,"doc_count":12},{"key_as_string":"2017-02-08T13:13:00.000Z","key":1486559580000,"doc_count":11},{"key_as_string":"2017-02-08T13:14:00.000Z","key":1486559640000,"doc_count":15},{"key_as_string":"2017-02-08T13:15:00.000Z","key":1486559700000,"doc_count":10},{"key_as_string":"2017-02-08T13:16:00.000Z","key":1486559760000,"doc_count":16},{"key_as_string":"2017-02-08T13:17:00.000Z","key":1486559820000,"doc_count":16},{"key_as_string":"2017-02-08T13:18:00.000Z","key":1486559880000,"doc_count":14},{"key_as_string":"2017-02-08T13:19:00.000Z","key":1486559940000,"doc_count":12},{"key_as_string":"2017-02-08T13:20:00.000Z","key":1486560000000,"doc_count":10},{"key_as_string":"2017-02-08T13:21:00.000Z","key":1486560060000,"doc_count":18},{"key_as_string":"2017-02-08T13:22:00.000Z","key":1486560120000,"doc_count":14},{"key_as_string":"2017-02-08T13:23:00.000Z","key":1486560180000,"doc_count":11},{"key_as_string":"2017-02-08T13:24:00.000Z","key":1486560240000,"doc_count":15},{"key_as_string":"2017-02-08T13:25:00.000Z","key":1486560300000,"doc_count":17},{"key_as_string":"2017-02-08T13:26:00.000Z","key":1486560360000,"doc_count":10},{"key_as_string":"2017-02-08T13:27:00.000Z","key":1486560420000,"doc_count":14},{"key_as_string":"2017-02-08T13:28:00.000Z","key":1486560480000,"doc_count":14},{"key_as_string":"2017-02-08T13:29:00.000Z","key":1486560540000,"doc_count":13},{"key_as_string":"2017-02-08T13:30:00.000Z","key":1486560600000,"doc_count":15},{"key_as_string":"2017-02-08T13:31:00.000Z","key":1486560660000,"doc_count":16},{"key_as_string":"2017-02-08T13:32:00.000Z","key":1486560720000,"doc_count":13},{"key_as_string":"2017-02-08T13:33:00.000Z","key":1486560780000,"doc_count":13},{"key_as_string":"2017-02-08T13:34:00.000Z","key":1486560840000,"doc_count":15},{"key_as_string":"2017-02-08T13:35:00.000Z","key":1486560900000,"doc_count":8},{"key_as_string":"2017-02-08T13:36:00.000Z","key":1486560960000,"doc_count":18},{"key_as_string":"2017-02-08T13:37:00.000Z","key":1486561020000,"doc_count":13},{"key_as_string":"2017-02-08T13:38:00.000Z","key":1486561080000,"doc_count":14},{"key_as_string":"2017-02-08T13:39:00.000Z","key":1486561140000,"doc_count":14},{"key_as_string":"2017-02-08T13:40:00.000Z","key":1486561200000,"doc_count":17},{"key_as_string":"2017-02-08T13:41:00.000Z","key":1486561260000,"doc_count":12},{"key_as_string":"2017-02-08T13:42:00.000Z","key":1486561320000,"doc_count":10},{"key_as_string":"2017-02-08T13:43:00.000Z","key":1486561380000,"doc_count":17},{"key_as_string":"2017-02-08T13:44:00.000Z","key":1486561440000,"doc_count":12},{"key_as_string":"2017-02-08T13:45:00.000Z","key":1486561500000,"doc_count":9},{"key_as_string":"2017-02-08T13:46:00.000Z","key":1486561560000,"doc_count":21},{"key_as_string":"2017-02-08T13:47:00.000Z","key":1486561620000,"doc_count":16},{"key_as_string":"2017-02-08T13:48:00.000Z","key":1486561680000,"doc_count":17},{"key_as_string":"2017-02-08T13:49:00.000Z","key":1486561740000,"doc_count":11},{"key_as_string":"2017-02-08T13:50:00.000Z","key":1486561800000,"doc_count":17},{"key_as_string":"2017-02-08T13:51:00.000Z","key":1486561860000,"doc_count":9},{"key_as_string":"2017-02-08T13:52:00.000Z","key":1486561920000,"doc_count":14},{"key_as_string":"2017-02-08T13:53:00.000Z","key":1486561980000,"doc_count":10},{"key_as_string":"2017-02-08T13:54:00.000Z","key":1486562040000,"doc_count":9},{"key_as_string":"2017-02-08T13:55:00.000Z","key":1486562100000,"doc_count":21},{"key_as_string":"2017-02-08T13:56:00.000Z","key":1486562160000,"doc_count":14},{"key_as_string":"2017-02-08T13:57:00.000Z","key":1486562220000,"doc_count":11},{"key_as_string":"2017-02-08T13:58:00.000Z","key":1486562280000,"doc_count":15},{"key_as_string":"2017-02-08T13:59:00.000Z","key":1486562340000,"doc_count":10},{"key_as_string":"2017-02-08T14:00:00.000Z","key":1486562400000,"doc_count":12},{"key_as_string":"2017-02-08T14:01:00.000Z","key":1486562460000,"doc_count":15},{"key_as_string":"2017-02-08T14:02:00.000Z","key":1486562520000,"doc_count":11},{"key_as_string":"2017-02-08T14:03:00.000Z","key":1486562580000,"doc_count":14},{"key_as_string":"2017-02-08T14:04:00.000Z","key":1486562640000,"doc_count":18},{"key_as_string":"2017-02-08T14:05:00.000Z","key":1486562700000,"doc_count":11},{"key_as_string":"2017-02-08T14:06:00.000Z","key":1486562760000,"doc_count":11},{"key_as_string":"2017-02-08T14:07:00.000Z","key":1486562820000,"doc_count":16},{"key_as_string":"2017-02-08T14:08:00.000Z","key":1486562880000,"doc_count":10},{"key_as_string":"2017-02-08T14:09:00.000Z","key":1486562940000,"doc_count":14},{"key_as_string":"2017-02-08T14:10:00.000Z","key":1486563000000,"doc_count":14},{"key_as_string":"2017-02-08T14:11:00.000Z","key":1486563060000,"doc_count":9},{"key_as_string":"2017-02-08T14:12:00.000Z","key":1486563120000,"doc_count":15},{"key_as_string":"2017-02-08T14:13:00.000Z","key":1486563180000,"doc_count":17},{"key_as_string":"2017-02-08T14:14:00.000Z","key":1486563240000,"doc_count":5},{"key_as_string":"2017-02-08T14:15:00.000Z","key":1486563300000,"doc_count":15},{"key_as_string":"2017-02-08T14:16:00.000Z","key":1486563360000,"doc_count":15},{"key_as_string":"2017-02-08T14:17:00.000Z","key":1486563420000,"doc_count":13},{"key_as_string":"2017-02-08T14:18:00.000Z","key":1486563480000,"doc_count":15},{"key_as_string":"2017-02-08T14:19:00.000Z","key":1486563540000,"doc_count":14},{"key_as_string":"2017-02-08T14:20:00.000Z","key":1486563600000,"doc_count":8},{"key_as_string":"2017-02-08T14:21:00.000Z","key":1486563660000,"doc_count":14},{"key_as_string":"2017-02-08T14:22:00.000Z","key":1486563720000,"doc_count":11},{"key_as_string":"2017-02-08T14:23:00.000Z","key":1486563780000,"doc_count":14},{"key_as_string":"2017-02-08T14:24:00.000Z","key":1486563840000,"doc_count":15},{"key_as_string":"2017-02-08T14:25:00.000Z","key":1486563900000,"doc_count":9},{"key_as_string":"2017-02-08T14:26:00.000Z","key":1486563960000,"doc_count":16},{"key_as_string":"2017-02-08T14:27:00.000Z","key":1486564020000,"doc_count":15},{"key_as_string":"2017-02-08T14:28:00.000Z","key":1486564080000,"doc_count":13},{"key_as_string":"2017-02-08T14:29:00.000Z","key":1486564140000,"doc_count":14},{"key_as_string":"2017-02-08T14:30:00.000Z","key":1486564200000,"doc_count":10},{"key_as_string":"2017-02-08T14:31:00.000Z","key":1486564260000,"doc_count":15},{"key_as_string":"2017-02-08T14:32:00.000Z","key":1486564320000,"doc_count":15},{"key_as_string":"2017-02-08T14:33:00.000Z","key":1486564380000,"doc_count":11},{"key_as_string":"2017-02-08T14:34:00.000Z","key":1486564440000,"doc_count":13},{"key_as_string":"2017-02-08T14:35:00.000Z","key":1486564500000,"doc_count":14},{"key_as_string":"2017-02-08T14:36:00.000Z","key":1486564560000,"doc_count":11},{"key_as_string":"2017-02-08T14:37:00.000Z","key":1486564620000,"doc_count":10},{"key_as_string":"2017-02-08T14:38:00.000Z","key":1486564680000,"doc_count":18},{"key_as_string":"2017-02-08T14:39:00.000Z","key":1486564740000,"doc_count":12},{"key_as_string":"2017-02-08T14:40:00.000Z","key":1486564800000,"doc_count":14},{"key_as_string":"2017-02-08T14:41:00.000Z","key":1486564860000,"doc_count":15},{"key_as_string":"2017-02-08T14:42:00.000Z","key":1486564920000,"doc_count":17},{"key_as_string":"2017-02-08T14:43:00.000Z","key":1486564980000,"doc_count":9},{"key_as_string":"2017-02-08T14:44:00.000Z","key":1486565040000,"doc_count":10},{"key_as_string":"2017-02-08T14:45:00.000Z","key":1486565100000,"doc_count":9},{"key_as_string":"2017-02-08T14:46:00.000Z","key":1486565160000,"doc_count":15},{"key_as_string":"2017-02-08T14:47:00.000Z","key":1486565220000,"doc_count":12},{"key_as_string":"2017-02-08T14:48:00.000Z","key":1486565280000,"doc_count":10},{"key_as_string":"2017-02-08T14:49:00.000Z","key":1486565340000,"doc_count":18},{"key_as_string":"2017-02-08T14:50:00.000Z","key":1486565400000,"doc_count":9},{"key_as_string":"2017-02-08T14:51:00.000Z","key":1486565460000,"doc_count":15},{"key_as_string":"2017-02-08T14:52:00.000Z","key":1486565520000,"doc_count":19},{"key_as_string":"2017-02-08T14:53:00.000Z","key":1486565580000,"doc_count":11},{"key_as_string":"2017-02-08T14:54:00.000Z","key":1486565640000,"doc_count":12},{"key_as_string":"2017-02-08T14:55:00.000Z","key":1486565700000,"doc_count":10},{"key_as_string":"2017-02-08T14:56:00.000Z","key":1486565760000,"doc_count":9},{"key_as_string":"2017-02-08T14:57:00.000Z","key":1486565820000,"doc_count":21},{"key_as_string":"2017-02-08T14:58:00.000Z","key":1486565880000,"doc_count":13},{"key_as_string":"2017-02-08T14:59:00.000Z","key":1486565940000,"doc_count":16},{"key_as_string":"2017-02-08T15:00:00.000Z","key":1486566000000,"doc_count":9},{"key_as_string":"2017-02-08T15:01:00.000Z","key":1486566060000,"doc_count":16},{"key_as_string":"2017-02-08T15:02:00.000Z","key":1486566120000,"doc_count":11},{"key_as_string":"2017-02-08T15:03:00.000Z","key":1486566180000,"doc_count":14},{"key_as_string":"2017-02-08T15:04:00.000Z","key":1486566240000,"doc_count":16},{"key_as_string":"2017-02-08T15:05:00.000Z","key":1486566300000,"doc_count":10},{"key_as_string":"2017-02-08T15:06:00.000Z","key":1486566360000,"doc_count":14},{"key_as_string":"2017-02-08T15:07:00.000Z","key":1486566420000,"doc_count":9},{"key_as_string":"2017-02-08T15:08:00.000Z","key":1486566480000,"doc_count":13},{"key_as_string":"2017-02-08T15:09:00.000Z","key":1486566540000,"doc_count":13},{"key_as_string":"2017-02-08T15:10:00.000Z","key":1486566600000,"doc_count":14},{"key_as_string":"2017-02-08T15:11:00.000Z","key":1486566660000,"doc_count":15},{"key_as_string":"2017-02-08T15:12:00.000Z","key":1486566720000,"doc_count":15},{"key_as_string":"2017-02-08T15:13:00.000Z","key":1486566780000,"doc_count":11},{"key_as_string":"2017-02-08T15:14:00.000Z","key":1486566840000,"doc_count":7},{"key_as_string":"2017-02-08T15:15:00.000Z","key":1486566900000,"doc_count":20},{"key_as_string":"2017-02-08T15:16:00.000Z","key":1486566960000,"doc_count":11},{"key_as_string":"2017-02-08T15:17:00.000Z","key":1486567020000,"doc_count":15},{"key_as_string":"2017-02-08T15:18:00.000Z","key":1486567080000,"doc_count":14},{"key_as_string":"2017-02-08T15:19:00.000Z","key":1486567140000,"doc_count":11},{"key_as_string":"2017-02-08T15:20:00.000Z","key":1486567200000,"doc_count":18},{"key_as_string":"2017-02-08T15:21:00.000Z","key":1486567260000,"doc_count":11},{"key_as_string":"2017-02-08T15:22:00.000Z","key":1486567320000,"doc_count":12},{"key_as_string":"2017-02-08T15:23:00.000Z","key":1486567380000,"doc_count":12},{"key_as_string":"2017-02-08T15:24:00.000Z","key":1486567440000,"doc_count":8},{"key_as_string":"2017-02-08T15:25:00.000Z","key":1486567500000,"doc_count":11},{"key_as_string":"2017-02-08T15:26:00.000Z","key":1486567560000,"doc_count":12},{"key_as_string":"2017-02-08T15:27:00.000Z","key":1486567620000,"doc_count":12},{"key_as_string":"2017-02-08T15:28:00.000Z","key":1486567680000,"doc_count":14},{"key_as_string":"2017-02-08T15:29:00.000Z","key":1486567740000,"doc_count":13},{"key_as_string":"2017-02-08T15:30:00.000Z","key":1486567800000,"doc_count":13},{"key_as_string":"2017-02-08T15:31:00.000Z","key":1486567860000,"doc_count":14},{"key_as_string":"2017-02-08T15:32:00.000Z","key":1486567920000,"doc_count":15},{"key_as_string":"2017-02-08T15:33:00.000Z","key":1486567980000,"doc_count":12},{"key_as_string":"2017-02-08T15:34:00.000Z","key":1486568040000,"doc_count":15},{"key_as_string":"2017-02-08T15:35:00.000Z","key":1486568100000,"doc_count":20},{"key_as_string":"2017-02-08T15:36:00.000Z","key":1486568160000,"doc_count":11},{"key_as_string":"2017-02-08T15:37:00.000Z","key":1486568220000,"doc_count":13},{"key_as_string":"2017-02-08T15:38:00.000Z","key":1486568280000,"doc_count":13},{"key_as_string":"2017-02-08T15:39:00.000Z","key":1486568340000,"doc_count":10},{"key_as_string":"2017-02-08T15:40:00.000Z","key":1486568400000,"doc_count":14},{"key_as_string":"2017-02-08T15:41:00.000Z","key":1486568460000,"doc_count":16},{"key_as_string":"2017-02-08T15:42:00.000Z","key":1486568520000,"doc_count":17},{"key_as_string":"2017-02-08T15:43:00.000Z","key":1486568580000,"doc_count":15},{"key_as_string":"2017-02-08T15:44:00.000Z","key":1486568640000,"doc_count":14},{"key_as_string":"2017-02-08T15:45:00.000Z","key":1486568700000,"doc_count":14},{"key_as_string":"2017-02-08T15:46:00.000Z","key":1486568760000,"doc_count":12},{"key_as_string":"2017-02-08T15:47:00.000Z","key":1486568820000,"doc_count":16},{"key_as_string":"2017-02-08T15:48:00.000Z","key":1486568880000,"doc_count":16},{"key_as_string":"2017-02-08T15:49:00.000Z","key":1486568940000,"doc_count":12},{"key_as_string":"2017-02-08T15:50:00.000Z","key":1486569000000,"doc_count":15},{"key_as_string":"2017-02-08T15:51:00.000Z","key":1486569060000,"doc_count":10},{"key_as_string":"2017-02-08T15:52:00.000Z","key":1486569120000,"doc_count":12},{"key_as_string":"2017-02-08T15:53:00.000Z","key":1486569180000,"doc_count":12},{"key_as_string":"2017-02-08T15:54:00.000Z","key":1486569240000,"doc_count":16},{"key_as_string":"2017-02-08T15:55:00.000Z","key":1486569300000,"doc_count":12},{"key_as_string":"2017-02-08T15:56:00.000Z","key":1486569360000,"doc_count":9},{"key_as_string":"2017-02-08T15:57:00.000Z","key":1486569420000,"doc_count":11},{"key_as_string":"2017-02-08T15:58:00.000Z","key":1486569480000,"doc_count":10},{"key_as_string":"2017-02-08T15:59:00.000Z","key":1486569540000,"doc_count":12},{"key_as_string":"2017-02-08T16:00:00.000Z","key":1486569600000,"doc_count":12},{"key_as_string":"2017-02-08T16:01:00.000Z","key":1486569660000,"doc_count":14},{"key_as_string":"2017-02-08T16:02:00.000Z","key":1486569720000,"doc_count":13},{"key_as_string":"2017-02-08T16:03:00.000Z","key":1486569780000,"doc_count":11},{"key_as_string":"2017-02-08T16:04:00.000Z","key":1486569840000,"doc_count":12},{"key_as_string":"2017-02-08T16:05:00.000Z","key":1486569900000,"doc_count":16},{"key_as_string":"2017-02-08T16:06:00.000Z","key":1486569960000,"doc_count":13},{"key_as_string":"2017-02-08T16:07:00.000Z","key":1486570020000,"doc_count":12},{"key_as_string":"2017-02-08T16:08:00.000Z","key":1486570080000,"doc_count":15},{"key_as_string":"2017-02-08T16:09:00.000Z","key":1486570140000,"doc_count":7},{"key_as_string":"2017-02-08T16:10:00.000Z","key":1486570200000,"doc_count":15},{"key_as_string":"2017-02-08T16:11:00.000Z","key":1486570260000,"doc_count":13},{"key_as_string":"2017-02-08T16:12:00.000Z","key":1486570320000,"doc_count":10},{"key_as_string":"2017-02-08T16:13:00.000Z","key":1486570380000,"doc_count":13},{"key_as_string":"2017-02-08T16:14:00.000Z","key":1486570440000,"doc_count":13},{"key_as_string":"2017-02-08T16:15:00.000Z","key":1486570500000,"doc_count":7},{"key_as_string":"2017-02-08T16:16:00.000Z","key":1486570560000,"doc_count":12},{"key_as_string":"2017-02-08T16:17:00.000Z","key":1486570620000,"doc_count":11},{"key_as_string":"2017-02-08T16:18:00.000Z","key":1486570680000,"doc_count":10},{"key_as_string":"2017-02-08T16:19:00.000Z","key":1486570740000,"doc_count":8},{"key_as_string":"2017-02-08T16:20:00.000Z","key":1486570800000,"doc_count":13},{"key_as_string":"2017-02-08T16:21:00.000Z","key":1486570860000,"doc_count":12},{"key_as_string":"2017-02-08T16:22:00.000Z","key":1486570920000,"doc_count":12},{"key_as_string":"2017-02-08T16:23:00.000Z","key":1486570980000,"doc_count":9},{"key_as_string":"2017-02-08T16:24:00.000Z","key":1486571040000,"doc_count":12},{"key_as_string":"2017-02-08T16:25:00.000Z","key":1486571100000,"doc_count":13},{"key_as_string":"2017-02-08T16:26:00.000Z","key":1486571160000,"doc_count":17},{"key_as_string":"2017-02-08T16:27:00.000Z","key":1486571220000,"doc_count":15},{"key_as_string":"2017-02-08T16:28:00.000Z","key":1486571280000,"doc_count":14},{"key_as_string":"2017-02-08T16:29:00.000Z","key":1486571340000,"doc_count":15},{"key_as_string":"2017-02-08T16:30:00.000Z","key":1486571400000,"doc_count":12},{"key_as_string":"2017-02-08T16:31:00.000Z","key":1486571460000,"doc_count":11},{"key_as_string":"2017-02-08T16:32:00.000Z","key":1486571520000,"doc_count":16},{"key_as_string":"2017-02-08T16:33:00.000Z","key":1486571580000,"doc_count":9},{"key_as_string":"2017-02-08T16:34:00.000Z","key":1486571640000,"doc_count":10},{"key_as_string":"2017-02-08T16:35:00.000Z","key":1486571700000,"doc_count":16},{"key_as_string":"2017-02-08T16:36:00.000Z","key":1486571760000,"doc_count":18},{"key_as_string":"2017-02-08T16:37:00.000Z","key":1486571820000,"doc_count":13},{"key_as_string":"2017-02-08T16:38:00.000Z","key":1486571880000,"doc_count":12},{"key_as_string":"2017-02-08T16:39:00.000Z","key":1486571940000,"doc_count":12},{"key_as_string":"2017-02-08T16:40:00.000Z","key":1486572000000,"doc_count":15},{"key_as_string":"2017-02-08T16:41:00.000Z","key":1486572060000,"doc_count":14},{"key_as_string":"2017-02-08T16:42:00.000Z","key":1486572120000,"doc_count":9},{"key_as_string":"2017-02-08T16:43:00.000Z","key":1486572180000,"doc_count":10},{"key_as_string":"2017-02-08T16:44:00.000Z","key":1486572240000,"doc_count":14},{"key_as_string":"2017-02-08T16:45:00.000Z","key":1486572300000,"doc_count":15},{"key_as_string":"2017-02-08T16:46:00.000Z","key":1486572360000,"doc_count":18},{"key_as_string":"2017-02-08T16:47:00.000Z","key":1486572420000,"doc_count":8},{"key_as_string":"2017-02-08T16:48:00.000Z","key":1486572480000,"doc_count":14},{"key_as_string":"2017-02-08T16:49:00.000Z","key":1486572540000,"doc_count":11},{"key_as_string":"2017-02-08T16:50:00.000Z","key":1486572600000,"doc_count":12},{"key_as_string":"2017-02-08T16:51:00.000Z","key":1486572660000,"doc_count":8},{"key_as_string":"2017-02-08T16:52:00.000Z","key":1486572720000,"doc_count":13},{"key_as_string":"2017-02-08T16:53:00.000Z","key":1486572780000,"doc_count":12},{"key_as_string":"2017-02-08T16:54:00.000Z","key":1486572840000,"doc_count":15},{"key_as_string":"2017-02-08T16:55:00.000Z","key":1486572900000,"doc_count":15},{"key_as_string":"2017-02-08T16:56:00.000Z","key":1486572960000,"doc_count":12},{"key_as_string":"2017-02-08T16:57:00.000Z","key":1486573020000,"doc_count":10},{"key_as_string":"2017-02-08T16:58:00.000Z","key":1486573080000,"doc_count":11},{"key_as_string":"2017-02-08T16:59:00.000Z","key":1486573140000,"doc_count":18},{"key_as_string":"2017-02-08T17:00:00.000Z","key":1486573200000,"doc_count":11},{"key_as_string":"2017-02-08T17:01:00.000Z","key":1486573260000,"doc_count":10},{"key_as_string":"2017-02-08T17:02:00.000Z","key":1486573320000,"doc_count":14},{"key_as_string":"2017-02-08T17:03:00.000Z","key":1486573380000,"doc_count":11},{"key_as_string":"2017-02-08T17:04:00.000Z","key":1486573440000,"doc_count":14},{"key_as_string":"2017-02-08T17:05:00.000Z","key":1486573500000,"doc_count":7},{"key_as_string":"2017-02-08T17:06:00.000Z","key":1486573560000,"doc_count":17},{"key_as_string":"2017-02-08T17:07:00.000Z","key":1486573620000,"doc_count":6},{"key_as_string":"2017-02-08T17:08:00.000Z","key":1486573680000,"doc_count":12},{"key_as_string":"2017-02-08T17:09:00.000Z","key":1486573740000,"doc_count":7},{"key_as_string":"2017-02-08T17:10:00.000Z","key":1486573800000,"doc_count":17},{"key_as_string":"2017-02-08T17:11:00.000Z","key":1486573860000,"doc_count":19},{"key_as_string":"2017-02-08T17:12:00.000Z","key":1486573920000,"doc_count":13},{"key_as_string":"2017-02-08T17:13:00.000Z","key":1486573980000,"doc_count":9},{"key_as_string":"2017-02-08T17:14:00.000Z","key":1486574040000,"doc_count":16},{"key_as_string":"2017-02-08T17:15:00.000Z","key":1486574100000,"doc_count":15},{"key_as_string":"2017-02-08T17:16:00.000Z","key":1486574160000,"doc_count":11},{"key_as_string":"2017-02-08T17:17:00.000Z","key":1486574220000,"doc_count":18},{"key_as_string":"2017-02-08T17:18:00.000Z","key":1486574280000,"doc_count":13},{"key_as_string":"2017-02-08T17:19:00.000Z","key":1486574340000,"doc_count":16},{"key_as_string":"2017-02-08T17:20:00.000Z","key":1486574400000,"doc_count":14},{"key_as_string":"2017-02-08T17:21:00.000Z","key":1486574460000,"doc_count":13},{"key_as_string":"2017-02-08T17:22:00.000Z","key":1486574520000,"doc_count":13},{"key_as_string":"2017-02-08T17:23:00.000Z","key":1486574580000,"doc_count":11},{"key_as_string":"2017-02-08T17:24:00.000Z","key":1486574640000,"doc_count":8},{"key_as_string":"2017-02-08T17:25:00.000Z","key":1486574700000,"doc_count":9},{"key_as_string":"2017-02-08T17:26:00.000Z","key":1486574760000,"doc_count":16},{"key_as_string":"2017-02-08T17:27:00.000Z","key":1486574820000,"doc_count":13},{"key_as_string":"2017-02-08T17:28:00.000Z","key":1486574880000,"doc_count":9},{"key_as_string":"2017-02-08T17:29:00.000Z","key":1486574940000,"doc_count":13},{"key_as_string":"2017-02-08T17:30:00.000Z","key":1486575000000,"doc_count":13},{"key_as_string":"2017-02-08T17:31:00.000Z","key":1486575060000,"doc_count":15},{"key_as_string":"2017-02-08T17:32:00.000Z","key":1486575120000,"doc_count":10},{"key_as_string":"2017-02-08T17:33:00.000Z","key":1486575180000,"doc_count":14},{"key_as_string":"2017-02-08T17:34:00.000Z","key":1486575240000,"doc_count":10},{"key_as_string":"2017-02-08T17:35:00.000Z","key":1486575300000,"doc_count":14},{"key_as_string":"2017-02-08T17:36:00.000Z","key":1486575360000,"doc_count":10},{"key_as_string":"2017-02-08T17:37:00.000Z","key":1486575420000,"doc_count":9},{"key_as_string":"2017-02-08T17:38:00.000Z","key":1486575480000,"doc_count":11},{"key_as_string":"2017-02-08T17:39:00.000Z","key":1486575540000,"doc_count":12},{"key_as_string":"2017-02-08T17:40:00.000Z","key":1486575600000,"doc_count":10},{"key_as_string":"2017-02-08T17:41:00.000Z","key":1486575660000,"doc_count":15},{"key_as_string":"2017-02-08T17:42:00.000Z","key":1486575720000,"doc_count":18},{"key_as_string":"2017-02-08T17:43:00.000Z","key":1486575780000,"doc_count":9},{"key_as_string":"2017-02-08T17:44:00.000Z","key":1486575840000,"doc_count":14},{"key_as_string":"2017-02-08T17:45:00.000Z","key":1486575900000,"doc_count":9},{"key_as_string":"2017-02-08T17:46:00.000Z","key":1486575960000,"doc_count":11},{"key_as_string":"2017-02-08T17:47:00.000Z","key":1486576020000,"doc_count":13},{"key_as_string":"2017-02-08T17:48:00.000Z","key":1486576080000,"doc_count":10},{"key_as_string":"2017-02-08T17:49:00.000Z","key":1486576140000,"doc_count":9},{"key_as_string":"2017-02-08T17:50:00.000Z","key":1486576200000,"doc_count":13},{"key_as_string":"2017-02-08T17:51:00.000Z","key":1486576260000,"doc_count":12},{"key_as_string":"2017-02-08T17:52:00.000Z","key":1486576320000,"doc_count":11},{"key_as_string":"2017-02-08T17:53:00.000Z","key":1486576380000,"doc_count":12},{"key_as_string":"2017-02-08T17:54:00.000Z","key":1486576440000,"doc_count":11},{"key_as_string":"2017-02-08T17:55:00.000Z","key":1486576500000,"doc_count":14},{"key_as_string":"2017-02-08T17:56:00.000Z","key":1486576560000,"doc_count":17},{"key_as_string":"2017-02-08T17:57:00.000Z","key":1486576620000,"doc_count":12},{"key_as_string":"2017-02-08T17:58:00.000Z","key":1486576680000,"doc_count":8},{"key_as_string":"2017-02-08T17:59:00.000Z","key":1486576740000,"doc_count":12},{"key_as_string":"2017-02-08T18:00:00.000Z","key":1486576800000,"doc_count":15},{"key_as_string":"2017-02-08T18:01:00.000Z","key":1486576860000,"doc_count":10},{"key_as_string":"2017-02-08T18:02:00.000Z","key":1486576920000,"doc_count":11},{"key_as_string":"2017-02-08T18:03:00.000Z","key":1486576980000,"doc_count":11},{"key_as_string":"2017-02-08T18:04:00.000Z","key":1486577040000,"doc_count":13},{"key_as_string":"2017-02-08T18:05:00.000Z","key":1486577100000,"doc_count":11},{"key_as_string":"2017-02-08T18:06:00.000Z","key":1486577160000,"doc_count":10},{"key_as_string":"2017-02-08T18:07:00.000Z","key":1486577220000,"doc_count":12},{"key_as_string":"2017-02-08T18:08:00.000Z","key":1486577280000,"doc_count":13},{"key_as_string":"2017-02-08T18:09:00.000Z","key":1486577340000,"doc_count":8},{"key_as_string":"2017-02-08T18:10:00.000Z","key":1486577400000,"doc_count":6},{"key_as_string":"2017-02-08T18:11:00.000Z","key":1486577460000,"doc_count":17},{"key_as_string":"2017-02-08T18:12:00.000Z","key":1486577520000,"doc_count":11},{"key_as_string":"2017-02-08T18:13:00.000Z","key":1486577580000,"doc_count":13},{"key_as_string":"2017-02-08T18:14:00.000Z","key":1486577640000,"doc_count":10},{"key_as_string":"2017-02-08T18:15:00.000Z","key":1486577700000,"doc_count":11},{"key_as_string":"2017-02-08T18:16:00.000Z","key":1486577760000,"doc_count":13},{"key_as_string":"2017-02-08T18:17:00.000Z","key":1486577820000,"doc_count":12},{"key_as_string":"2017-02-08T18:18:00.000Z","key":1486577880000,"doc_count":15},{"key_as_string":"2017-02-08T18:19:00.000Z","key":1486577940000,"doc_count":10},{"key_as_string":"2017-02-08T18:20:00.000Z","key":1486578000000,"doc_count":10},{"key_as_string":"2017-02-08T18:21:00.000Z","key":1486578060000,"doc_count":12},{"key_as_string":"2017-02-08T18:22:00.000Z","key":1486578120000,"doc_count":16},{"key_as_string":"2017-02-08T18:23:00.000Z","key":1486578180000,"doc_count":7},{"key_as_string":"2017-02-08T18:24:00.000Z","key":1486578240000,"doc_count":12},{"key_as_string":"2017-02-08T18:25:00.000Z","key":1486578300000,"doc_count":10},{"key_as_string":"2017-02-08T18:26:00.000Z","key":1486578360000,"doc_count":8},{"key_as_string":"2017-02-08T18:27:00.000Z","key":1486578420000,"doc_count":15},{"key_as_string":"2017-02-08T18:28:00.000Z","key":1486578480000,"doc_count":10},{"key_as_string":"2017-02-08T18:29:00.000Z","key":1486578540000,"doc_count":13},{"key_as_string":"2017-02-08T18:30:00.000Z","key":1486578600000,"doc_count":10},{"key_as_string":"2017-02-08T18:31:00.000Z","key":1486578660000,"doc_count":11},{"key_as_string":"2017-02-08T18:32:00.000Z","key":1486578720000,"doc_count":12},{"key_as_string":"2017-02-08T18:33:00.000Z","key":1486578780000,"doc_count":10},{"key_as_string":"2017-02-08T18:34:00.000Z","key":1486578840000,"doc_count":15},{"key_as_string":"2017-02-08T18:35:00.000Z","key":1486578900000,"doc_count":13},{"key_as_string":"2017-02-08T18:36:00.000Z","key":1486578960000,"doc_count":13},{"key_as_string":"2017-02-08T18:37:00.000Z","key":1486579020000,"doc_count":11},{"key_as_string":"2017-02-08T18:38:00.000Z","key":1486579080000,"doc_count":9},{"key_as_string":"2017-02-08T18:39:00.000Z","key":1486579140000,"doc_count":14},{"key_as_string":"2017-02-08T18:40:00.000Z","key":1486579200000,"doc_count":12},{"key_as_string":"2017-02-08T18:41:00.000Z","key":1486579260000,"doc_count":14},{"key_as_string":"2017-02-08T18:42:00.000Z","key":1486579320000,"doc_count":13},{"key_as_string":"2017-02-08T18:43:00.000Z","key":1486579380000,"doc_count":11},{"key_as_string":"2017-02-08T18:44:00.000Z","key":1486579440000,"doc_count":11},{"key_as_string":"2017-02-08T18:45:00.000Z","key":1486579500000,"doc_count":11},{"key_as_string":"2017-02-08T18:46:00.000Z","key":1486579560000,"doc_count":9},{"key_as_string":"2017-02-08T18:47:00.000Z","key":1486579620000,"doc_count":14},{"key_as_string":"2017-02-08T18:48:00.000Z","key":1486579680000,"doc_count":9},{"key_as_string":"2017-02-08T18:49:00.000Z","key":1486579740000,"doc_count":13},{"key_as_string":"2017-02-08T18:50:00.000Z","key":1486579800000,"doc_count":16},{"key_as_string":"2017-02-08T18:51:00.000Z","key":1486579860000,"doc_count":10},{"key_as_string":"2017-02-08T18:52:00.000Z","key":1486579920000,"doc_count":9},{"key_as_string":"2017-02-08T18:53:00.000Z","key":1486579980000,"doc_count":9},{"key_as_string":"2017-02-08T18:54:00.000Z","key":1486580040000,"doc_count":11},{"key_as_string":"2017-02-08T18:55:00.000Z","key":1486580100000,"doc_count":13},{"key_as_string":"2017-02-08T18:56:00.000Z","key":1486580160000,"doc_count":11},{"key_as_string":"2017-02-08T18:57:00.000Z","key":1486580220000,"doc_count":16},{"key_as_string":"2017-02-08T18:58:00.000Z","key":1486580280000,"doc_count":11},{"key_as_string":"2017-02-08T18:59:00.000Z","key":1486580340000,"doc_count":14},{"key_as_string":"2017-02-08T19:00:00.000Z","key":1486580400000,"doc_count":14},{"key_as_string":"2017-02-08T19:01:00.000Z","key":1486580460000,"doc_count":7},{"key_as_string":"2017-02-08T19:02:00.000Z","key":1486580520000,"doc_count":12},{"key_as_string":"2017-02-08T19:03:00.000Z","key":1486580580000,"doc_count":12},{"key_as_string":"2017-02-08T19:04:00.000Z","key":1486580640000,"doc_count":8},{"key_as_string":"2017-02-08T19:05:00.000Z","key":1486580700000,"doc_count":7},{"key_as_string":"2017-02-08T19:06:00.000Z","key":1486580760000,"doc_count":17},{"key_as_string":"2017-02-08T19:07:00.000Z","key":1486580820000,"doc_count":8},{"key_as_string":"2017-02-08T19:08:00.000Z","key":1486580880000,"doc_count":15},{"key_as_string":"2017-02-08T19:09:00.000Z","key":1486580940000,"doc_count":11},{"key_as_string":"2017-02-08T19:10:00.000Z","key":1486581000000,"doc_count":9},{"key_as_string":"2017-02-08T19:11:00.000Z","key":1486581060000,"doc_count":14},{"key_as_string":"2017-02-08T19:12:00.000Z","key":1486581120000,"doc_count":15},{"key_as_string":"2017-02-08T19:13:00.000Z","key":1486581180000,"doc_count":12},{"key_as_string":"2017-02-08T19:14:00.000Z","key":1486581240000,"doc_count":7},{"key_as_string":"2017-02-08T19:15:00.000Z","key":1486581300000,"doc_count":10},{"key_as_string":"2017-02-08T19:16:00.000Z","key":1486581360000,"doc_count":13},{"key_as_string":"2017-02-08T19:17:00.000Z","key":1486581420000,"doc_count":13},{"key_as_string":"2017-02-08T19:18:00.000Z","key":1486581480000,"doc_count":8},{"key_as_string":"2017-02-08T19:19:00.000Z","key":1486581540000,"doc_count":9},{"key_as_string":"2017-02-08T19:20:00.000Z","key":1486581600000,"doc_count":12},{"key_as_string":"2017-02-08T19:21:00.000Z","key":1486581660000,"doc_count":8},{"key_as_string":"2017-02-08T19:22:00.000Z","key":1486581720000,"doc_count":16},{"key_as_string":"2017-02-08T19:23:00.000Z","key":1486581780000,"doc_count":10},{"key_as_string":"2017-02-08T19:24:00.000Z","key":1486581840000,"doc_count":12},{"key_as_string":"2017-02-08T19:25:00.000Z","key":1486581900000,"doc_count":11},{"key_as_string":"2017-02-08T19:26:00.000Z","key":1486581960000,"doc_count":8},{"key_as_string":"2017-02-08T19:27:00.000Z","key":1486582020000,"doc_count":11},{"key_as_string":"2017-02-08T19:28:00.000Z","key":1486582080000,"doc_count":19},{"key_as_string":"2017-02-08T19:29:00.000Z","key":1486582140000,"doc_count":8},{"key_as_string":"2017-02-08T19:30:00.000Z","key":1486582200000,"doc_count":13},{"key_as_string":"2017-02-08T19:31:00.000Z","key":1486582260000,"doc_count":17},{"key_as_string":"2017-02-08T19:32:00.000Z","key":1486582320000,"doc_count":11},{"key_as_string":"2017-02-08T19:33:00.000Z","key":1486582380000,"doc_count":9},{"key_as_string":"2017-02-08T19:34:00.000Z","key":1486582440000,"doc_count":9},{"key_as_string":"2017-02-08T19:35:00.000Z","key":1486582500000,"doc_count":18},{"key_as_string":"2017-02-08T19:36:00.000Z","key":1486582560000,"doc_count":8},{"key_as_string":"2017-02-08T19:37:00.000Z","key":1486582620000,"doc_count":9},{"key_as_string":"2017-02-08T19:38:00.000Z","key":1486582680000,"doc_count":12},{"key_as_string":"2017-02-08T19:39:00.000Z","key":1486582740000,"doc_count":11},{"key_as_string":"2017-02-08T19:40:00.000Z","key":1486582800000,"doc_count":13},{"key_as_string":"2017-02-08T19:41:00.000Z","key":1486582860000,"doc_count":8},{"key_as_string":"2017-02-08T19:42:00.000Z","key":1486582920000,"doc_count":10},{"key_as_string":"2017-02-08T19:43:00.000Z","key":1486582980000,"doc_count":10},{"key_as_string":"2017-02-08T19:44:00.000Z","key":1486583040000,"doc_count":15},{"key_as_string":"2017-02-08T19:45:00.000Z","key":1486583100000,"doc_count":7},{"key_as_string":"2017-02-08T19:46:00.000Z","key":1486583160000,"doc_count":12},{"key_as_string":"2017-02-08T19:47:00.000Z","key":1486583220000,"doc_count":8},{"key_as_string":"2017-02-08T19:48:00.000Z","key":1486583280000,"doc_count":11},{"key_as_string":"2017-02-08T19:49:00.000Z","key":1486583340000,"doc_count":10},{"key_as_string":"2017-02-08T19:50:00.000Z","key":1486583400000,"doc_count":10},{"key_as_string":"2017-02-08T19:51:00.000Z","key":1486583460000,"doc_count":9},{"key_as_string":"2017-02-08T19:52:00.000Z","key":1486583520000,"doc_count":13},{"key_as_string":"2017-02-08T19:53:00.000Z","key":1486583580000,"doc_count":6},{"key_as_string":"2017-02-08T19:54:00.000Z","key":1486583640000,"doc_count":16},{"key_as_string":"2017-02-08T19:55:00.000Z","key":1486583700000,"doc_count":15},{"key_as_string":"2017-02-08T19:56:00.000Z","key":1486583760000,"doc_count":14},{"key_as_string":"2017-02-08T19:57:00.000Z","key":1486583820000,"doc_count":6},{"key_as_string":"2017-02-08T19:58:00.000Z","key":1486583880000,"doc_count":13},{"key_as_string":"2017-02-08T19:59:00.000Z","key":1486583940000,"doc_count":11},{"key_as_string":"2017-02-08T20:00:00.000Z","key":1486584000000,"doc_count":9},{"key_as_string":"2017-02-08T20:01:00.000Z","key":1486584060000,"doc_count":11},{"key_as_string":"2017-02-08T20:02:00.000Z","key":1486584120000,"doc_count":16},{"key_as_string":"2017-02-08T20:03:00.000Z","key":1486584180000,"doc_count":8},{"key_as_string":"2017-02-08T20:04:00.000Z","key":1486584240000,"doc_count":9},{"key_as_string":"2017-02-08T20:05:00.000Z","key":1486584300000,"doc_count":8},{"key_as_string":"2017-02-08T20:06:00.000Z","key":1486584360000,"doc_count":13},{"key_as_string":"2017-02-08T20:07:00.000Z","key":1486584420000,"doc_count":15},{"key_as_string":"2017-02-08T20:08:00.000Z","key":1486584480000,"doc_count":7},{"key_as_string":"2017-02-08T20:09:00.000Z","key":1486584540000,"doc_count":13},{"key_as_string":"2017-02-08T20:10:00.000Z","key":1486584600000,"doc_count":12},{"key_as_string":"2017-02-08T20:11:00.000Z","key":1486584660000,"doc_count":6},{"key_as_string":"2017-02-08T20:12:00.000Z","key":1486584720000,"doc_count":9},{"key_as_string":"2017-02-08T20:13:00.000Z","key":1486584780000,"doc_count":12},{"key_as_string":"2017-02-08T20:14:00.000Z","key":1486584840000,"doc_count":13},{"key_as_string":"2017-02-08T20:15:00.000Z","key":1486584900000,"doc_count":13},{"key_as_string":"2017-02-08T20:16:00.000Z","key":1486584960000,"doc_count":10},{"key_as_string":"2017-02-08T20:17:00.000Z","key":1486585020000,"doc_count":8},{"key_as_string":"2017-02-08T20:18:00.000Z","key":1486585080000,"doc_count":12},{"key_as_string":"2017-02-08T20:19:00.000Z","key":1486585140000,"doc_count":12},{"key_as_string":"2017-02-08T20:20:00.000Z","key":1486585200000,"doc_count":14},{"key_as_string":"2017-02-08T20:21:00.000Z","key":1486585260000,"doc_count":11},{"key_as_string":"2017-02-08T20:22:00.000Z","key":1486585320000,"doc_count":13},{"key_as_string":"2017-02-08T20:23:00.000Z","key":1486585380000,"doc_count":13},{"key_as_string":"2017-02-08T20:24:00.000Z","key":1486585440000,"doc_count":9},{"key_as_string":"2017-02-08T20:25:00.000Z","key":1486585500000,"doc_count":11},{"key_as_string":"2017-02-08T20:26:00.000Z","key":1486585560000,"doc_count":9},{"key_as_string":"2017-02-08T20:27:00.000Z","key":1486585620000,"doc_count":20},{"key_as_string":"2017-02-08T20:28:00.000Z","key":1486585680000,"doc_count":6},{"key_as_string":"2017-02-08T20:29:00.000Z","key":1486585740000,"doc_count":12},{"key_as_string":"2017-02-08T20:30:00.000Z","key":1486585800000,"doc_count":11},{"key_as_string":"2017-02-08T20:31:00.000Z","key":1486585860000,"doc_count":9},{"key_as_string":"2017-02-08T20:32:00.000Z","key":1486585920000,"doc_count":12},{"key_as_string":"2017-02-08T20:33:00.000Z","key":1486585980000,"doc_count":8},{"key_as_string":"2017-02-08T20:34:00.000Z","key":1486586040000,"doc_count":12},{"key_as_string":"2017-02-08T20:35:00.000Z","key":1486586100000,"doc_count":11},{"key_as_string":"2017-02-08T20:36:00.000Z","key":1486586160000,"doc_count":9},{"key_as_string":"2017-02-08T20:37:00.000Z","key":1486586220000,"doc_count":14},{"key_as_string":"2017-02-08T20:38:00.000Z","key":1486586280000,"doc_count":12},{"key_as_string":"2017-02-08T20:39:00.000Z","key":1486586340000,"doc_count":8},{"key_as_string":"2017-02-08T20:40:00.000Z","key":1486586400000,"doc_count":11},{"key_as_string":"2017-02-08T20:41:00.000Z","key":1486586460000,"doc_count":10},{"key_as_string":"2017-02-08T20:42:00.000Z","key":1486586520000,"doc_count":8},{"key_as_string":"2017-02-08T20:43:00.000Z","key":1486586580000,"doc_count":11},{"key_as_string":"2017-02-08T20:44:00.000Z","key":1486586640000,"doc_count":16},{"key_as_string":"2017-02-08T20:45:00.000Z","key":1486586700000,"doc_count":9},{"key_as_string":"2017-02-08T20:46:00.000Z","key":1486586760000,"doc_count":9},{"key_as_string":"2017-02-08T20:47:00.000Z","key":1486586820000,"doc_count":8},{"key_as_string":"2017-02-08T20:48:00.000Z","key":1486586880000,"doc_count":13},{"key_as_string":"2017-02-08T20:49:00.000Z","key":1486586940000,"doc_count":10},{"key_as_string":"2017-02-08T20:50:00.000Z","key":1486587000000,"doc_count":9},{"key_as_string":"2017-02-08T20:51:00.000Z","key":1486587060000,"doc_count":10},{"key_as_string":"2017-02-08T20:52:00.000Z","key":1486587120000,"doc_count":10},{"key_as_string":"2017-02-08T20:53:00.000Z","key":1486587180000,"doc_count":10},{"key_as_string":"2017-02-08T20:54:00.000Z","key":1486587240000,"doc_count":12},{"key_as_string":"2017-02-08T20:55:00.000Z","key":1486587300000,"doc_count":8},{"key_as_string":"2017-02-08T20:56:00.000Z","key":1486587360000,"doc_count":10},{"key_as_string":"2017-02-08T20:57:00.000Z","key":1486587420000,"doc_count":11},{"key_as_string":"2017-02-08T20:58:00.000Z","key":1486587480000,"doc_count":9},{"key_as_string":"2017-02-08T20:59:00.000Z","key":1486587540000,"doc_count":15},{"key_as_string":"2017-02-08T21:00:00.000Z","key":1486587600000,"doc_count":12},{"key_as_string":"2017-02-08T21:01:00.000Z","key":1486587660000,"doc_count":13},{"key_as_string":"2017-02-08T21:02:00.000Z","key":1486587720000,"doc_count":10},{"key_as_string":"2017-02-08T21:03:00.000Z","key":1486587780000,"doc_count":7},{"key_as_string":"2017-02-08T21:04:00.000Z","key":1486587840000,"doc_count":9},{"key_as_string":"2017-02-08T21:05:00.000Z","key":1486587900000,"doc_count":13},{"key_as_string":"2017-02-08T21:06:00.000Z","key":1486587960000,"doc_count":9},{"key_as_string":"2017-02-08T21:07:00.000Z","key":1486588020000,"doc_count":12},{"key_as_string":"2017-02-08T21:08:00.000Z","key":1486588080000,"doc_count":6},{"key_as_string":"2017-02-08T21:09:00.000Z","key":1486588140000,"doc_count":17},{"key_as_string":"2017-02-08T21:10:00.000Z","key":1486588200000,"doc_count":4},{"key_as_string":"2017-02-08T21:11:00.000Z","key":1486588260000,"doc_count":13},{"key_as_string":"2017-02-08T21:12:00.000Z","key":1486588320000,"doc_count":9},{"key_as_string":"2017-02-08T21:13:00.000Z","key":1486588380000,"doc_count":13},{"key_as_string":"2017-02-08T21:14:00.000Z","key":1486588440000,"doc_count":10},{"key_as_string":"2017-02-08T21:15:00.000Z","key":1486588500000,"doc_count":8},{"key_as_string":"2017-02-08T21:16:00.000Z","key":1486588560000,"doc_count":8},{"key_as_string":"2017-02-08T21:17:00.000Z","key":1486588620000,"doc_count":10},{"key_as_string":"2017-02-08T21:18:00.000Z","key":1486588680000,"doc_count":12},{"key_as_string":"2017-02-08T21:19:00.000Z","key":1486588740000,"doc_count":10},{"key_as_string":"2017-02-08T21:20:00.000Z","key":1486588800000,"doc_count":13},{"key_as_string":"2017-02-08T21:21:00.000Z","key":1486588860000,"doc_count":14},{"key_as_string":"2017-02-08T21:22:00.000Z","key":1486588920000,"doc_count":9},{"key_as_string":"2017-02-08T21:23:00.000Z","key":1486588980000,"doc_count":9},{"key_as_string":"2017-02-08T21:24:00.000Z","key":1486589040000,"doc_count":12},{"key_as_string":"2017-02-08T21:25:00.000Z","key":1486589100000,"doc_count":12},{"key_as_string":"2017-02-08T21:26:00.000Z","key":1486589160000,"doc_count":19},{"key_as_string":"2017-02-08T21:27:00.000Z","key":1486589220000,"doc_count":6},{"key_as_string":"2017-02-08T21:28:00.000Z","key":1486589280000,"doc_count":15},{"key_as_string":"2017-02-08T21:29:00.000Z","key":1486589340000,"doc_count":10},{"key_as_string":"2017-02-08T21:30:00.000Z","key":1486589400000,"doc_count":9},{"key_as_string":"2017-02-08T21:31:00.000Z","key":1486589460000,"doc_count":9},{"key_as_string":"2017-02-08T21:32:00.000Z","key":1486589520000,"doc_count":12},{"key_as_string":"2017-02-08T21:33:00.000Z","key":1486589580000,"doc_count":10},{"key_as_string":"2017-02-08T21:34:00.000Z","key":1486589640000,"doc_count":11},{"key_as_string":"2017-02-08T21:35:00.000Z","key":1486589700000,"doc_count":10},{"key_as_string":"2017-02-08T21:36:00.000Z","key":1486589760000,"doc_count":15},{"key_as_string":"2017-02-08T21:37:00.000Z","key":1486589820000,"doc_count":8},{"key_as_string":"2017-02-08T21:38:00.000Z","key":1486589880000,"doc_count":11},{"key_as_string":"2017-02-08T21:39:00.000Z","key":1486589940000,"doc_count":9},{"key_as_string":"2017-02-08T21:40:00.000Z","key":1486590000000,"doc_count":9},{"key_as_string":"2017-02-08T21:41:00.000Z","key":1486590060000,"doc_count":10},{"key_as_string":"2017-02-08T21:42:00.000Z","key":1486590120000,"doc_count":8},{"key_as_string":"2017-02-08T21:43:00.000Z","key":1486590180000,"doc_count":13},{"key_as_string":"2017-02-08T21:44:00.000Z","key":1486590240000,"doc_count":13},{"key_as_string":"2017-02-08T21:45:00.000Z","key":1486590300000,"doc_count":15},{"key_as_string":"2017-02-08T21:46:00.000Z","key":1486590360000,"doc_count":8},{"key_as_string":"2017-02-08T21:47:00.000Z","key":1486590420000,"doc_count":7},{"key_as_string":"2017-02-08T21:48:00.000Z","key":1486590480000,"doc_count":14},{"key_as_string":"2017-02-08T21:49:00.000Z","key":1486590540000,"doc_count":8},{"key_as_string":"2017-02-08T21:50:00.000Z","key":1486590600000,"doc_count":11},{"key_as_string":"2017-02-08T21:51:00.000Z","key":1486590660000,"doc_count":12},{"key_as_string":"2017-02-08T21:52:00.000Z","key":1486590720000,"doc_count":9},{"key_as_string":"2017-02-08T21:53:00.000Z","key":1486590780000,"doc_count":11},{"key_as_string":"2017-02-08T21:54:00.000Z","key":1486590840000,"doc_count":9},{"key_as_string":"2017-02-08T21:55:00.000Z","key":1486590900000,"doc_count":7},{"key_as_string":"2017-02-08T21:56:00.000Z","key":1486590960000,"doc_count":13},{"key_as_string":"2017-02-08T21:57:00.000Z","key":1486591020000,"doc_count":10},{"key_as_string":"2017-02-08T21:58:00.000Z","key":1486591080000,"doc_count":12},{"key_as_string":"2017-02-08T21:59:00.000Z","key":1486591140000,"doc_count":15},{"key_as_string":"2017-02-08T22:00:00.000Z","key":1486591200000,"doc_count":13},{"key_as_string":"2017-02-08T22:01:00.000Z","key":1486591260000,"doc_count":14},{"key_as_string":"2017-02-08T22:02:00.000Z","key":1486591320000,"doc_count":7},{"key_as_string":"2017-02-08T22:03:00.000Z","key":1486591380000,"doc_count":12},{"key_as_string":"2017-02-08T22:04:00.000Z","key":1486591440000,"doc_count":11},{"key_as_string":"2017-02-08T22:05:00.000Z","key":1486591500000,"doc_count":11},{"key_as_string":"2017-02-08T22:06:00.000Z","key":1486591560000,"doc_count":11},{"key_as_string":"2017-02-08T22:07:00.000Z","key":1486591620000,"doc_count":8},{"key_as_string":"2017-02-08T22:08:00.000Z","key":1486591680000,"doc_count":14},{"key_as_string":"2017-02-08T22:09:00.000Z","key":1486591740000,"doc_count":11},{"key_as_string":"2017-02-08T22:10:00.000Z","key":1486591800000,"doc_count":16},{"key_as_string":"2017-02-08T22:11:00.000Z","key":1486591860000,"doc_count":8},{"key_as_string":"2017-02-08T22:12:00.000Z","key":1486591920000,"doc_count":15},{"key_as_string":"2017-02-08T22:13:00.000Z","key":1486591980000,"doc_count":11},{"key_as_string":"2017-02-08T22:14:00.000Z","key":1486592040000,"doc_count":6},{"key_as_string":"2017-02-08T22:15:00.000Z","key":1486592100000,"doc_count":11},{"key_as_string":"2017-02-08T22:16:00.000Z","key":1486592160000,"doc_count":9},{"key_as_string":"2017-02-08T22:17:00.000Z","key":1486592220000,"doc_count":15},{"key_as_string":"2017-02-08T22:18:00.000Z","key":1486592280000,"doc_count":10},{"key_as_string":"2017-02-08T22:19:00.000Z","key":1486592340000,"doc_count":7},{"key_as_string":"2017-02-08T22:20:00.000Z","key":1486592400000,"doc_count":11},{"key_as_string":"2017-02-08T22:21:00.000Z","key":1486592460000,"doc_count":8},{"key_as_string":"2017-02-08T22:22:00.000Z","key":1486592520000,"doc_count":11},{"key_as_string":"2017-02-08T22:23:00.000Z","key":1486592580000,"doc_count":5},{"key_as_string":"2017-02-08T22:24:00.000Z","key":1486592640000,"doc_count":13},{"key_as_string":"2017-02-08T22:25:00.000Z","key":1486592700000,"doc_count":11},{"key_as_string":"2017-02-08T22:26:00.000Z","key":1486592760000,"doc_count":13},{"key_as_string":"2017-02-08T22:27:00.000Z","key":1486592820000,"doc_count":15},{"key_as_string":"2017-02-08T22:28:00.000Z","key":1486592880000,"doc_count":12},{"key_as_string":"2017-02-08T22:29:00.000Z","key":1486592940000,"doc_count":7},{"key_as_string":"2017-02-08T22:30:00.000Z","key":1486593000000,"doc_count":14},{"key_as_string":"2017-02-08T22:31:00.000Z","key":1486593060000,"doc_count":13},{"key_as_string":"2017-02-08T22:32:00.000Z","key":1486593120000,"doc_count":13},{"key_as_string":"2017-02-08T22:33:00.000Z","key":1486593180000,"doc_count":9},{"key_as_string":"2017-02-08T22:34:00.000Z","key":1486593240000,"doc_count":13},{"key_as_string":"2017-02-08T22:35:00.000Z","key":1486593300000,"doc_count":14},{"key_as_string":"2017-02-08T22:36:00.000Z","key":1486593360000,"doc_count":9},{"key_as_string":"2017-02-08T22:37:00.000Z","key":1486593420000,"doc_count":11},{"key_as_string":"2017-02-08T22:38:00.000Z","key":1486593480000,"doc_count":8},{"key_as_string":"2017-02-08T22:39:00.000Z","key":1486593540000,"doc_count":14},{"key_as_string":"2017-02-08T22:40:00.000Z","key":1486593600000,"doc_count":16},{"key_as_string":"2017-02-08T22:41:00.000Z","key":1486593660000,"doc_count":7},{"key_as_string":"2017-02-08T22:42:00.000Z","key":1486593720000,"doc_count":14},{"key_as_string":"2017-02-08T22:43:00.000Z","key":1486593780000,"doc_count":10},{"key_as_string":"2017-02-08T22:44:00.000Z","key":1486593840000,"doc_count":12},{"key_as_string":"2017-02-08T22:45:00.000Z","key":1486593900000,"doc_count":7},{"key_as_string":"2017-02-08T22:46:00.000Z","key":1486593960000,"doc_count":13},{"key_as_string":"2017-02-08T22:47:00.000Z","key":1486594020000,"doc_count":11},{"key_as_string":"2017-02-08T22:48:00.000Z","key":1486594080000,"doc_count":18},{"key_as_string":"2017-02-08T22:49:00.000Z","key":1486594140000,"doc_count":4},{"key_as_string":"2017-02-08T22:50:00.000Z","key":1486594200000,"doc_count":5},{"key_as_string":"2017-02-08T22:51:00.000Z","key":1486594260000,"doc_count":16},{"key_as_string":"2017-02-08T22:52:00.000Z","key":1486594320000,"doc_count":8},{"key_as_string":"2017-02-08T22:53:00.000Z","key":1486594380000,"doc_count":11},{"key_as_string":"2017-02-08T22:54:00.000Z","key":1486594440000,"doc_count":10},{"key_as_string":"2017-02-08T22:55:00.000Z","key":1486594500000,"doc_count":10},{"key_as_string":"2017-02-08T22:56:00.000Z","key":1486594560000,"doc_count":15},{"key_as_string":"2017-02-08T22:57:00.000Z","key":1486594620000,"doc_count":7},{"key_as_string":"2017-02-08T22:58:00.000Z","key":1486594680000,"doc_count":14},{"key_as_string":"2017-02-08T22:59:00.000Z","key":1486594740000,"doc_count":14},{"key_as_string":"2017-02-08T23:00:00.000Z","key":1486594800000,"doc_count":6},{"key_as_string":"2017-02-08T23:01:00.000Z","key":1486594860000,"doc_count":9},{"key_as_string":"2017-02-08T23:02:00.000Z","key":1486594920000,"doc_count":14},{"key_as_string":"2017-02-08T23:03:00.000Z","key":1486594980000,"doc_count":9},{"key_as_string":"2017-02-08T23:04:00.000Z","key":1486595040000,"doc_count":11},{"key_as_string":"2017-02-08T23:05:00.000Z","key":1486595100000,"doc_count":14},{"key_as_string":"2017-02-08T23:06:00.000Z","key":1486595160000,"doc_count":6},{"key_as_string":"2017-02-08T23:07:00.000Z","key":1486595220000,"doc_count":10},{"key_as_string":"2017-02-08T23:08:00.000Z","key":1486595280000,"doc_count":9},{"key_as_string":"2017-02-08T23:09:00.000Z","key":1486595340000,"doc_count":5},{"key_as_string":"2017-02-08T23:10:00.000Z","key":1486595400000,"doc_count":12},{"key_as_string":"2017-02-08T23:11:00.000Z","key":1486595460000,"doc_count":11},{"key_as_string":"2017-02-08T23:12:00.000Z","key":1486595520000,"doc_count":8},{"key_as_string":"2017-02-08T23:13:00.000Z","key":1486595580000,"doc_count":10},{"key_as_string":"2017-02-08T23:14:00.000Z","key":1486595640000,"doc_count":14},{"key_as_string":"2017-02-08T23:15:00.000Z","key":1486595700000,"doc_count":6},{"key_as_string":"2017-02-08T23:16:00.000Z","key":1486595760000,"doc_count":9},{"key_as_string":"2017-02-08T23:17:00.000Z","key":1486595820000,"doc_count":12},{"key_as_string":"2017-02-08T23:18:00.000Z","key":1486595880000,"doc_count":12},{"key_as_string":"2017-02-08T23:19:00.000Z","key":1486595940000,"doc_count":5},{"key_as_string":"2017-02-08T23:20:00.000Z","key":1486596000000,"doc_count":12},{"key_as_string":"2017-02-08T23:21:00.000Z","key":1486596060000,"doc_count":10},{"key_as_string":"2017-02-08T23:22:00.000Z","key":1486596120000,"doc_count":13},{"key_as_string":"2017-02-08T23:23:00.000Z","key":1486596180000,"doc_count":10},{"key_as_string":"2017-02-08T23:24:00.000Z","key":1486596240000,"doc_count":8},{"key_as_string":"2017-02-08T23:25:00.000Z","key":1486596300000,"doc_count":11},{"key_as_string":"2017-02-08T23:26:00.000Z","key":1486596360000,"doc_count":11},{"key_as_string":"2017-02-08T23:27:00.000Z","key":1486596420000,"doc_count":5},{"key_as_string":"2017-02-08T23:28:00.000Z","key":1486596480000,"doc_count":9},{"key_as_string":"2017-02-08T23:29:00.000Z","key":1486596540000,"doc_count":12},{"key_as_string":"2017-02-08T23:30:00.000Z","key":1486596600000,"doc_count":7},{"key_as_string":"2017-02-08T23:31:00.000Z","key":1486596660000,"doc_count":14},{"key_as_string":"2017-02-08T23:32:00.000Z","key":1486596720000,"doc_count":12},{"key_as_string":"2017-02-08T23:33:00.000Z","key":1486596780000,"doc_count":12},{"key_as_string":"2017-02-08T23:34:00.000Z","key":1486596840000,"doc_count":18},{"key_as_string":"2017-02-08T23:35:00.000Z","key":1486596900000,"doc_count":6},{"key_as_string":"2017-02-08T23:36:00.000Z","key":1486596960000,"doc_count":7},{"key_as_string":"2017-02-08T23:37:00.000Z","key":1486597020000,"doc_count":12},{"key_as_string":"2017-02-08T23:38:00.000Z","key":1486597080000,"doc_count":11},{"key_as_string":"2017-02-08T23:39:00.000Z","key":1486597140000,"doc_count":7},{"key_as_string":"2017-02-08T23:40:00.000Z","key":1486597200000,"doc_count":12},{"key_as_string":"2017-02-08T23:41:00.000Z","key":1486597260000,"doc_count":16},{"key_as_string":"2017-02-08T23:42:00.000Z","key":1486597320000,"doc_count":8},{"key_as_string":"2017-02-08T23:43:00.000Z","key":1486597380000,"doc_count":11},{"key_as_string":"2017-02-08T23:44:00.000Z","key":1486597440000,"doc_count":8},{"key_as_string":"2017-02-08T23:45:00.000Z","key":1486597500000,"doc_count":11},{"key_as_string":"2017-02-08T23:46:00.000Z","key":1486597560000,"doc_count":11},{"key_as_string":"2017-02-08T23:47:00.000Z","key":1486597620000,"doc_count":9},{"key_as_string":"2017-02-08T23:48:00.000Z","key":1486597680000,"doc_count":7},{"key_as_string":"2017-02-08T23:49:00.000Z","key":1486597740000,"doc_count":11},{"key_as_string":"2017-02-08T23:50:00.000Z","key":1486597800000,"doc_count":11},{"key_as_string":"2017-02-08T23:51:00.000Z","key":1486597860000,"doc_count":8},{"key_as_string":"2017-02-08T23:52:00.000Z","key":1486597920000,"doc_count":11},{"key_as_string":"2017-02-08T23:53:00.000Z","key":1486597980000,"doc_count":10},{"key_as_string":"2017-02-08T23:54:00.000Z","key":1486598040000,"doc_count":11},{"key_as_string":"2017-02-08T23:55:00.000Z","key":1486598100000,"doc_count":9},{"key_as_string":"2017-02-08T23:56:00.000Z","key":1486598160000,"doc_count":6},{"key_as_string":"2017-02-08T23:57:00.000Z","key":1486598220000,"doc_count":13},{"key_as_string":"2017-02-08T23:58:00.000Z","key":1486598280000,"doc_count":10},{"key_as_string":"2017-02-08T23:59:00.000Z","key":1486598340000,"doc_count":9},{"key_as_string":"2017-02-09T00:00:00.000Z","key":1486598400000,"doc_count":14},{"key_as_string":"2017-02-09T00:01:00.000Z","key":1486598460000,"doc_count":7},{"key_as_string":"2017-02-09T00:02:00.000Z","key":1486598520000,"doc_count":11},{"key_as_string":"2017-02-09T00:03:00.000Z","key":1486598580000,"doc_count":14},{"key_as_string":"2017-02-09T00:04:00.000Z","key":1486598640000,"doc_count":7},{"key_as_string":"2017-02-09T00:05:00.000Z","key":1486598700000,"doc_count":15},{"key_as_string":"2017-02-09T00:06:00.000Z","key":1486598760000,"doc_count":4},{"key_as_string":"2017-02-09T00:07:00.000Z","key":1486598820000,"doc_count":14},{"key_as_string":"2017-02-09T00:08:00.000Z","key":1486598880000,"doc_count":11},{"key_as_string":"2017-02-09T00:09:00.000Z","key":1486598940000,"doc_count":8},{"key_as_string":"2017-02-09T00:10:00.000Z","key":1486599000000,"doc_count":9},{"key_as_string":"2017-02-09T00:11:00.000Z","key":1486599060000,"doc_count":13},{"key_as_string":"2017-02-09T00:12:00.000Z","key":1486599120000,"doc_count":8},{"key_as_string":"2017-02-09T00:13:00.000Z","key":1486599180000,"doc_count":9},{"key_as_string":"2017-02-09T00:14:00.000Z","key":1486599240000,"doc_count":14},{"key_as_string":"2017-02-09T00:15:00.000Z","key":1486599300000,"doc_count":13},{"key_as_string":"2017-02-09T00:16:00.000Z","key":1486599360000,"doc_count":8},{"key_as_string":"2017-02-09T00:17:00.000Z","key":1486599420000,"doc_count":6},{"key_as_string":"2017-02-09T00:18:00.000Z","key":1486599480000,"doc_count":14},{"key_as_string":"2017-02-09T00:19:00.000Z","key":1486599540000,"doc_count":15},{"key_as_string":"2017-02-09T00:20:00.000Z","key":1486599600000,"doc_count":9},{"key_as_string":"2017-02-09T00:21:00.000Z","key":1486599660000,"doc_count":6},{"key_as_string":"2017-02-09T00:22:00.000Z","key":1486599720000,"doc_count":9},{"key_as_string":"2017-02-09T00:23:00.000Z","key":1486599780000,"doc_count":15},{"key_as_string":"2017-02-09T00:24:00.000Z","key":1486599840000,"doc_count":10},{"key_as_string":"2017-02-09T00:25:00.000Z","key":1486599900000,"doc_count":9},{"key_as_string":"2017-02-09T00:26:00.000Z","key":1486599960000,"doc_count":11},{"key_as_string":"2017-02-09T00:27:00.000Z","key":1486600020000,"doc_count":10},{"key_as_string":"2017-02-09T00:28:00.000Z","key":1486600080000,"doc_count":11},{"key_as_string":"2017-02-09T00:29:00.000Z","key":1486600140000,"doc_count":8},{"key_as_string":"2017-02-09T00:30:00.000Z","key":1486600200000,"doc_count":5},{"key_as_string":"2017-02-09T00:31:00.000Z","key":1486600260000,"doc_count":15},{"key_as_string":"2017-02-09T00:32:00.000Z","key":1486600320000,"doc_count":5},{"key_as_string":"2017-02-09T00:33:00.000Z","key":1486600380000,"doc_count":12},{"key_as_string":"2017-02-09T00:34:00.000Z","key":1486600440000,"doc_count":11},{"key_as_string":"2017-02-09T00:35:00.000Z","key":1486600500000,"doc_count":8},{"key_as_string":"2017-02-09T00:36:00.000Z","key":1486600560000,"doc_count":10},{"key_as_string":"2017-02-09T00:37:00.000Z","key":1486600620000,"doc_count":11},{"key_as_string":"2017-02-09T00:38:00.000Z","key":1486600680000,"doc_count":11},{"key_as_string":"2017-02-09T00:39:00.000Z","key":1486600740000,"doc_count":7},{"key_as_string":"2017-02-09T00:40:00.000Z","key":1486600800000,"doc_count":8},{"key_as_string":"2017-02-09T00:41:00.000Z","key":1486600860000,"doc_count":10},{"key_as_string":"2017-02-09T00:42:00.000Z","key":1486600920000,"doc_count":11},{"key_as_string":"2017-02-09T00:43:00.000Z","key":1486600980000,"doc_count":10},{"key_as_string":"2017-02-09T00:44:00.000Z","key":1486601040000,"doc_count":9},{"key_as_string":"2017-02-09T00:45:00.000Z","key":1486601100000,"doc_count":10},{"key_as_string":"2017-02-09T00:46:00.000Z","key":1486601160000,"doc_count":9},{"key_as_string":"2017-02-09T00:47:00.000Z","key":1486601220000,"doc_count":9},{"key_as_string":"2017-02-09T00:48:00.000Z","key":1486601280000,"doc_count":11},{"key_as_string":"2017-02-09T00:49:00.000Z","key":1486601340000,"doc_count":6},{"key_as_string":"2017-02-09T00:50:00.000Z","key":1486601400000,"doc_count":13},{"key_as_string":"2017-02-09T00:51:00.000Z","key":1486601460000,"doc_count":8},{"key_as_string":"2017-02-09T00:52:00.000Z","key":1486601520000,"doc_count":9},{"key_as_string":"2017-02-09T00:53:00.000Z","key":1486601580000,"doc_count":12},{"key_as_string":"2017-02-09T00:54:00.000Z","key":1486601640000,"doc_count":11},{"key_as_string":"2017-02-09T00:55:00.000Z","key":1486601700000,"doc_count":7},{"key_as_string":"2017-02-09T00:56:00.000Z","key":1486601760000,"doc_count":16},{"key_as_string":"2017-02-09T00:57:00.000Z","key":1486601820000,"doc_count":11},{"key_as_string":"2017-02-09T00:58:00.000Z","key":1486601880000,"doc_count":7},{"key_as_string":"2017-02-09T00:59:00.000Z","key":1486601940000,"doc_count":9},{"key_as_string":"2017-02-09T01:00:00.000Z","key":1486602000000,"doc_count":14},{"key_as_string":"2017-02-09T01:01:00.000Z","key":1486602060000,"doc_count":9},{"key_as_string":"2017-02-09T01:02:00.000Z","key":1486602120000,"doc_count":15},{"key_as_string":"2017-02-09T01:03:00.000Z","key":1486602180000,"doc_count":10},{"key_as_string":"2017-02-09T01:04:00.000Z","key":1486602240000,"doc_count":14},{"key_as_string":"2017-02-09T01:05:00.000Z","key":1486602300000,"doc_count":8},{"key_as_string":"2017-02-09T01:06:00.000Z","key":1486602360000,"doc_count":13},{"key_as_string":"2017-02-09T01:07:00.000Z","key":1486602420000,"doc_count":10},{"key_as_string":"2017-02-09T01:08:00.000Z","key":1486602480000,"doc_count":7},{"key_as_string":"2017-02-09T01:09:00.000Z","key":1486602540000,"doc_count":15},{"key_as_string":"2017-02-09T01:10:00.000Z","key":1486602600000,"doc_count":10},{"key_as_string":"2017-02-09T01:11:00.000Z","key":1486602660000,"doc_count":7},{"key_as_string":"2017-02-09T01:12:00.000Z","key":1486602720000,"doc_count":14},{"key_as_string":"2017-02-09T01:13:00.000Z","key":1486602780000,"doc_count":11},{"key_as_string":"2017-02-09T01:14:00.000Z","key":1486602840000,"doc_count":10},{"key_as_string":"2017-02-09T01:15:00.000Z","key":1486602900000,"doc_count":12},{"key_as_string":"2017-02-09T01:16:00.000Z","key":1486602960000,"doc_count":10},{"key_as_string":"2017-02-09T01:17:00.000Z","key":1486603020000,"doc_count":9},{"key_as_string":"2017-02-09T01:18:00.000Z","key":1486603080000,"doc_count":12},{"key_as_string":"2017-02-09T01:19:00.000Z","key":1486603140000,"doc_count":6},{"key_as_string":"2017-02-09T01:20:00.000Z","key":1486603200000,"doc_count":12},{"key_as_string":"2017-02-09T01:21:00.000Z","key":1486603260000,"doc_count":7},{"key_as_string":"2017-02-09T01:22:00.000Z","key":1486603320000,"doc_count":11},{"key_as_string":"2017-02-09T01:23:00.000Z","key":1486603380000,"doc_count":6},{"key_as_string":"2017-02-09T01:24:00.000Z","key":1486603440000,"doc_count":14},{"key_as_string":"2017-02-09T01:25:00.000Z","key":1486603500000,"doc_count":6},{"key_as_string":"2017-02-09T01:26:00.000Z","key":1486603560000,"doc_count":9},{"key_as_string":"2017-02-09T01:27:00.000Z","key":1486603620000,"doc_count":14},{"key_as_string":"2017-02-09T01:28:00.000Z","key":1486603680000,"doc_count":9},{"key_as_string":"2017-02-09T01:29:00.000Z","key":1486603740000,"doc_count":13},{"key_as_string":"2017-02-09T01:30:00.000Z","key":1486603800000,"doc_count":9},{"key_as_string":"2017-02-09T01:31:00.000Z","key":1486603860000,"doc_count":11},{"key_as_string":"2017-02-09T01:32:00.000Z","key":1486603920000,"doc_count":13},{"key_as_string":"2017-02-09T01:33:00.000Z","key":1486603980000,"doc_count":6},{"key_as_string":"2017-02-09T01:34:00.000Z","key":1486604040000,"doc_count":13},{"key_as_string":"2017-02-09T01:35:00.000Z","key":1486604100000,"doc_count":7},{"key_as_string":"2017-02-09T01:36:00.000Z","key":1486604160000,"doc_count":11},{"key_as_string":"2017-02-09T01:37:00.000Z","key":1486604220000,"doc_count":10},{"key_as_string":"2017-02-09T01:38:00.000Z","key":1486604280000,"doc_count":6},{"key_as_string":"2017-02-09T01:39:00.000Z","key":1486604340000,"doc_count":14},{"key_as_string":"2017-02-09T01:40:00.000Z","key":1486604400000,"doc_count":5},{"key_as_string":"2017-02-09T01:41:00.000Z","key":1486604460000,"doc_count":8},{"key_as_string":"2017-02-09T01:42:00.000Z","key":1486604520000,"doc_count":12},{"key_as_string":"2017-02-09T01:43:00.000Z","key":1486604580000,"doc_count":16},{"key_as_string":"2017-02-09T01:44:00.000Z","key":1486604640000,"doc_count":8},{"key_as_string":"2017-02-09T01:45:00.000Z","key":1486604700000,"doc_count":5},{"key_as_string":"2017-02-09T01:46:00.000Z","key":1486604760000,"doc_count":13},{"key_as_string":"2017-02-09T01:47:00.000Z","key":1486604820000,"doc_count":7},{"key_as_string":"2017-02-09T01:48:00.000Z","key":1486604880000,"doc_count":8},{"key_as_string":"2017-02-09T01:49:00.000Z","key":1486604940000,"doc_count":17},{"key_as_string":"2017-02-09T01:50:00.000Z","key":1486605000000,"doc_count":7},{"key_as_string":"2017-02-09T01:51:00.000Z","key":1486605060000,"doc_count":10},{"key_as_string":"2017-02-09T01:52:00.000Z","key":1486605120000,"doc_count":13},{"key_as_string":"2017-02-09T01:53:00.000Z","key":1486605180000,"doc_count":15},{"key_as_string":"2017-02-09T01:54:00.000Z","key":1486605240000,"doc_count":12},{"key_as_string":"2017-02-09T01:55:00.000Z","key":1486605300000,"doc_count":6},{"key_as_string":"2017-02-09T01:56:00.000Z","key":1486605360000,"doc_count":14},{"key_as_string":"2017-02-09T01:57:00.000Z","key":1486605420000,"doc_count":13},{"key_as_string":"2017-02-09T01:58:00.000Z","key":1486605480000,"doc_count":12},{"key_as_string":"2017-02-09T01:59:00.000Z","key":1486605540000,"doc_count":11},{"key_as_string":"2017-02-09T02:00:00.000Z","key":1486605600000,"doc_count":12},{"key_as_string":"2017-02-09T02:01:00.000Z","key":1486605660000,"doc_count":15},{"key_as_string":"2017-02-09T02:02:00.000Z","key":1486605720000,"doc_count":6},{"key_as_string":"2017-02-09T02:03:00.000Z","key":1486605780000,"doc_count":15},{"key_as_string":"2017-02-09T02:04:00.000Z","key":1486605840000,"doc_count":4},{"key_as_string":"2017-02-09T02:05:00.000Z","key":1486605900000,"doc_count":14},{"key_as_string":"2017-02-09T02:06:00.000Z","key":1486605960000,"doc_count":15},{"key_as_string":"2017-02-09T02:07:00.000Z","key":1486606020000,"doc_count":8},{"key_as_string":"2017-02-09T02:08:00.000Z","key":1486606080000,"doc_count":9},{"key_as_string":"2017-02-09T02:09:00.000Z","key":1486606140000,"doc_count":13},{"key_as_string":"2017-02-09T02:10:00.000Z","key":1486606200000,"doc_count":8},{"key_as_string":"2017-02-09T02:11:00.000Z","key":1486606260000,"doc_count":12},{"key_as_string":"2017-02-09T02:12:00.000Z","key":1486606320000,"doc_count":12},{"key_as_string":"2017-02-09T02:13:00.000Z","key":1486606380000,"doc_count":16},{"key_as_string":"2017-02-09T02:14:00.000Z","key":1486606440000,"doc_count":9},{"key_as_string":"2017-02-09T02:15:00.000Z","key":1486606500000,"doc_count":10},{"key_as_string":"2017-02-09T02:16:00.000Z","key":1486606560000,"doc_count":12},{"key_as_string":"2017-02-09T02:17:00.000Z","key":1486606620000,"doc_count":11},{"key_as_string":"2017-02-09T02:18:00.000Z","key":1486606680000,"doc_count":10},{"key_as_string":"2017-02-09T02:19:00.000Z","key":1486606740000,"doc_count":10},{"key_as_string":"2017-02-09T02:20:00.000Z","key":1486606800000,"doc_count":9},{"key_as_string":"2017-02-09T02:21:00.000Z","key":1486606860000,"doc_count":16},{"key_as_string":"2017-02-09T02:22:00.000Z","key":1486606920000,"doc_count":11},{"key_as_string":"2017-02-09T02:23:00.000Z","key":1486606980000,"doc_count":10},{"key_as_string":"2017-02-09T02:24:00.000Z","key":1486607040000,"doc_count":10},{"key_as_string":"2017-02-09T02:25:00.000Z","key":1486607100000,"doc_count":11},{"key_as_string":"2017-02-09T02:26:00.000Z","key":1486607160000,"doc_count":7},{"key_as_string":"2017-02-09T02:27:00.000Z","key":1486607220000,"doc_count":12},{"key_as_string":"2017-02-09T02:28:00.000Z","key":1486607280000,"doc_count":9},{"key_as_string":"2017-02-09T02:29:00.000Z","key":1486607340000,"doc_count":6},{"key_as_string":"2017-02-09T02:30:00.000Z","key":1486607400000,"doc_count":10},{"key_as_string":"2017-02-09T02:31:00.000Z","key":1486607460000,"doc_count":8},{"key_as_string":"2017-02-09T02:32:00.000Z","key":1486607520000,"doc_count":14},{"key_as_string":"2017-02-09T02:33:00.000Z","key":1486607580000,"doc_count":9},{"key_as_string":"2017-02-09T02:34:00.000Z","key":1486607640000,"doc_count":10},{"key_as_string":"2017-02-09T02:35:00.000Z","key":1486607700000,"doc_count":9},{"key_as_string":"2017-02-09T02:36:00.000Z","key":1486607760000,"doc_count":11},{"key_as_string":"2017-02-09T02:37:00.000Z","key":1486607820000,"doc_count":11},{"key_as_string":"2017-02-09T02:38:00.000Z","key":1486607880000,"doc_count":6},{"key_as_string":"2017-02-09T02:39:00.000Z","key":1486607940000,"doc_count":20},{"key_as_string":"2017-02-09T02:40:00.000Z","key":1486608000000,"doc_count":11},{"key_as_string":"2017-02-09T02:41:00.000Z","key":1486608060000,"doc_count":11},{"key_as_string":"2017-02-09T02:42:00.000Z","key":1486608120000,"doc_count":11},{"key_as_string":"2017-02-09T02:43:00.000Z","key":1486608180000,"doc_count":5},{"key_as_string":"2017-02-09T02:44:00.000Z","key":1486608240000,"doc_count":10},{"key_as_string":"2017-02-09T02:45:00.000Z","key":1486608300000,"doc_count":10},{"key_as_string":"2017-02-09T02:46:00.000Z","key":1486608360000,"doc_count":14},{"key_as_string":"2017-02-09T02:47:00.000Z","key":1486608420000,"doc_count":10},{"key_as_string":"2017-02-09T02:48:00.000Z","key":1486608480000,"doc_count":13},{"key_as_string":"2017-02-09T02:49:00.000Z","key":1486608540000,"doc_count":9},{"key_as_string":"2017-02-09T02:50:00.000Z","key":1486608600000,"doc_count":10},{"key_as_string":"2017-02-09T02:51:00.000Z","key":1486608660000,"doc_count":9},{"key_as_string":"2017-02-09T02:52:00.000Z","key":1486608720000,"doc_count":13},{"key_as_string":"2017-02-09T02:53:00.000Z","key":1486608780000,"doc_count":16},{"key_as_string":"2017-02-09T02:54:00.000Z","key":1486608840000,"doc_count":5},{"key_as_string":"2017-02-09T02:55:00.000Z","key":1486608900000,"doc_count":8},{"key_as_string":"2017-02-09T02:56:00.000Z","key":1486608960000,"doc_count":13},{"key_as_string":"2017-02-09T02:57:00.000Z","key":1486609020000,"doc_count":10},{"key_as_string":"2017-02-09T02:58:00.000Z","key":1486609080000,"doc_count":11},{"key_as_string":"2017-02-09T02:59:00.000Z","key":1486609140000,"doc_count":9},{"key_as_string":"2017-02-09T03:00:00.000Z","key":1486609200000,"doc_count":14},{"key_as_string":"2017-02-09T03:01:00.000Z","key":1486609260000,"doc_count":7},{"key_as_string":"2017-02-09T03:02:00.000Z","key":1486609320000,"doc_count":9},{"key_as_string":"2017-02-09T03:03:00.000Z","key":1486609380000,"doc_count":11},{"key_as_string":"2017-02-09T03:04:00.000Z","key":1486609440000,"doc_count":21},{"key_as_string":"2017-02-09T03:05:00.000Z","key":1486609500000,"doc_count":12},{"key_as_string":"2017-02-09T03:06:00.000Z","key":1486609560000,"doc_count":14},{"key_as_string":"2017-02-09T03:07:00.000Z","key":1486609620000,"doc_count":9},{"key_as_string":"2017-02-09T03:08:00.000Z","key":1486609680000,"doc_count":12},{"key_as_string":"2017-02-09T03:09:00.000Z","key":1486609740000,"doc_count":12},{"key_as_string":"2017-02-09T03:10:00.000Z","key":1486609800000,"doc_count":10},{"key_as_string":"2017-02-09T03:11:00.000Z","key":1486609860000,"doc_count":7},{"key_as_string":"2017-02-09T03:12:00.000Z","key":1486609920000,"doc_count":11},{"key_as_string":"2017-02-09T03:13:00.000Z","key":1486609980000,"doc_count":9},{"key_as_string":"2017-02-09T03:14:00.000Z","key":1486610040000,"doc_count":5},{"key_as_string":"2017-02-09T03:15:00.000Z","key":1486610100000,"doc_count":14},{"key_as_string":"2017-02-09T03:16:00.000Z","key":1486610160000,"doc_count":7},{"key_as_string":"2017-02-09T03:17:00.000Z","key":1486610220000,"doc_count":11},{"key_as_string":"2017-02-09T03:18:00.000Z","key":1486610280000,"doc_count":14},{"key_as_string":"2017-02-09T03:19:00.000Z","key":1486610340000,"doc_count":12},{"key_as_string":"2017-02-09T03:20:00.000Z","key":1486610400000,"doc_count":7},{"key_as_string":"2017-02-09T03:21:00.000Z","key":1486610460000,"doc_count":15},{"key_as_string":"2017-02-09T03:22:00.000Z","key":1486610520000,"doc_count":12},{"key_as_string":"2017-02-09T03:23:00.000Z","key":1486610580000,"doc_count":10},{"key_as_string":"2017-02-09T03:24:00.000Z","key":1486610640000,"doc_count":6},{"key_as_string":"2017-02-09T03:25:00.000Z","key":1486610700000,"doc_count":11},{"key_as_string":"2017-02-09T03:26:00.000Z","key":1486610760000,"doc_count":10},{"key_as_string":"2017-02-09T03:27:00.000Z","key":1486610820000,"doc_count":11},{"key_as_string":"2017-02-09T03:28:00.000Z","key":1486610880000,"doc_count":12},{"key_as_string":"2017-02-09T03:29:00.000Z","key":1486610940000,"doc_count":11},{"key_as_string":"2017-02-09T03:30:00.000Z","key":1486611000000,"doc_count":15},{"key_as_string":"2017-02-09T03:31:00.000Z","key":1486611060000,"doc_count":10},{"key_as_string":"2017-02-09T03:32:00.000Z","key":1486611120000,"doc_count":14},{"key_as_string":"2017-02-09T03:33:00.000Z","key":1486611180000,"doc_count":10},{"key_as_string":"2017-02-09T03:34:00.000Z","key":1486611240000,"doc_count":10},{"key_as_string":"2017-02-09T03:35:00.000Z","key":1486611300000,"doc_count":15},{"key_as_string":"2017-02-09T03:36:00.000Z","key":1486611360000,"doc_count":8},{"key_as_string":"2017-02-09T03:37:00.000Z","key":1486611420000,"doc_count":8},{"key_as_string":"2017-02-09T03:38:00.000Z","key":1486611480000,"doc_count":15},{"key_as_string":"2017-02-09T03:39:00.000Z","key":1486611540000,"doc_count":7},{"key_as_string":"2017-02-09T03:40:00.000Z","key":1486611600000,"doc_count":15},{"key_as_string":"2017-02-09T03:41:00.000Z","key":1486611660000,"doc_count":7},{"key_as_string":"2017-02-09T03:42:00.000Z","key":1486611720000,"doc_count":14},{"key_as_string":"2017-02-09T03:43:00.000Z","key":1486611780000,"doc_count":8},{"key_as_string":"2017-02-09T03:44:00.000Z","key":1486611840000,"doc_count":9},{"key_as_string":"2017-02-09T03:45:00.000Z","key":1486611900000,"doc_count":11},{"key_as_string":"2017-02-09T03:46:00.000Z","key":1486611960000,"doc_count":15},{"key_as_string":"2017-02-09T03:47:00.000Z","key":1486612020000,"doc_count":6},{"key_as_string":"2017-02-09T03:48:00.000Z","key":1486612080000,"doc_count":9},{"key_as_string":"2017-02-09T03:49:00.000Z","key":1486612140000,"doc_count":13},{"key_as_string":"2017-02-09T03:50:00.000Z","key":1486612200000,"doc_count":9},{"key_as_string":"2017-02-09T03:51:00.000Z","key":1486612260000,"doc_count":11},{"key_as_string":"2017-02-09T03:52:00.000Z","key":1486612320000,"doc_count":12},{"key_as_string":"2017-02-09T03:53:00.000Z","key":1486612380000,"doc_count":13},{"key_as_string":"2017-02-09T03:54:00.000Z","key":1486612440000,"doc_count":7},{"key_as_string":"2017-02-09T03:55:00.000Z","key":1486612500000,"doc_count":14},{"key_as_string":"2017-02-09T03:56:00.000Z","key":1486612560000,"doc_count":10},{"key_as_string":"2017-02-09T03:57:00.000Z","key":1486612620000,"doc_count":8},{"key_as_string":"2017-02-09T03:58:00.000Z","key":1486612680000,"doc_count":6},{"key_as_string":"2017-02-09T03:59:00.000Z","key":1486612740000,"doc_count":11},{"key_as_string":"2017-02-09T04:00:00.000Z","key":1486612800000,"doc_count":8},{"key_as_string":"2017-02-09T04:01:00.000Z","key":1486612860000,"doc_count":12},{"key_as_string":"2017-02-09T04:02:00.000Z","key":1486612920000,"doc_count":13},{"key_as_string":"2017-02-09T04:03:00.000Z","key":1486612980000,"doc_count":10},{"key_as_string":"2017-02-09T04:04:00.000Z","key":1486613040000,"doc_count":12},{"key_as_string":"2017-02-09T04:05:00.000Z","key":1486613100000,"doc_count":9},{"key_as_string":"2017-02-09T04:06:00.000Z","key":1486613160000,"doc_count":16},{"key_as_string":"2017-02-09T04:07:00.000Z","key":1486613220000,"doc_count":12},{"key_as_string":"2017-02-09T04:08:00.000Z","key":1486613280000,"doc_count":16},{"key_as_string":"2017-02-09T04:09:00.000Z","key":1486613340000,"doc_count":10},{"key_as_string":"2017-02-09T04:10:00.000Z","key":1486613400000,"doc_count":9},{"key_as_string":"2017-02-09T04:11:00.000Z","key":1486613460000,"doc_count":9},{"key_as_string":"2017-02-09T04:12:00.000Z","key":1486613520000,"doc_count":11},{"key_as_string":"2017-02-09T04:13:00.000Z","key":1486613580000,"doc_count":8},{"key_as_string":"2017-02-09T04:14:00.000Z","key":1486613640000,"doc_count":11},{"key_as_string":"2017-02-09T04:15:00.000Z","key":1486613700000,"doc_count":7},{"key_as_string":"2017-02-09T04:16:00.000Z","key":1486613760000,"doc_count":9},{"key_as_string":"2017-02-09T04:17:00.000Z","key":1486613820000,"doc_count":14},{"key_as_string":"2017-02-09T04:18:00.000Z","key":1486613880000,"doc_count":12},{"key_as_string":"2017-02-09T04:19:00.000Z","key":1486613940000,"doc_count":12},{"key_as_string":"2017-02-09T04:20:00.000Z","key":1486614000000,"doc_count":11},{"key_as_string":"2017-02-09T04:21:00.000Z","key":1486614060000,"doc_count":15},{"key_as_string":"2017-02-09T04:22:00.000Z","key":1486614120000,"doc_count":15},{"key_as_string":"2017-02-09T04:23:00.000Z","key":1486614180000,"doc_count":14},{"key_as_string":"2017-02-09T04:24:00.000Z","key":1486614240000,"doc_count":10},{"key_as_string":"2017-02-09T04:25:00.000Z","key":1486614300000,"doc_count":8},{"key_as_string":"2017-02-09T04:26:00.000Z","key":1486614360000,"doc_count":8},{"key_as_string":"2017-02-09T04:27:00.000Z","key":1486614420000,"doc_count":14},{"key_as_string":"2017-02-09T04:28:00.000Z","key":1486614480000,"doc_count":9},{"key_as_string":"2017-02-09T04:29:00.000Z","key":1486614540000,"doc_count":16},{"key_as_string":"2017-02-09T04:30:00.000Z","key":1486614600000,"doc_count":11},{"key_as_string":"2017-02-09T04:31:00.000Z","key":1486614660000,"doc_count":13},{"key_as_string":"2017-02-09T04:32:00.000Z","key":1486614720000,"doc_count":9},{"key_as_string":"2017-02-09T04:33:00.000Z","key":1486614780000,"doc_count":16},{"key_as_string":"2017-02-09T04:34:00.000Z","key":1486614840000,"doc_count":11},{"key_as_string":"2017-02-09T04:35:00.000Z","key":1486614900000,"doc_count":9},{"key_as_string":"2017-02-09T04:36:00.000Z","key":1486614960000,"doc_count":11},{"key_as_string":"2017-02-09T04:37:00.000Z","key":1486615020000,"doc_count":14},{"key_as_string":"2017-02-09T04:38:00.000Z","key":1486615080000,"doc_count":14},{"key_as_string":"2017-02-09T04:39:00.000Z","key":1486615140000,"doc_count":10},{"key_as_string":"2017-02-09T04:40:00.000Z","key":1486615200000,"doc_count":7},{"key_as_string":"2017-02-09T04:41:00.000Z","key":1486615260000,"doc_count":13},{"key_as_string":"2017-02-09T04:42:00.000Z","key":1486615320000,"doc_count":10},{"key_as_string":"2017-02-09T04:43:00.000Z","key":1486615380000,"doc_count":17},{"key_as_string":"2017-02-09T04:44:00.000Z","key":1486615440000,"doc_count":12},{"key_as_string":"2017-02-09T04:45:00.000Z","key":1486615500000,"doc_count":9},{"key_as_string":"2017-02-09T04:46:00.000Z","key":1486615560000,"doc_count":7},{"key_as_string":"2017-02-09T04:47:00.000Z","key":1486615620000,"doc_count":14},{"key_as_string":"2017-02-09T04:48:00.000Z","key":1486615680000,"doc_count":11},{"key_as_string":"2017-02-09T04:49:00.000Z","key":1486615740000,"doc_count":15},{"key_as_string":"2017-02-09T04:50:00.000Z","key":1486615800000,"doc_count":12},{"key_as_string":"2017-02-09T04:51:00.000Z","key":1486615860000,"doc_count":11},{"key_as_string":"2017-02-09T04:52:00.000Z","key":1486615920000,"doc_count":10},{"key_as_string":"2017-02-09T04:53:00.000Z","key":1486615980000,"doc_count":10},{"key_as_string":"2017-02-09T04:54:00.000Z","key":1486616040000,"doc_count":12},{"key_as_string":"2017-02-09T04:55:00.000Z","key":1486616100000,"doc_count":10},{"key_as_string":"2017-02-09T04:56:00.000Z","key":1486616160000,"doc_count":13},{"key_as_string":"2017-02-09T04:57:00.000Z","key":1486616220000,"doc_count":10},{"key_as_string":"2017-02-09T04:58:00.000Z","key":1486616280000,"doc_count":12},{"key_as_string":"2017-02-09T04:59:00.000Z","key":1486616340000,"doc_count":13},{"key_as_string":"2017-02-09T05:00:00.000Z","key":1486616400000,"doc_count":11},{"key_as_string":"2017-02-09T05:01:00.000Z","key":1486616460000,"doc_count":14},{"key_as_string":"2017-02-09T05:02:00.000Z","key":1486616520000,"doc_count":10},{"key_as_string":"2017-02-09T05:03:00.000Z","key":1486616580000,"doc_count":13},{"key_as_string":"2017-02-09T05:04:00.000Z","key":1486616640000,"doc_count":11},{"key_as_string":"2017-02-09T05:05:00.000Z","key":1486616700000,"doc_count":11},{"key_as_string":"2017-02-09T05:06:00.000Z","key":1486616760000,"doc_count":12},{"key_as_string":"2017-02-09T05:07:00.000Z","key":1486616820000,"doc_count":21},{"key_as_string":"2017-02-09T05:08:00.000Z","key":1486616880000,"doc_count":7},{"key_as_string":"2017-02-09T05:09:00.000Z","key":1486616940000,"doc_count":14},{"key_as_string":"2017-02-09T05:10:00.000Z","key":1486617000000,"doc_count":9},{"key_as_string":"2017-02-09T05:11:00.000Z","key":1486617060000,"doc_count":7},{"key_as_string":"2017-02-09T05:12:00.000Z","key":1486617120000,"doc_count":15},{"key_as_string":"2017-02-09T05:13:00.000Z","key":1486617180000,"doc_count":12},{"key_as_string":"2017-02-09T05:14:00.000Z","key":1486617240000,"doc_count":15},{"key_as_string":"2017-02-09T05:15:00.000Z","key":1486617300000,"doc_count":12},{"key_as_string":"2017-02-09T05:16:00.000Z","key":1486617360000,"doc_count":15},{"key_as_string":"2017-02-09T05:17:00.000Z","key":1486617420000,"doc_count":11},{"key_as_string":"2017-02-09T05:18:00.000Z","key":1486617480000,"doc_count":16},{"key_as_string":"2017-02-09T05:19:00.000Z","key":1486617540000,"doc_count":8},{"key_as_string":"2017-02-09T05:20:00.000Z","key":1486617600000,"doc_count":12},{"key_as_string":"2017-02-09T05:21:00.000Z","key":1486617660000,"doc_count":12},{"key_as_string":"2017-02-09T05:22:00.000Z","key":1486617720000,"doc_count":11},{"key_as_string":"2017-02-09T05:23:00.000Z","key":1486617780000,"doc_count":10},{"key_as_string":"2017-02-09T05:24:00.000Z","key":1486617840000,"doc_count":15},{"key_as_string":"2017-02-09T05:25:00.000Z","key":1486617900000,"doc_count":8},{"key_as_string":"2017-02-09T05:26:00.000Z","key":1486617960000,"doc_count":13},{"key_as_string":"2017-02-09T05:27:00.000Z","key":1486618020000,"doc_count":14},{"key_as_string":"2017-02-09T05:28:00.000Z","key":1486618080000,"doc_count":7},{"key_as_string":"2017-02-09T05:29:00.000Z","key":1486618140000,"doc_count":9},{"key_as_string":"2017-02-09T05:30:00.000Z","key":1486618200000,"doc_count":10},{"key_as_string":"2017-02-09T05:31:00.000Z","key":1486618260000,"doc_count":11},{"key_as_string":"2017-02-09T05:32:00.000Z","key":1486618320000,"doc_count":8},{"key_as_string":"2017-02-09T05:33:00.000Z","key":1486618380000,"doc_count":12},{"key_as_string":"2017-02-09T05:34:00.000Z","key":1486618440000,"doc_count":13},{"key_as_string":"2017-02-09T05:35:00.000Z","key":1486618500000,"doc_count":10},{"key_as_string":"2017-02-09T05:36:00.000Z","key":1486618560000,"doc_count":13},{"key_as_string":"2017-02-09T05:37:00.000Z","key":1486618620000,"doc_count":5},{"key_as_string":"2017-02-09T05:38:00.000Z","key":1486618680000,"doc_count":13},{"key_as_string":"2017-02-09T05:39:00.000Z","key":1486618740000,"doc_count":5},{"key_as_string":"2017-02-09T05:40:00.000Z","key":1486618800000,"doc_count":13},{"key_as_string":"2017-02-09T05:41:00.000Z","key":1486618860000,"doc_count":12},{"key_as_string":"2017-02-09T05:42:00.000Z","key":1486618920000,"doc_count":21},{"key_as_string":"2017-02-09T05:43:00.000Z","key":1486618980000,"doc_count":6},{"key_as_string":"2017-02-09T05:44:00.000Z","key":1486619040000,"doc_count":16},{"key_as_string":"2017-02-09T05:45:00.000Z","key":1486619100000,"doc_count":11},{"key_as_string":"2017-02-09T05:46:00.000Z","key":1486619160000,"doc_count":10},{"key_as_string":"2017-02-09T05:47:00.000Z","key":1486619220000,"doc_count":10},{"key_as_string":"2017-02-09T05:48:00.000Z","key":1486619280000,"doc_count":12},{"key_as_string":"2017-02-09T05:49:00.000Z","key":1486619340000,"doc_count":11},{"key_as_string":"2017-02-09T05:50:00.000Z","key":1486619400000,"doc_count":13},{"key_as_string":"2017-02-09T05:51:00.000Z","key":1486619460000,"doc_count":12},{"key_as_string":"2017-02-09T05:52:00.000Z","key":1486619520000,"doc_count":6},{"key_as_string":"2017-02-09T05:53:00.000Z","key":1486619580000,"doc_count":13},{"key_as_string":"2017-02-09T05:54:00.000Z","key":1486619640000,"doc_count":11},{"key_as_string":"2017-02-09T05:55:00.000Z","key":1486619700000,"doc_count":13},{"key_as_string":"2017-02-09T05:56:00.000Z","key":1486619760000,"doc_count":13},{"key_as_string":"2017-02-09T05:57:00.000Z","key":1486619820000,"doc_count":13},{"key_as_string":"2017-02-09T05:58:00.000Z","key":1486619880000,"doc_count":9},{"key_as_string":"2017-02-09T05:59:00.000Z","key":1486619940000,"doc_count":12},{"key_as_string":"2017-02-09T06:00:00.000Z","key":1486620000000,"doc_count":12},{"key_as_string":"2017-02-09T06:01:00.000Z","key":1486620060000,"doc_count":17},{"key_as_string":"2017-02-09T06:02:00.000Z","key":1486620120000,"doc_count":14},{"key_as_string":"2017-02-09T06:03:00.000Z","key":1486620180000,"doc_count":7},{"key_as_string":"2017-02-09T06:04:00.000Z","key":1486620240000,"doc_count":9},{"key_as_string":"2017-02-09T06:05:00.000Z","key":1486620300000,"doc_count":15},{"key_as_string":"2017-02-09T06:06:00.000Z","key":1486620360000,"doc_count":11},{"key_as_string":"2017-02-09T06:07:00.000Z","key":1486620420000,"doc_count":10},{"key_as_string":"2017-02-09T06:08:00.000Z","key":1486620480000,"doc_count":11},{"key_as_string":"2017-02-09T06:09:00.000Z","key":1486620540000,"doc_count":8},{"key_as_string":"2017-02-09T06:10:00.000Z","key":1486620600000,"doc_count":12},{"key_as_string":"2017-02-09T06:11:00.000Z","key":1486620660000,"doc_count":16},{"key_as_string":"2017-02-09T06:12:00.000Z","key":1486620720000,"doc_count":11},{"key_as_string":"2017-02-09T06:13:00.000Z","key":1486620780000,"doc_count":8},{"key_as_string":"2017-02-09T06:14:00.000Z","key":1486620840000,"doc_count":11},{"key_as_string":"2017-02-09T06:15:00.000Z","key":1486620900000,"doc_count":16},{"key_as_string":"2017-02-09T06:16:00.000Z","key":1486620960000,"doc_count":12},{"key_as_string":"2017-02-09T06:17:00.000Z","key":1486621020000,"doc_count":10},{"key_as_string":"2017-02-09T06:18:00.000Z","key":1486621080000,"doc_count":10},{"key_as_string":"2017-02-09T06:19:00.000Z","key":1486621140000,"doc_count":18},{"key_as_string":"2017-02-09T06:20:00.000Z","key":1486621200000,"doc_count":8},{"key_as_string":"2017-02-09T06:21:00.000Z","key":1486621260000,"doc_count":13},{"key_as_string":"2017-02-09T06:22:00.000Z","key":1486621320000,"doc_count":10},{"key_as_string":"2017-02-09T06:23:00.000Z","key":1486621380000,"doc_count":15},{"key_as_string":"2017-02-09T06:24:00.000Z","key":1486621440000,"doc_count":13},{"key_as_string":"2017-02-09T06:25:00.000Z","key":1486621500000,"doc_count":10},{"key_as_string":"2017-02-09T06:26:00.000Z","key":1486621560000,"doc_count":11},{"key_as_string":"2017-02-09T06:27:00.000Z","key":1486621620000,"doc_count":10},{"key_as_string":"2017-02-09T06:28:00.000Z","key":1486621680000,"doc_count":11},{"key_as_string":"2017-02-09T06:29:00.000Z","key":1486621740000,"doc_count":14},{"key_as_string":"2017-02-09T06:30:00.000Z","key":1486621800000,"doc_count":7},{"key_as_string":"2017-02-09T06:31:00.000Z","key":1486621860000,"doc_count":13},{"key_as_string":"2017-02-09T06:32:00.000Z","key":1486621920000,"doc_count":10},{"key_as_string":"2017-02-09T06:33:00.000Z","key":1486621980000,"doc_count":7},{"key_as_string":"2017-02-09T06:34:00.000Z","key":1486622040000,"doc_count":15},{"key_as_string":"2017-02-09T06:35:00.000Z","key":1486622100000,"doc_count":10},{"key_as_string":"2017-02-09T06:36:00.000Z","key":1486622160000,"doc_count":14},{"key_as_string":"2017-02-09T06:37:00.000Z","key":1486622220000,"doc_count":10},{"key_as_string":"2017-02-09T06:38:00.000Z","key":1486622280000,"doc_count":8},{"key_as_string":"2017-02-09T06:39:00.000Z","key":1486622340000,"doc_count":7},{"key_as_string":"2017-02-09T06:40:00.000Z","key":1486622400000,"doc_count":13},{"key_as_string":"2017-02-09T06:41:00.000Z","key":1486622460000,"doc_count":13},{"key_as_string":"2017-02-09T06:42:00.000Z","key":1486622520000,"doc_count":7},{"key_as_string":"2017-02-09T06:43:00.000Z","key":1486622580000,"doc_count":12},{"key_as_string":"2017-02-09T06:44:00.000Z","key":1486622640000,"doc_count":11},{"key_as_string":"2017-02-09T06:45:00.000Z","key":1486622700000,"doc_count":10},{"key_as_string":"2017-02-09T06:46:00.000Z","key":1486622760000,"doc_count":16},{"key_as_string":"2017-02-09T06:47:00.000Z","key":1486622820000,"doc_count":17},{"key_as_string":"2017-02-09T06:48:00.000Z","key":1486622880000,"doc_count":13},{"key_as_string":"2017-02-09T06:49:00.000Z","key":1486622940000,"doc_count":11},{"key_as_string":"2017-02-09T06:50:00.000Z","key":1486623000000,"doc_count":8},{"key_as_string":"2017-02-09T06:51:00.000Z","key":1486623060000,"doc_count":6},{"key_as_string":"2017-02-09T06:52:00.000Z","key":1486623120000,"doc_count":17},{"key_as_string":"2017-02-09T06:53:00.000Z","key":1486623180000,"doc_count":11},{"key_as_string":"2017-02-09T06:54:00.000Z","key":1486623240000,"doc_count":11},{"key_as_string":"2017-02-09T06:55:00.000Z","key":1486623300000,"doc_count":14},{"key_as_string":"2017-02-09T06:56:00.000Z","key":1486623360000,"doc_count":10},{"key_as_string":"2017-02-09T06:57:00.000Z","key":1486623420000,"doc_count":14},{"key_as_string":"2017-02-09T06:58:00.000Z","key":1486623480000,"doc_count":8},{"key_as_string":"2017-02-09T06:59:00.000Z","key":1486623540000,"doc_count":10},{"key_as_string":"2017-02-09T07:00:00.000Z","key":1486623600000,"doc_count":11},{"key_as_string":"2017-02-09T07:01:00.000Z","key":1486623660000,"doc_count":16},{"key_as_string":"2017-02-09T07:02:00.000Z","key":1486623720000,"doc_count":17},{"key_as_string":"2017-02-09T07:03:00.000Z","key":1486623780000,"doc_count":16},{"key_as_string":"2017-02-09T07:04:00.000Z","key":1486623840000,"doc_count":15},{"key_as_string":"2017-02-09T07:05:00.000Z","key":1486623900000,"doc_count":12},{"key_as_string":"2017-02-09T07:06:00.000Z","key":1486623960000,"doc_count":9},{"key_as_string":"2017-02-09T07:07:00.000Z","key":1486624020000,"doc_count":11},{"key_as_string":"2017-02-09T07:08:00.000Z","key":1486624080000,"doc_count":11},{"key_as_string":"2017-02-09T07:09:00.000Z","key":1486624140000,"doc_count":20},{"key_as_string":"2017-02-09T07:10:00.000Z","key":1486624200000,"doc_count":9},{"key_as_string":"2017-02-09T07:11:00.000Z","key":1486624260000,"doc_count":17},{"key_as_string":"2017-02-09T07:12:00.000Z","key":1486624320000,"doc_count":13},{"key_as_string":"2017-02-09T07:13:00.000Z","key":1486624380000,"doc_count":18},{"key_as_string":"2017-02-09T07:14:00.000Z","key":1486624440000,"doc_count":8},{"key_as_string":"2017-02-09T07:15:00.000Z","key":1486624500000,"doc_count":14},{"key_as_string":"2017-02-09T07:16:00.000Z","key":1486624560000,"doc_count":9},{"key_as_string":"2017-02-09T07:17:00.000Z","key":1486624620000,"doc_count":15},{"key_as_string":"2017-02-09T07:18:00.000Z","key":1486624680000,"doc_count":15},{"key_as_string":"2017-02-09T07:19:00.000Z","key":1486624740000,"doc_count":15},{"key_as_string":"2017-02-09T07:20:00.000Z","key":1486624800000,"doc_count":17},{"key_as_string":"2017-02-09T07:21:00.000Z","key":1486624860000,"doc_count":13},{"key_as_string":"2017-02-09T07:22:00.000Z","key":1486624920000,"doc_count":10},{"key_as_string":"2017-02-09T07:23:00.000Z","key":1486624980000,"doc_count":10},{"key_as_string":"2017-02-09T07:24:00.000Z","key":1486625040000,"doc_count":9},{"key_as_string":"2017-02-09T07:25:00.000Z","key":1486625100000,"doc_count":16},{"key_as_string":"2017-02-09T07:26:00.000Z","key":1486625160000,"doc_count":14},{"key_as_string":"2017-02-09T07:27:00.000Z","key":1486625220000,"doc_count":13},{"key_as_string":"2017-02-09T07:28:00.000Z","key":1486625280000,"doc_count":14},{"key_as_string":"2017-02-09T07:29:00.000Z","key":1486625340000,"doc_count":14},{"key_as_string":"2017-02-09T07:30:00.000Z","key":1486625400000,"doc_count":14},{"key_as_string":"2017-02-09T07:31:00.000Z","key":1486625460000,"doc_count":16},{"key_as_string":"2017-02-09T07:32:00.000Z","key":1486625520000,"doc_count":13},{"key_as_string":"2017-02-09T07:33:00.000Z","key":1486625580000,"doc_count":12},{"key_as_string":"2017-02-09T07:34:00.000Z","key":1486625640000,"doc_count":15},{"key_as_string":"2017-02-09T07:35:00.000Z","key":1486625700000,"doc_count":13},{"key_as_string":"2017-02-09T07:36:00.000Z","key":1486625760000,"doc_count":15},{"key_as_string":"2017-02-09T07:37:00.000Z","key":1486625820000,"doc_count":12},{"key_as_string":"2017-02-09T07:38:00.000Z","key":1486625880000,"doc_count":9},{"key_as_string":"2017-02-09T07:39:00.000Z","key":1486625940000,"doc_count":20},{"key_as_string":"2017-02-09T07:40:00.000Z","key":1486626000000,"doc_count":13},{"key_as_string":"2017-02-09T07:41:00.000Z","key":1486626060000,"doc_count":17},{"key_as_string":"2017-02-09T07:42:00.000Z","key":1486626120000,"doc_count":10},{"key_as_string":"2017-02-09T07:43:00.000Z","key":1486626180000,"doc_count":15},{"key_as_string":"2017-02-09T07:44:00.000Z","key":1486626240000,"doc_count":11},{"key_as_string":"2017-02-09T07:45:00.000Z","key":1486626300000,"doc_count":11},{"key_as_string":"2017-02-09T07:46:00.000Z","key":1486626360000,"doc_count":13},{"key_as_string":"2017-02-09T07:47:00.000Z","key":1486626420000,"doc_count":14},{"key_as_string":"2017-02-09T07:48:00.000Z","key":1486626480000,"doc_count":13},{"key_as_string":"2017-02-09T07:49:00.000Z","key":1486626540000,"doc_count":13},{"key_as_string":"2017-02-09T07:50:00.000Z","key":1486626600000,"doc_count":12},{"key_as_string":"2017-02-09T07:51:00.000Z","key":1486626660000,"doc_count":15},{"key_as_string":"2017-02-09T07:52:00.000Z","key":1486626720000,"doc_count":15},{"key_as_string":"2017-02-09T07:53:00.000Z","key":1486626780000,"doc_count":15},{"key_as_string":"2017-02-09T07:54:00.000Z","key":1486626840000,"doc_count":12},{"key_as_string":"2017-02-09T07:55:00.000Z","key":1486626900000,"doc_count":11},{"key_as_string":"2017-02-09T07:56:00.000Z","key":1486626960000,"doc_count":14},{"key_as_string":"2017-02-09T07:57:00.000Z","key":1486627020000,"doc_count":8},{"key_as_string":"2017-02-09T07:58:00.000Z","key":1486627080000,"doc_count":17},{"key_as_string":"2017-02-09T07:59:00.000Z","key":1486627140000,"doc_count":13},{"key_as_string":"2017-02-09T08:00:00.000Z","key":1486627200000,"doc_count":13},{"key_as_string":"2017-02-09T08:01:00.000Z","key":1486627260000,"doc_count":12},{"key_as_string":"2017-02-09T08:02:00.000Z","key":1486627320000,"doc_count":19},{"key_as_string":"2017-02-09T08:03:00.000Z","key":1486627380000,"doc_count":15},{"key_as_string":"2017-02-09T08:04:00.000Z","key":1486627440000,"doc_count":9},{"key_as_string":"2017-02-09T08:05:00.000Z","key":1486627500000,"doc_count":14},{"key_as_string":"2017-02-09T08:06:00.000Z","key":1486627560000,"doc_count":14},{"key_as_string":"2017-02-09T08:07:00.000Z","key":1486627620000,"doc_count":13},{"key_as_string":"2017-02-09T08:08:00.000Z","key":1486627680000,"doc_count":10},{"key_as_string":"2017-02-09T08:09:00.000Z","key":1486627740000,"doc_count":14},{"key_as_string":"2017-02-09T08:10:00.000Z","key":1486627800000,"doc_count":11},{"key_as_string":"2017-02-09T08:11:00.000Z","key":1486627860000,"doc_count":12},{"key_as_string":"2017-02-09T08:12:00.000Z","key":1486627920000,"doc_count":15},{"key_as_string":"2017-02-09T08:13:00.000Z","key":1486627980000,"doc_count":13},{"key_as_string":"2017-02-09T08:14:00.000Z","key":1486628040000,"doc_count":12},{"key_as_string":"2017-02-09T08:15:00.000Z","key":1486628100000,"doc_count":11},{"key_as_string":"2017-02-09T08:16:00.000Z","key":1486628160000,"doc_count":21},{"key_as_string":"2017-02-09T08:17:00.000Z","key":1486628220000,"doc_count":16},{"key_as_string":"2017-02-09T08:18:00.000Z","key":1486628280000,"doc_count":16},{"key_as_string":"2017-02-09T08:19:00.000Z","key":1486628340000,"doc_count":11},{"key_as_string":"2017-02-09T08:20:00.000Z","key":1486628400000,"doc_count":14},{"key_as_string":"2017-02-09T08:21:00.000Z","key":1486628460000,"doc_count":14},{"key_as_string":"2017-02-09T08:22:00.000Z","key":1486628520000,"doc_count":13},{"key_as_string":"2017-02-09T08:23:00.000Z","key":1486628580000,"doc_count":13},{"key_as_string":"2017-02-09T08:24:00.000Z","key":1486628640000,"doc_count":12},{"key_as_string":"2017-02-09T08:25:00.000Z","key":1486628700000,"doc_count":16},{"key_as_string":"2017-02-09T08:26:00.000Z","key":1486628760000,"doc_count":12},{"key_as_string":"2017-02-09T08:27:00.000Z","key":1486628820000,"doc_count":10},{"key_as_string":"2017-02-09T08:28:00.000Z","key":1486628880000,"doc_count":15},{"key_as_string":"2017-02-09T08:29:00.000Z","key":1486628940000,"doc_count":17},{"key_as_string":"2017-02-09T08:30:00.000Z","key":1486629000000,"doc_count":12},{"key_as_string":"2017-02-09T08:31:00.000Z","key":1486629060000,"doc_count":13},{"key_as_string":"2017-02-09T08:32:00.000Z","key":1486629120000,"doc_count":14},{"key_as_string":"2017-02-09T08:33:00.000Z","key":1486629180000,"doc_count":14},{"key_as_string":"2017-02-09T08:34:00.000Z","key":1486629240000,"doc_count":14},{"key_as_string":"2017-02-09T08:35:00.000Z","key":1486629300000,"doc_count":12},{"key_as_string":"2017-02-09T08:36:00.000Z","key":1486629360000,"doc_count":14},{"key_as_string":"2017-02-09T08:37:00.000Z","key":1486629420000,"doc_count":14},{"key_as_string":"2017-02-09T08:38:00.000Z","key":1486629480000,"doc_count":12},{"key_as_string":"2017-02-09T08:39:00.000Z","key":1486629540000,"doc_count":7},{"key_as_string":"2017-02-09T08:40:00.000Z","key":1486629600000,"doc_count":18},{"key_as_string":"2017-02-09T08:41:00.000Z","key":1486629660000,"doc_count":10},{"key_as_string":"2017-02-09T08:42:00.000Z","key":1486629720000,"doc_count":13},{"key_as_string":"2017-02-09T08:43:00.000Z","key":1486629780000,"doc_count":14},{"key_as_string":"2017-02-09T08:44:00.000Z","key":1486629840000,"doc_count":16},{"key_as_string":"2017-02-09T08:45:00.000Z","key":1486629900000,"doc_count":14},{"key_as_string":"2017-02-09T08:46:00.000Z","key":1486629960000,"doc_count":13},{"key_as_string":"2017-02-09T08:47:00.000Z","key":1486630020000,"doc_count":15},{"key_as_string":"2017-02-09T08:48:00.000Z","key":1486630080000,"doc_count":14},{"key_as_string":"2017-02-09T08:49:00.000Z","key":1486630140000,"doc_count":16},{"key_as_string":"2017-02-09T08:50:00.000Z","key":1486630200000,"doc_count":11},{"key_as_string":"2017-02-09T08:51:00.000Z","key":1486630260000,"doc_count":15},{"key_as_string":"2017-02-09T08:52:00.000Z","key":1486630320000,"doc_count":15},{"key_as_string":"2017-02-09T08:53:00.000Z","key":1486630380000,"doc_count":12},{"key_as_string":"2017-02-09T08:54:00.000Z","key":1486630440000,"doc_count":13},{"key_as_string":"2017-02-09T08:55:00.000Z","key":1486630500000,"doc_count":11},{"key_as_string":"2017-02-09T08:56:00.000Z","key":1486630560000,"doc_count":7},{"key_as_string":"2017-02-09T08:57:00.000Z","key":1486630620000,"doc_count":14},{"key_as_string":"2017-02-09T08:58:00.000Z","key":1486630680000,"doc_count":12},{"key_as_string":"2017-02-09T08:59:00.000Z","key":1486630740000,"doc_count":8},{"key_as_string":"2017-02-09T09:00:00.000Z","key":1486630800000,"doc_count":15},{"key_as_string":"2017-02-09T09:01:00.000Z","key":1486630860000,"doc_count":13},{"key_as_string":"2017-02-09T09:02:00.000Z","key":1486630920000,"doc_count":17},{"key_as_string":"2017-02-09T09:03:00.000Z","key":1486630980000,"doc_count":10},{"key_as_string":"2017-02-09T09:04:00.000Z","key":1486631040000,"doc_count":12},{"key_as_string":"2017-02-09T09:05:00.000Z","key":1486631100000,"doc_count":12},{"key_as_string":"2017-02-09T09:06:00.000Z","key":1486631160000,"doc_count":10},{"key_as_string":"2017-02-09T09:07:00.000Z","key":1486631220000,"doc_count":13},{"key_as_string":"2017-02-09T09:08:00.000Z","key":1486631280000,"doc_count":15},{"key_as_string":"2017-02-09T09:09:00.000Z","key":1486631340000,"doc_count":10},{"key_as_string":"2017-02-09T09:10:00.000Z","key":1486631400000,"doc_count":17},{"key_as_string":"2017-02-09T09:11:00.000Z","key":1486631460000,"doc_count":14},{"key_as_string":"2017-02-09T09:12:00.000Z","key":1486631520000,"doc_count":10},{"key_as_string":"2017-02-09T09:13:00.000Z","key":1486631580000,"doc_count":15},{"key_as_string":"2017-02-09T09:14:00.000Z","key":1486631640000,"doc_count":14},{"key_as_string":"2017-02-09T09:15:00.000Z","key":1486631700000,"doc_count":16},{"key_as_string":"2017-02-09T09:16:00.000Z","key":1486631760000,"doc_count":11},{"key_as_string":"2017-02-09T09:17:00.000Z","key":1486631820000,"doc_count":16},{"key_as_string":"2017-02-09T09:18:00.000Z","key":1486631880000,"doc_count":11},{"key_as_string":"2017-02-09T09:19:00.000Z","key":1486631940000,"doc_count":11},{"key_as_string":"2017-02-09T09:20:00.000Z","key":1486632000000,"doc_count":18},{"key_as_string":"2017-02-09T09:21:00.000Z","key":1486632060000,"doc_count":10},{"key_as_string":"2017-02-09T09:22:00.000Z","key":1486632120000,"doc_count":10},{"key_as_string":"2017-02-09T09:23:00.000Z","key":1486632180000,"doc_count":14},{"key_as_string":"2017-02-09T09:24:00.000Z","key":1486632240000,"doc_count":18},{"key_as_string":"2017-02-09T09:25:00.000Z","key":1486632300000,"doc_count":12},{"key_as_string":"2017-02-09T09:26:00.000Z","key":1486632360000,"doc_count":16},{"key_as_string":"2017-02-09T09:27:00.000Z","key":1486632420000,"doc_count":15},{"key_as_string":"2017-02-09T09:28:00.000Z","key":1486632480000,"doc_count":25},{"key_as_string":"2017-02-09T09:29:00.000Z","key":1486632540000,"doc_count":13},{"key_as_string":"2017-02-09T09:30:00.000Z","key":1486632600000,"doc_count":11},{"key_as_string":"2017-02-09T09:31:00.000Z","key":1486632660000,"doc_count":12},{"key_as_string":"2017-02-09T09:32:00.000Z","key":1486632720000,"doc_count":16},{"key_as_string":"2017-02-09T09:33:00.000Z","key":1486632780000,"doc_count":10},{"key_as_string":"2017-02-09T09:34:00.000Z","key":1486632840000,"doc_count":14},{"key_as_string":"2017-02-09T09:35:00.000Z","key":1486632900000,"doc_count":14},{"key_as_string":"2017-02-09T09:36:00.000Z","key":1486632960000,"doc_count":16},{"key_as_string":"2017-02-09T09:37:00.000Z","key":1486633020000,"doc_count":19},{"key_as_string":"2017-02-09T09:38:00.000Z","key":1486633080000,"doc_count":15},{"key_as_string":"2017-02-09T09:39:00.000Z","key":1486633140000,"doc_count":13},{"key_as_string":"2017-02-09T09:40:00.000Z","key":1486633200000,"doc_count":10},{"key_as_string":"2017-02-09T09:41:00.000Z","key":1486633260000,"doc_count":16},{"key_as_string":"2017-02-09T09:42:00.000Z","key":1486633320000,"doc_count":20},{"key_as_string":"2017-02-09T09:43:00.000Z","key":1486633380000,"doc_count":7},{"key_as_string":"2017-02-09T09:44:00.000Z","key":1486633440000,"doc_count":16},{"key_as_string":"2017-02-09T09:45:00.000Z","key":1486633500000,"doc_count":10},{"key_as_string":"2017-02-09T09:46:00.000Z","key":1486633560000,"doc_count":15},{"key_as_string":"2017-02-09T09:47:00.000Z","key":1486633620000,"doc_count":10},{"key_as_string":"2017-02-09T09:48:00.000Z","key":1486633680000,"doc_count":19},{"key_as_string":"2017-02-09T09:49:00.000Z","key":1486633740000,"doc_count":11},{"key_as_string":"2017-02-09T09:50:00.000Z","key":1486633800000,"doc_count":12},{"key_as_string":"2017-02-09T09:51:00.000Z","key":1486633860000,"doc_count":13},{"key_as_string":"2017-02-09T09:52:00.000Z","key":1486633920000,"doc_count":10},{"key_as_string":"2017-02-09T09:53:00.000Z","key":1486633980000,"doc_count":15},{"key_as_string":"2017-02-09T09:54:00.000Z","key":1486634040000,"doc_count":11},{"key_as_string":"2017-02-09T09:55:00.000Z","key":1486634100000,"doc_count":15},{"key_as_string":"2017-02-09T09:56:00.000Z","key":1486634160000,"doc_count":14},{"key_as_string":"2017-02-09T09:57:00.000Z","key":1486634220000,"doc_count":11},{"key_as_string":"2017-02-09T09:58:00.000Z","key":1486634280000,"doc_count":9},{"key_as_string":"2017-02-09T09:59:00.000Z","key":1486634340000,"doc_count":18},{"key_as_string":"2017-02-09T10:00:00.000Z","key":1486634400000,"doc_count":13},{"key_as_string":"2017-02-09T10:01:00.000Z","key":1486634460000,"doc_count":13},{"key_as_string":"2017-02-09T10:02:00.000Z","key":1486634520000,"doc_count":11},{"key_as_string":"2017-02-09T10:03:00.000Z","key":1486634580000,"doc_count":15},{"key_as_string":"2017-02-09T10:04:00.000Z","key":1486634640000,"doc_count":11},{"key_as_string":"2017-02-09T10:05:00.000Z","key":1486634700000,"doc_count":16},{"key_as_string":"2017-02-09T10:06:00.000Z","key":1486634760000,"doc_count":15},{"key_as_string":"2017-02-09T10:07:00.000Z","key":1486634820000,"doc_count":9},{"key_as_string":"2017-02-09T10:08:00.000Z","key":1486634880000,"doc_count":14},{"key_as_string":"2017-02-09T10:09:00.000Z","key":1486634940000,"doc_count":16},{"key_as_string":"2017-02-09T10:10:00.000Z","key":1486635000000,"doc_count":16},{"key_as_string":"2017-02-09T10:11:00.000Z","key":1486635060000,"doc_count":13},{"key_as_string":"2017-02-09T10:12:00.000Z","key":1486635120000,"doc_count":16},{"key_as_string":"2017-02-09T10:13:00.000Z","key":1486635180000,"doc_count":12},{"key_as_string":"2017-02-09T10:14:00.000Z","key":1486635240000,"doc_count":12},{"key_as_string":"2017-02-09T10:15:00.000Z","key":1486635300000,"doc_count":14},{"key_as_string":"2017-02-09T10:16:00.000Z","key":1486635360000,"doc_count":12},{"key_as_string":"2017-02-09T10:17:00.000Z","key":1486635420000,"doc_count":18},{"key_as_string":"2017-02-09T10:18:00.000Z","key":1486635480000,"doc_count":19},{"key_as_string":"2017-02-09T10:19:00.000Z","key":1486635540000,"doc_count":8},{"key_as_string":"2017-02-09T10:20:00.000Z","key":1486635600000,"doc_count":12},{"key_as_string":"2017-02-09T10:21:00.000Z","key":1486635660000,"doc_count":13},{"key_as_string":"2017-02-09T10:22:00.000Z","key":1486635720000,"doc_count":16},{"key_as_string":"2017-02-09T10:23:00.000Z","key":1486635780000,"doc_count":13},{"key_as_string":"2017-02-09T10:24:00.000Z","key":1486635840000,"doc_count":9},{"key_as_string":"2017-02-09T10:25:00.000Z","key":1486635900000,"doc_count":17},{"key_as_string":"2017-02-09T10:26:00.000Z","key":1486635960000,"doc_count":16},{"key_as_string":"2017-02-09T10:27:00.000Z","key":1486636020000,"doc_count":14},{"key_as_string":"2017-02-09T10:28:00.000Z","key":1486636080000,"doc_count":11},{"key_as_string":"2017-02-09T10:29:00.000Z","key":1486636140000,"doc_count":12},{"key_as_string":"2017-02-09T10:30:00.000Z","key":1486636200000,"doc_count":19},{"key_as_string":"2017-02-09T10:31:00.000Z","key":1486636260000,"doc_count":16},{"key_as_string":"2017-02-09T10:32:00.000Z","key":1486636320000,"doc_count":12},{"key_as_string":"2017-02-09T10:33:00.000Z","key":1486636380000,"doc_count":14},{"key_as_string":"2017-02-09T10:34:00.000Z","key":1486636440000,"doc_count":11},{"key_as_string":"2017-02-09T10:35:00.000Z","key":1486636500000,"doc_count":15},{"key_as_string":"2017-02-09T10:36:00.000Z","key":1486636560000,"doc_count":18},{"key_as_string":"2017-02-09T10:37:00.000Z","key":1486636620000,"doc_count":15},{"key_as_string":"2017-02-09T10:38:00.000Z","key":1486636680000,"doc_count":12},{"key_as_string":"2017-02-09T10:39:00.000Z","key":1486636740000,"doc_count":14},{"key_as_string":"2017-02-09T10:40:00.000Z","key":1486636800000,"doc_count":14},{"key_as_string":"2017-02-09T10:41:00.000Z","key":1486636860000,"doc_count":17},{"key_as_string":"2017-02-09T10:42:00.000Z","key":1486636920000,"doc_count":13},{"key_as_string":"2017-02-09T10:43:00.000Z","key":1486636980000,"doc_count":9},{"key_as_string":"2017-02-09T10:44:00.000Z","key":1486637040000,"doc_count":11},{"key_as_string":"2017-02-09T10:45:00.000Z","key":1486637100000,"doc_count":14},{"key_as_string":"2017-02-09T10:46:00.000Z","key":1486637160000,"doc_count":15},{"key_as_string":"2017-02-09T10:47:00.000Z","key":1486637220000,"doc_count":14},{"key_as_string":"2017-02-09T10:48:00.000Z","key":1486637280000,"doc_count":13},{"key_as_string":"2017-02-09T10:49:00.000Z","key":1486637340000,"doc_count":16},{"key_as_string":"2017-02-09T10:50:00.000Z","key":1486637400000,"doc_count":11},{"key_as_string":"2017-02-09T10:51:00.000Z","key":1486637460000,"doc_count":15},{"key_as_string":"2017-02-09T10:52:00.000Z","key":1486637520000,"doc_count":16},{"key_as_string":"2017-02-09T10:53:00.000Z","key":1486637580000,"doc_count":14},{"key_as_string":"2017-02-09T10:54:00.000Z","key":1486637640000,"doc_count":13},{"key_as_string":"2017-02-09T10:55:00.000Z","key":1486637700000,"doc_count":12},{"key_as_string":"2017-02-09T10:56:00.000Z","key":1486637760000,"doc_count":14},{"key_as_string":"2017-02-09T10:57:00.000Z","key":1486637820000,"doc_count":14},{"key_as_string":"2017-02-09T10:58:00.000Z","key":1486637880000,"doc_count":15},{"key_as_string":"2017-02-09T10:59:00.000Z","key":1486637940000,"doc_count":11},{"key_as_string":"2017-02-09T11:00:00.000Z","key":1486638000000,"doc_count":14},{"key_as_string":"2017-02-09T11:01:00.000Z","key":1486638060000,"doc_count":12},{"key_as_string":"2017-02-09T11:02:00.000Z","key":1486638120000,"doc_count":17},{"key_as_string":"2017-02-09T11:03:00.000Z","key":1486638180000,"doc_count":10},{"key_as_string":"2017-02-09T11:04:00.000Z","key":1486638240000,"doc_count":14},{"key_as_string":"2017-02-09T11:05:00.000Z","key":1486638300000,"doc_count":13},{"key_as_string":"2017-02-09T11:06:00.000Z","key":1486638360000,"doc_count":11},{"key_as_string":"2017-02-09T11:07:00.000Z","key":1486638420000,"doc_count":14},{"key_as_string":"2017-02-09T11:08:00.000Z","key":1486638480000,"doc_count":8},{"key_as_string":"2017-02-09T11:09:00.000Z","key":1486638540000,"doc_count":20},{"key_as_string":"2017-02-09T11:10:00.000Z","key":1486638600000,"doc_count":15},{"key_as_string":"2017-02-09T11:11:00.000Z","key":1486638660000,"doc_count":17},{"key_as_string":"2017-02-09T11:12:00.000Z","key":1486638720000,"doc_count":14},{"key_as_string":"2017-02-09T11:13:00.000Z","key":1486638780000,"doc_count":12},{"key_as_string":"2017-02-09T11:14:00.000Z","key":1486638840000,"doc_count":12},{"key_as_string":"2017-02-09T11:15:00.000Z","key":1486638900000,"doc_count":18},{"key_as_string":"2017-02-09T11:16:00.000Z","key":1486638960000,"doc_count":17},{"key_as_string":"2017-02-09T11:17:00.000Z","key":1486639020000,"doc_count":16},{"key_as_string":"2017-02-09T11:18:00.000Z","key":1486639080000,"doc_count":11},{"key_as_string":"2017-02-09T11:19:00.000Z","key":1486639140000,"doc_count":14},{"key_as_string":"2017-02-09T11:20:00.000Z","key":1486639200000,"doc_count":17},{"key_as_string":"2017-02-09T11:21:00.000Z","key":1486639260000,"doc_count":16},{"key_as_string":"2017-02-09T11:22:00.000Z","key":1486639320000,"doc_count":13},{"key_as_string":"2017-02-09T11:23:00.000Z","key":1486639380000,"doc_count":15},{"key_as_string":"2017-02-09T11:24:00.000Z","key":1486639440000,"doc_count":11},{"key_as_string":"2017-02-09T11:25:00.000Z","key":1486639500000,"doc_count":12},{"key_as_string":"2017-02-09T11:26:00.000Z","key":1486639560000,"doc_count":20},{"key_as_string":"2017-02-09T11:27:00.000Z","key":1486639620000,"doc_count":13},{"key_as_string":"2017-02-09T11:28:00.000Z","key":1486639680000,"doc_count":15},{"key_as_string":"2017-02-09T11:29:00.000Z","key":1486639740000,"doc_count":19},{"key_as_string":"2017-02-09T11:30:00.000Z","key":1486639800000,"doc_count":12},{"key_as_string":"2017-02-09T11:31:00.000Z","key":1486639860000,"doc_count":12},{"key_as_string":"2017-02-09T11:32:00.000Z","key":1486639920000,"doc_count":13},{"key_as_string":"2017-02-09T11:33:00.000Z","key":1486639980000,"doc_count":15},{"key_as_string":"2017-02-09T11:34:00.000Z","key":1486640040000,"doc_count":14},{"key_as_string":"2017-02-09T11:35:00.000Z","key":1486640100000,"doc_count":16},{"key_as_string":"2017-02-09T11:36:00.000Z","key":1486640160000,"doc_count":18},{"key_as_string":"2017-02-09T11:37:00.000Z","key":1486640220000,"doc_count":13},{"key_as_string":"2017-02-09T11:38:00.000Z","key":1486640280000,"doc_count":11},{"key_as_string":"2017-02-09T11:39:00.000Z","key":1486640340000,"doc_count":18},{"key_as_string":"2017-02-09T11:40:00.000Z","key":1486640400000,"doc_count":10},{"key_as_string":"2017-02-09T11:41:00.000Z","key":1486640460000,"doc_count":16},{"key_as_string":"2017-02-09T11:42:00.000Z","key":1486640520000,"doc_count":16},{"key_as_string":"2017-02-09T11:43:00.000Z","key":1486640580000,"doc_count":10},{"key_as_string":"2017-02-09T11:44:00.000Z","key":1486640640000,"doc_count":8},{"key_as_string":"2017-02-09T11:45:00.000Z","key":1486640700000,"doc_count":17},{"key_as_string":"2017-02-09T11:46:00.000Z","key":1486640760000,"doc_count":15},{"key_as_string":"2017-02-09T11:47:00.000Z","key":1486640820000,"doc_count":11},{"key_as_string":"2017-02-09T11:48:00.000Z","key":1486640880000,"doc_count":14},{"key_as_string":"2017-02-09T11:49:00.000Z","key":1486640940000,"doc_count":16},{"key_as_string":"2017-02-09T11:50:00.000Z","key":1486641000000,"doc_count":15},{"key_as_string":"2017-02-09T11:51:00.000Z","key":1486641060000,"doc_count":16},{"key_as_string":"2017-02-09T11:52:00.000Z","key":1486641120000,"doc_count":19},{"key_as_string":"2017-02-09T11:53:00.000Z","key":1486641180000,"doc_count":14},{"key_as_string":"2017-02-09T11:54:00.000Z","key":1486641240000,"doc_count":11},{"key_as_string":"2017-02-09T11:55:00.000Z","key":1486641300000,"doc_count":7},{"key_as_string":"2017-02-09T11:56:00.000Z","key":1486641360000,"doc_count":15},{"key_as_string":"2017-02-09T11:57:00.000Z","key":1486641420000,"doc_count":10},{"key_as_string":"2017-02-09T11:58:00.000Z","key":1486641480000,"doc_count":11},{"key_as_string":"2017-02-09T11:59:00.000Z","key":1486641540000,"doc_count":16},{"key_as_string":"2017-02-09T12:00:00.000Z","key":1486641600000,"doc_count":18},{"key_as_string":"2017-02-09T12:01:00.000Z","key":1486641660000,"doc_count":11},{"key_as_string":"2017-02-09T12:02:00.000Z","key":1486641720000,"doc_count":16},{"key_as_string":"2017-02-09T12:03:00.000Z","key":1486641780000,"doc_count":12},{"key_as_string":"2017-02-09T12:04:00.000Z","key":1486641840000,"doc_count":14},{"key_as_string":"2017-02-09T12:05:00.000Z","key":1486641900000,"doc_count":13},{"key_as_string":"2017-02-09T12:06:00.000Z","key":1486641960000,"doc_count":11},{"key_as_string":"2017-02-09T12:07:00.000Z","key":1486642020000,"doc_count":19},{"key_as_string":"2017-02-09T12:08:00.000Z","key":1486642080000,"doc_count":19},{"key_as_string":"2017-02-09T12:09:00.000Z","key":1486642140000,"doc_count":9},{"key_as_string":"2017-02-09T12:10:00.000Z","key":1486642200000,"doc_count":16},{"key_as_string":"2017-02-09T12:11:00.000Z","key":1486642260000,"doc_count":14},{"key_as_string":"2017-02-09T12:12:00.000Z","key":1486642320000,"doc_count":12},{"key_as_string":"2017-02-09T12:13:00.000Z","key":1486642380000,"doc_count":19},{"key_as_string":"2017-02-09T12:14:00.000Z","key":1486642440000,"doc_count":16},{"key_as_string":"2017-02-09T12:15:00.000Z","key":1486642500000,"doc_count":15},{"key_as_string":"2017-02-09T12:16:00.000Z","key":1486642560000,"doc_count":13},{"key_as_string":"2017-02-09T12:17:00.000Z","key":1486642620000,"doc_count":10},{"key_as_string":"2017-02-09T12:18:00.000Z","key":1486642680000,"doc_count":13},{"key_as_string":"2017-02-09T12:19:00.000Z","key":1486642740000,"doc_count":13},{"key_as_string":"2017-02-09T12:20:00.000Z","key":1486642800000,"doc_count":11},{"key_as_string":"2017-02-09T12:21:00.000Z","key":1486642860000,"doc_count":19},{"key_as_string":"2017-02-09T12:22:00.000Z","key":1486642920000,"doc_count":13},{"key_as_string":"2017-02-09T12:23:00.000Z","key":1486642980000,"doc_count":17},{"key_as_string":"2017-02-09T12:24:00.000Z","key":1486643040000,"doc_count":10},{"key_as_string":"2017-02-09T12:25:00.000Z","key":1486643100000,"doc_count":12},{"key_as_string":"2017-02-09T12:26:00.000Z","key":1486643160000,"doc_count":13},{"key_as_string":"2017-02-09T12:27:00.000Z","key":1486643220000,"doc_count":12},{"key_as_string":"2017-02-09T12:28:00.000Z","key":1486643280000,"doc_count":16},{"key_as_string":"2017-02-09T12:29:00.000Z","key":1486643340000,"doc_count":15},{"key_as_string":"2017-02-09T12:30:00.000Z","key":1486643400000,"doc_count":14},{"key_as_string":"2017-02-09T12:31:00.000Z","key":1486643460000,"doc_count":18},{"key_as_string":"2017-02-09T12:32:00.000Z","key":1486643520000,"doc_count":10},{"key_as_string":"2017-02-09T12:33:00.000Z","key":1486643580000,"doc_count":13},{"key_as_string":"2017-02-09T12:34:00.000Z","key":1486643640000,"doc_count":9},{"key_as_string":"2017-02-09T12:35:00.000Z","key":1486643700000,"doc_count":16},{"key_as_string":"2017-02-09T12:36:00.000Z","key":1486643760000,"doc_count":12},{"key_as_string":"2017-02-09T12:37:00.000Z","key":1486643820000,"doc_count":13},{"key_as_string":"2017-02-09T12:38:00.000Z","key":1486643880000,"doc_count":16},{"key_as_string":"2017-02-09T12:39:00.000Z","key":1486643940000,"doc_count":9},{"key_as_string":"2017-02-09T12:40:00.000Z","key":1486644000000,"doc_count":12},{"key_as_string":"2017-02-09T12:41:00.000Z","key":1486644060000,"doc_count":11},{"key_as_string":"2017-02-09T12:42:00.000Z","key":1486644120000,"doc_count":16},{"key_as_string":"2017-02-09T12:43:00.000Z","key":1486644180000,"doc_count":13},{"key_as_string":"2017-02-09T12:44:00.000Z","key":1486644240000,"doc_count":15},{"key_as_string":"2017-02-09T12:45:00.000Z","key":1486644300000,"doc_count":13},{"key_as_string":"2017-02-09T12:46:00.000Z","key":1486644360000,"doc_count":15},{"key_as_string":"2017-02-09T12:47:00.000Z","key":1486644420000,"doc_count":13},{"key_as_string":"2017-02-09T12:48:00.000Z","key":1486644480000,"doc_count":9},{"key_as_string":"2017-02-09T12:49:00.000Z","key":1486644540000,"doc_count":18},{"key_as_string":"2017-02-09T12:50:00.000Z","key":1486644600000,"doc_count":13},{"key_as_string":"2017-02-09T12:51:00.000Z","key":1486644660000,"doc_count":13},{"key_as_string":"2017-02-09T12:52:00.000Z","key":1486644720000,"doc_count":13},{"key_as_string":"2017-02-09T12:53:00.000Z","key":1486644780000,"doc_count":11},{"key_as_string":"2017-02-09T12:54:00.000Z","key":1486644840000,"doc_count":19},{"key_as_string":"2017-02-09T12:55:00.000Z","key":1486644900000,"doc_count":12},{"key_as_string":"2017-02-09T12:56:00.000Z","key":1486644960000,"doc_count":15},{"key_as_string":"2017-02-09T12:57:00.000Z","key":1486645020000,"doc_count":14},{"key_as_string":"2017-02-09T12:58:00.000Z","key":1486645080000,"doc_count":17},{"key_as_string":"2017-02-09T12:59:00.000Z","key":1486645140000,"doc_count":12},{"key_as_string":"2017-02-09T13:00:00.000Z","key":1486645200000,"doc_count":17},{"key_as_string":"2017-02-09T13:01:00.000Z","key":1486645260000,"doc_count":13},{"key_as_string":"2017-02-09T13:02:00.000Z","key":1486645320000,"doc_count":12},{"key_as_string":"2017-02-09T13:03:00.000Z","key":1486645380000,"doc_count":11},{"key_as_string":"2017-02-09T13:04:00.000Z","key":1486645440000,"doc_count":14},{"key_as_string":"2017-02-09T13:05:00.000Z","key":1486645500000,"doc_count":18},{"key_as_string":"2017-02-09T13:06:00.000Z","key":1486645560000,"doc_count":10},{"key_as_string":"2017-02-09T13:07:00.000Z","key":1486645620000,"doc_count":14},{"key_as_string":"2017-02-09T13:08:00.000Z","key":1486645680000,"doc_count":15},{"key_as_string":"2017-02-09T13:09:00.000Z","key":1486645740000,"doc_count":11},{"key_as_string":"2017-02-09T13:10:00.000Z","key":1486645800000,"doc_count":15},{"key_as_string":"2017-02-09T13:11:00.000Z","key":1486645860000,"doc_count":14},{"key_as_string":"2017-02-09T13:12:00.000Z","key":1486645920000,"doc_count":9},{"key_as_string":"2017-02-09T13:13:00.000Z","key":1486645980000,"doc_count":16},{"key_as_string":"2017-02-09T13:14:00.000Z","key":1486646040000,"doc_count":16},{"key_as_string":"2017-02-09T13:15:00.000Z","key":1486646100000,"doc_count":13},{"key_as_string":"2017-02-09T13:16:00.000Z","key":1486646160000,"doc_count":9},{"key_as_string":"2017-02-09T13:17:00.000Z","key":1486646220000,"doc_count":11},{"key_as_string":"2017-02-09T13:18:00.000Z","key":1486646280000,"doc_count":14},{"key_as_string":"2017-02-09T13:19:00.000Z","key":1486646340000,"doc_count":15},{"key_as_string":"2017-02-09T13:20:00.000Z","key":1486646400000,"doc_count":16},{"key_as_string":"2017-02-09T13:21:00.000Z","key":1486646460000,"doc_count":9},{"key_as_string":"2017-02-09T13:22:00.000Z","key":1486646520000,"doc_count":16},{"key_as_string":"2017-02-09T13:23:00.000Z","key":1486646580000,"doc_count":13},{"key_as_string":"2017-02-09T13:24:00.000Z","key":1486646640000,"doc_count":11},{"key_as_string":"2017-02-09T13:25:00.000Z","key":1486646700000,"doc_count":13},{"key_as_string":"2017-02-09T13:26:00.000Z","key":1486646760000,"doc_count":10},{"key_as_string":"2017-02-09T13:27:00.000Z","key":1486646820000,"doc_count":18},{"key_as_string":"2017-02-09T13:28:00.000Z","key":1486646880000,"doc_count":13},{"key_as_string":"2017-02-09T13:29:00.000Z","key":1486646940000,"doc_count":13},{"key_as_string":"2017-02-09T13:30:00.000Z","key":1486647000000,"doc_count":18},{"key_as_string":"2017-02-09T13:31:00.000Z","key":1486647060000,"doc_count":10},{"key_as_string":"2017-02-09T13:32:00.000Z","key":1486647120000,"doc_count":15},{"key_as_string":"2017-02-09T13:33:00.000Z","key":1486647180000,"doc_count":15},{"key_as_string":"2017-02-09T13:34:00.000Z","key":1486647240000,"doc_count":18},{"key_as_string":"2017-02-09T13:35:00.000Z","key":1486647300000,"doc_count":15},{"key_as_string":"2017-02-09T13:36:00.000Z","key":1486647360000,"doc_count":20},{"key_as_string":"2017-02-09T13:37:00.000Z","key":1486647420000,"doc_count":13},{"key_as_string":"2017-02-09T13:38:00.000Z","key":1486647480000,"doc_count":15},{"key_as_string":"2017-02-09T13:39:00.000Z","key":1486647540000,"doc_count":15},{"key_as_string":"2017-02-09T13:40:00.000Z","key":1486647600000,"doc_count":16},{"key_as_string":"2017-02-09T13:41:00.000Z","key":1486647660000,"doc_count":7},{"key_as_string":"2017-02-09T13:42:00.000Z","key":1486647720000,"doc_count":17},{"key_as_string":"2017-02-09T13:43:00.000Z","key":1486647780000,"doc_count":12},{"key_as_string":"2017-02-09T13:44:00.000Z","key":1486647840000,"doc_count":15},{"key_as_string":"2017-02-09T13:45:00.000Z","key":1486647900000,"doc_count":16},{"key_as_string":"2017-02-09T13:46:00.000Z","key":1486647960000,"doc_count":14},{"key_as_string":"2017-02-09T13:47:00.000Z","key":1486648020000,"doc_count":18},{"key_as_string":"2017-02-09T13:48:00.000Z","key":1486648080000,"doc_count":17},{"key_as_string":"2017-02-09T13:49:00.000Z","key":1486648140000,"doc_count":16},{"key_as_string":"2017-02-09T13:50:00.000Z","key":1486648200000,"doc_count":9},{"key_as_string":"2017-02-09T13:51:00.000Z","key":1486648260000,"doc_count":14},{"key_as_string":"2017-02-09T13:52:00.000Z","key":1486648320000,"doc_count":14},{"key_as_string":"2017-02-09T13:53:00.000Z","key":1486648380000,"doc_count":20},{"key_as_string":"2017-02-09T13:54:00.000Z","key":1486648440000,"doc_count":9},{"key_as_string":"2017-02-09T13:55:00.000Z","key":1486648500000,"doc_count":12},{"key_as_string":"2017-02-09T13:56:00.000Z","key":1486648560000,"doc_count":15},{"key_as_string":"2017-02-09T13:57:00.000Z","key":1486648620000,"doc_count":11},{"key_as_string":"2017-02-09T13:58:00.000Z","key":1486648680000,"doc_count":15},{"key_as_string":"2017-02-09T13:59:00.000Z","key":1486648740000,"doc_count":13},{"key_as_string":"2017-02-09T14:00:00.000Z","key":1486648800000,"doc_count":10},{"key_as_string":"2017-02-09T14:01:00.000Z","key":1486648860000,"doc_count":14},{"key_as_string":"2017-02-09T14:02:00.000Z","key":1486648920000,"doc_count":14},{"key_as_string":"2017-02-09T14:03:00.000Z","key":1486648980000,"doc_count":16},{"key_as_string":"2017-02-09T14:04:00.000Z","key":1486649040000,"doc_count":12},{"key_as_string":"2017-02-09T14:05:00.000Z","key":1486649100000,"doc_count":9},{"key_as_string":"2017-02-09T14:06:00.000Z","key":1486649160000,"doc_count":11},{"key_as_string":"2017-02-09T14:07:00.000Z","key":1486649220000,"doc_count":15},{"key_as_string":"2017-02-09T14:08:00.000Z","key":1486649280000,"doc_count":12},{"key_as_string":"2017-02-09T14:09:00.000Z","key":1486649340000,"doc_count":19},{"key_as_string":"2017-02-09T14:10:00.000Z","key":1486649400000,"doc_count":14},{"key_as_string":"2017-02-09T14:11:00.000Z","key":1486649460000,"doc_count":17},{"key_as_string":"2017-02-09T14:12:00.000Z","key":1486649520000,"doc_count":7},{"key_as_string":"2017-02-09T14:13:00.000Z","key":1486649580000,"doc_count":20},{"key_as_string":"2017-02-09T14:14:00.000Z","key":1486649640000,"doc_count":9},{"key_as_string":"2017-02-09T14:15:00.000Z","key":1486649700000,"doc_count":17},{"key_as_string":"2017-02-09T14:16:00.000Z","key":1486649760000,"doc_count":16},{"key_as_string":"2017-02-09T14:17:00.000Z","key":1486649820000,"doc_count":15},{"key_as_string":"2017-02-09T14:18:00.000Z","key":1486649880000,"doc_count":15},{"key_as_string":"2017-02-09T14:19:00.000Z","key":1486649940000,"doc_count":12},{"key_as_string":"2017-02-09T14:20:00.000Z","key":1486650000000,"doc_count":8},{"key_as_string":"2017-02-09T14:21:00.000Z","key":1486650060000,"doc_count":17},{"key_as_string":"2017-02-09T14:22:00.000Z","key":1486650120000,"doc_count":10},{"key_as_string":"2017-02-09T14:23:00.000Z","key":1486650180000,"doc_count":11},{"key_as_string":"2017-02-09T14:24:00.000Z","key":1486650240000,"doc_count":11},{"key_as_string":"2017-02-09T14:25:00.000Z","key":1486650300000,"doc_count":14},{"key_as_string":"2017-02-09T14:26:00.000Z","key":1486650360000,"doc_count":16},{"key_as_string":"2017-02-09T14:27:00.000Z","key":1486650420000,"doc_count":12},{"key_as_string":"2017-02-09T14:28:00.000Z","key":1486650480000,"doc_count":14},{"key_as_string":"2017-02-09T14:29:00.000Z","key":1486650540000,"doc_count":11},{"key_as_string":"2017-02-09T14:30:00.000Z","key":1486650600000,"doc_count":13},{"key_as_string":"2017-02-09T14:31:00.000Z","key":1486650660000,"doc_count":13},{"key_as_string":"2017-02-09T14:32:00.000Z","key":1486650720000,"doc_count":19},{"key_as_string":"2017-02-09T14:33:00.000Z","key":1486650780000,"doc_count":13},{"key_as_string":"2017-02-09T14:34:00.000Z","key":1486650840000,"doc_count":15},{"key_as_string":"2017-02-09T14:35:00.000Z","key":1486650900000,"doc_count":11},{"key_as_string":"2017-02-09T14:36:00.000Z","key":1486650960000,"doc_count":15},{"key_as_string":"2017-02-09T14:37:00.000Z","key":1486651020000,"doc_count":12},{"key_as_string":"2017-02-09T14:38:00.000Z","key":1486651080000,"doc_count":11},{"key_as_string":"2017-02-09T14:39:00.000Z","key":1486651140000,"doc_count":18},{"key_as_string":"2017-02-09T14:40:00.000Z","key":1486651200000,"doc_count":9},{"key_as_string":"2017-02-09T14:41:00.000Z","key":1486651260000,"doc_count":15},{"key_as_string":"2017-02-09T14:42:00.000Z","key":1486651320000,"doc_count":12},{"key_as_string":"2017-02-09T14:43:00.000Z","key":1486651380000,"doc_count":15},{"key_as_string":"2017-02-09T14:44:00.000Z","key":1486651440000,"doc_count":12},{"key_as_string":"2017-02-09T14:45:00.000Z","key":1486651500000,"doc_count":13},{"key_as_string":"2017-02-09T14:46:00.000Z","key":1486651560000,"doc_count":12},{"key_as_string":"2017-02-09T14:47:00.000Z","key":1486651620000,"doc_count":14},{"key_as_string":"2017-02-09T14:48:00.000Z","key":1486651680000,"doc_count":13},{"key_as_string":"2017-02-09T14:49:00.000Z","key":1486651740000,"doc_count":17},{"key_as_string":"2017-02-09T14:50:00.000Z","key":1486651800000,"doc_count":9},{"key_as_string":"2017-02-09T14:51:00.000Z","key":1486651860000,"doc_count":12},{"key_as_string":"2017-02-09T14:52:00.000Z","key":1486651920000,"doc_count":16},{"key_as_string":"2017-02-09T14:53:00.000Z","key":1486651980000,"doc_count":19},{"key_as_string":"2017-02-09T14:54:00.000Z","key":1486652040000,"doc_count":15},{"key_as_string":"2017-02-09T14:55:00.000Z","key":1486652100000,"doc_count":11},{"key_as_string":"2017-02-09T14:56:00.000Z","key":1486652160000,"doc_count":15},{"key_as_string":"2017-02-09T14:57:00.000Z","key":1486652220000,"doc_count":11},{"key_as_string":"2017-02-09T14:58:00.000Z","key":1486652280000,"doc_count":12},{"key_as_string":"2017-02-09T14:59:00.000Z","key":1486652340000,"doc_count":11},{"key_as_string":"2017-02-09T15:00:00.000Z","key":1486652400000,"doc_count":13},{"key_as_string":"2017-02-09T15:01:00.000Z","key":1486652460000,"doc_count":10},{"key_as_string":"2017-02-09T15:02:00.000Z","key":1486652520000,"doc_count":17},{"key_as_string":"2017-02-09T15:03:00.000Z","key":1486652580000,"doc_count":12},{"key_as_string":"2017-02-09T15:04:00.000Z","key":1486652640000,"doc_count":13},{"key_as_string":"2017-02-09T15:05:00.000Z","key":1486652700000,"doc_count":13},{"key_as_string":"2017-02-09T15:06:00.000Z","key":1486652760000,"doc_count":15},{"key_as_string":"2017-02-09T15:07:00.000Z","key":1486652820000,"doc_count":12},{"key_as_string":"2017-02-09T15:08:00.000Z","key":1486652880000,"doc_count":12},{"key_as_string":"2017-02-09T15:09:00.000Z","key":1486652940000,"doc_count":13},{"key_as_string":"2017-02-09T15:10:00.000Z","key":1486653000000,"doc_count":15},{"key_as_string":"2017-02-09T15:11:00.000Z","key":1486653060000,"doc_count":20},{"key_as_string":"2017-02-09T15:12:00.000Z","key":1486653120000,"doc_count":10},{"key_as_string":"2017-02-09T15:13:00.000Z","key":1486653180000,"doc_count":12},{"key_as_string":"2017-02-09T15:14:00.000Z","key":1486653240000,"doc_count":8},{"key_as_string":"2017-02-09T15:15:00.000Z","key":1486653300000,"doc_count":18},{"key_as_string":"2017-02-09T15:16:00.000Z","key":1486653360000,"doc_count":16},{"key_as_string":"2017-02-09T15:17:00.000Z","key":1486653420000,"doc_count":13},{"key_as_string":"2017-02-09T15:18:00.000Z","key":1486653480000,"doc_count":14},{"key_as_string":"2017-02-09T15:19:00.000Z","key":1486653540000,"doc_count":14},{"key_as_string":"2017-02-09T15:20:00.000Z","key":1486653600000,"doc_count":14},{"key_as_string":"2017-02-09T15:21:00.000Z","key":1486653660000,"doc_count":11},{"key_as_string":"2017-02-09T15:22:00.000Z","key":1486653720000,"doc_count":13},{"key_as_string":"2017-02-09T15:23:00.000Z","key":1486653780000,"doc_count":11},{"key_as_string":"2017-02-09T15:24:00.000Z","key":1486653840000,"doc_count":14},{"key_as_string":"2017-02-09T15:25:00.000Z","key":1486653900000,"doc_count":15},{"key_as_string":"2017-02-09T15:26:00.000Z","key":1486653960000,"doc_count":12},{"key_as_string":"2017-02-09T15:27:00.000Z","key":1486654020000,"doc_count":9},{"key_as_string":"2017-02-09T15:28:00.000Z","key":1486654080000,"doc_count":17},{"key_as_string":"2017-02-09T15:29:00.000Z","key":1486654140000,"doc_count":10},{"key_as_string":"2017-02-09T15:30:00.000Z","key":1486654200000,"doc_count":15},{"key_as_string":"2017-02-09T15:31:00.000Z","key":1486654260000,"doc_count":13},{"key_as_string":"2017-02-09T15:32:00.000Z","key":1486654320000,"doc_count":10},{"key_as_string":"2017-02-09T15:33:00.000Z","key":1486654380000,"doc_count":19},{"key_as_string":"2017-02-09T15:34:00.000Z","key":1486654440000,"doc_count":13},{"key_as_string":"2017-02-09T15:35:00.000Z","key":1486654500000,"doc_count":13},{"key_as_string":"2017-02-09T15:36:00.000Z","key":1486654560000,"doc_count":11},{"key_as_string":"2017-02-09T15:37:00.000Z","key":1486654620000,"doc_count":10},{"key_as_string":"2017-02-09T15:38:00.000Z","key":1486654680000,"doc_count":14},{"key_as_string":"2017-02-09T15:39:00.000Z","key":1486654740000,"doc_count":19},{"key_as_string":"2017-02-09T15:40:00.000Z","key":1486654800000,"doc_count":14},{"key_as_string":"2017-02-09T15:41:00.000Z","key":1486654860000,"doc_count":14},{"key_as_string":"2017-02-09T15:42:00.000Z","key":1486654920000,"doc_count":11},{"key_as_string":"2017-02-09T15:43:00.000Z","key":1486654980000,"doc_count":12},{"key_as_string":"2017-02-09T15:44:00.000Z","key":1486655040000,"doc_count":10},{"key_as_string":"2017-02-09T15:45:00.000Z","key":1486655100000,"doc_count":14},{"key_as_string":"2017-02-09T15:46:00.000Z","key":1486655160000,"doc_count":13},{"key_as_string":"2017-02-09T15:47:00.000Z","key":1486655220000,"doc_count":10},{"key_as_string":"2017-02-09T15:48:00.000Z","key":1486655280000,"doc_count":12},{"key_as_string":"2017-02-09T15:49:00.000Z","key":1486655340000,"doc_count":18},{"key_as_string":"2017-02-09T15:50:00.000Z","key":1486655400000,"doc_count":18},{"key_as_string":"2017-02-09T15:51:00.000Z","key":1486655460000,"doc_count":12},{"key_as_string":"2017-02-09T15:52:00.000Z","key":1486655520000,"doc_count":12},{"key_as_string":"2017-02-09T15:53:00.000Z","key":1486655580000,"doc_count":18},{"key_as_string":"2017-02-09T15:54:00.000Z","key":1486655640000,"doc_count":9},{"key_as_string":"2017-02-09T15:55:00.000Z","key":1486655700000,"doc_count":11},{"key_as_string":"2017-02-09T15:56:00.000Z","key":1486655760000,"doc_count":14},{"key_as_string":"2017-02-09T15:57:00.000Z","key":1486655820000,"doc_count":14},{"key_as_string":"2017-02-09T15:58:00.000Z","key":1486655880000,"doc_count":15},{"key_as_string":"2017-02-09T15:59:00.000Z","key":1486655940000,"doc_count":13},{"key_as_string":"2017-02-09T16:00:00.000Z","key":1486656000000,"doc_count":13},{"key_as_string":"2017-02-09T16:01:00.000Z","key":1486656060000,"doc_count":11},{"key_as_string":"2017-02-09T16:02:00.000Z","key":1486656120000,"doc_count":10},{"key_as_string":"2017-02-09T16:03:00.000Z","key":1486656180000,"doc_count":18},{"key_as_string":"2017-02-09T16:04:00.000Z","key":1486656240000,"doc_count":6},{"key_as_string":"2017-02-09T16:05:00.000Z","key":1486656300000,"doc_count":14},{"key_as_string":"2017-02-09T16:06:00.000Z","key":1486656360000,"doc_count":16},{"key_as_string":"2017-02-09T16:07:00.000Z","key":1486656420000,"doc_count":12},{"key_as_string":"2017-02-09T16:08:00.000Z","key":1486656480000,"doc_count":15},{"key_as_string":"2017-02-09T16:09:00.000Z","key":1486656540000,"doc_count":13},{"key_as_string":"2017-02-09T16:10:00.000Z","key":1486656600000,"doc_count":13},{"key_as_string":"2017-02-09T16:11:00.000Z","key":1486656660000,"doc_count":7},{"key_as_string":"2017-02-09T16:12:00.000Z","key":1486656720000,"doc_count":16},{"key_as_string":"2017-02-09T16:13:00.000Z","key":1486656780000,"doc_count":10},{"key_as_string":"2017-02-09T16:14:00.000Z","key":1486656840000,"doc_count":52},{"key_as_string":"2017-02-09T16:15:00.000Z","key":1486656900000,"doc_count":130},{"key_as_string":"2017-02-09T16:16:00.000Z","key":1486656960000,"doc_count":17},{"key_as_string":"2017-02-09T16:17:00.000Z","key":1486657020000,"doc_count":13},{"key_as_string":"2017-02-09T16:18:00.000Z","key":1486657080000,"doc_count":10},{"key_as_string":"2017-02-09T16:19:00.000Z","key":1486657140000,"doc_count":9},{"key_as_string":"2017-02-09T16:20:00.000Z","key":1486657200000,"doc_count":9},{"key_as_string":"2017-02-09T16:21:00.000Z","key":1486657260000,"doc_count":17},{"key_as_string":"2017-02-09T16:22:00.000Z","key":1486657320000,"doc_count":13},{"key_as_string":"2017-02-09T16:23:00.000Z","key":1486657380000,"doc_count":10},{"key_as_string":"2017-02-09T16:24:00.000Z","key":1486657440000,"doc_count":11},{"key_as_string":"2017-02-09T16:25:00.000Z","key":1486657500000,"doc_count":9},{"key_as_string":"2017-02-09T16:26:00.000Z","key":1486657560000,"doc_count":12},{"key_as_string":"2017-02-09T16:27:00.000Z","key":1486657620000,"doc_count":15},{"key_as_string":"2017-02-09T16:28:00.000Z","key":1486657680000,"doc_count":8},{"key_as_string":"2017-02-09T16:29:00.000Z","key":1486657740000,"doc_count":12},{"key_as_string":"2017-02-09T16:30:00.000Z","key":1486657800000,"doc_count":12},{"key_as_string":"2017-02-09T16:31:00.000Z","key":1486657860000,"doc_count":15},{"key_as_string":"2017-02-09T16:32:00.000Z","key":1486657920000,"doc_count":10},{"key_as_string":"2017-02-09T16:33:00.000Z","key":1486657980000,"doc_count":13},{"key_as_string":"2017-02-09T16:34:00.000Z","key":1486658040000,"doc_count":13},{"key_as_string":"2017-02-09T16:35:00.000Z","key":1486658100000,"doc_count":14},{"key_as_string":"2017-02-09T16:36:00.000Z","key":1486658160000,"doc_count":14},{"key_as_string":"2017-02-09T16:37:00.000Z","key":1486658220000,"doc_count":11},{"key_as_string":"2017-02-09T16:38:00.000Z","key":1486658280000,"doc_count":12},{"key_as_string":"2017-02-09T16:39:00.000Z","key":1486658340000,"doc_count":11},{"key_as_string":"2017-02-09T16:40:00.000Z","key":1486658400000,"doc_count":10},{"key_as_string":"2017-02-09T16:41:00.000Z","key":1486658460000,"doc_count":10},{"key_as_string":"2017-02-09T16:42:00.000Z","key":1486658520000,"doc_count":13},{"key_as_string":"2017-02-09T16:43:00.000Z","key":1486658580000,"doc_count":16},{"key_as_string":"2017-02-09T16:44:00.000Z","key":1486658640000,"doc_count":14},{"key_as_string":"2017-02-09T16:45:00.000Z","key":1486658700000,"doc_count":15},{"key_as_string":"2017-02-09T16:46:00.000Z","key":1486658760000,"doc_count":13},{"key_as_string":"2017-02-09T16:47:00.000Z","key":1486658820000,"doc_count":14},{"key_as_string":"2017-02-09T16:48:00.000Z","key":1486658880000,"doc_count":9},{"key_as_string":"2017-02-09T16:49:00.000Z","key":1486658940000,"doc_count":15},{"key_as_string":"2017-02-09T16:50:00.000Z","key":1486659000000,"doc_count":13},{"key_as_string":"2017-02-09T16:51:00.000Z","key":1486659060000,"doc_count":15},{"key_as_string":"2017-02-09T16:52:00.000Z","key":1486659120000,"doc_count":13},{"key_as_string":"2017-02-09T16:53:00.000Z","key":1486659180000,"doc_count":16},{"key_as_string":"2017-02-09T16:54:00.000Z","key":1486659240000,"doc_count":11},{"key_as_string":"2017-02-09T16:55:00.000Z","key":1486659300000,"doc_count":18},{"key_as_string":"2017-02-09T16:56:00.000Z","key":1486659360000,"doc_count":9},{"key_as_string":"2017-02-09T16:57:00.000Z","key":1486659420000,"doc_count":19},{"key_as_string":"2017-02-09T16:58:00.000Z","key":1486659480000,"doc_count":8},{"key_as_string":"2017-02-09T16:59:00.000Z","key":1486659540000,"doc_count":12},{"key_as_string":"2017-02-09T17:00:00.000Z","key":1486659600000,"doc_count":17},{"key_as_string":"2017-02-09T17:01:00.000Z","key":1486659660000,"doc_count":14},{"key_as_string":"2017-02-09T17:02:00.000Z","key":1486659720000,"doc_count":13},{"key_as_string":"2017-02-09T17:03:00.000Z","key":1486659780000,"doc_count":9},{"key_as_string":"2017-02-09T17:04:00.000Z","key":1486659840000,"doc_count":14},{"key_as_string":"2017-02-09T17:05:00.000Z","key":1486659900000,"doc_count":15},{"key_as_string":"2017-02-09T17:06:00.000Z","key":1486659960000,"doc_count":13},{"key_as_string":"2017-02-09T17:07:00.000Z","key":1486660020000,"doc_count":9},{"key_as_string":"2017-02-09T17:08:00.000Z","key":1486660080000,"doc_count":8},{"key_as_string":"2017-02-09T17:09:00.000Z","key":1486660140000,"doc_count":11},{"key_as_string":"2017-02-09T17:10:00.000Z","key":1486660200000,"doc_count":11},{"key_as_string":"2017-02-09T17:11:00.000Z","key":1486660260000,"doc_count":12},{"key_as_string":"2017-02-09T17:12:00.000Z","key":1486660320000,"doc_count":12},{"key_as_string":"2017-02-09T17:13:00.000Z","key":1486660380000,"doc_count":9},{"key_as_string":"2017-02-09T17:14:00.000Z","key":1486660440000,"doc_count":15},{"key_as_string":"2017-02-09T17:15:00.000Z","key":1486660500000,"doc_count":11},{"key_as_string":"2017-02-09T17:16:00.000Z","key":1486660560000,"doc_count":11},{"key_as_string":"2017-02-09T17:17:00.000Z","key":1486660620000,"doc_count":11},{"key_as_string":"2017-02-09T17:18:00.000Z","key":1486660680000,"doc_count":11},{"key_as_string":"2017-02-09T17:19:00.000Z","key":1486660740000,"doc_count":15},{"key_as_string":"2017-02-09T17:20:00.000Z","key":1486660800000,"doc_count":7},{"key_as_string":"2017-02-09T17:21:00.000Z","key":1486660860000,"doc_count":15},{"key_as_string":"2017-02-09T17:22:00.000Z","key":1486660920000,"doc_count":13},{"key_as_string":"2017-02-09T17:23:00.000Z","key":1486660980000,"doc_count":10},{"key_as_string":"2017-02-09T17:24:00.000Z","key":1486661040000,"doc_count":18},{"key_as_string":"2017-02-09T17:25:00.000Z","key":1486661100000,"doc_count":9},{"key_as_string":"2017-02-09T17:26:00.000Z","key":1486661160000,"doc_count":12},{"key_as_string":"2017-02-09T17:27:00.000Z","key":1486661220000,"doc_count":8},{"key_as_string":"2017-02-09T17:28:00.000Z","key":1486661280000,"doc_count":11},{"key_as_string":"2017-02-09T17:29:00.000Z","key":1486661340000,"doc_count":10},{"key_as_string":"2017-02-09T17:30:00.000Z","key":1486661400000,"doc_count":19},{"key_as_string":"2017-02-09T17:31:00.000Z","key":1486661460000,"doc_count":10},{"key_as_string":"2017-02-09T17:32:00.000Z","key":1486661520000,"doc_count":11},{"key_as_string":"2017-02-09T17:33:00.000Z","key":1486661580000,"doc_count":12},{"key_as_string":"2017-02-09T17:34:00.000Z","key":1486661640000,"doc_count":12},{"key_as_string":"2017-02-09T17:35:00.000Z","key":1486661700000,"doc_count":15},{"key_as_string":"2017-02-09T17:36:00.000Z","key":1486661760000,"doc_count":11},{"key_as_string":"2017-02-09T17:37:00.000Z","key":1486661820000,"doc_count":12},{"key_as_string":"2017-02-09T17:38:00.000Z","key":1486661880000,"doc_count":12},{"key_as_string":"2017-02-09T17:39:00.000Z","key":1486661940000,"doc_count":14},{"key_as_string":"2017-02-09T17:40:00.000Z","key":1486662000000,"doc_count":10},{"key_as_string":"2017-02-09T17:41:00.000Z","key":1486662060000,"doc_count":13},{"key_as_string":"2017-02-09T17:42:00.000Z","key":1486662120000,"doc_count":11},{"key_as_string":"2017-02-09T17:43:00.000Z","key":1486662180000,"doc_count":16},{"key_as_string":"2017-02-09T17:44:00.000Z","key":1486662240000,"doc_count":13},{"key_as_string":"2017-02-09T17:45:00.000Z","key":1486662300000,"doc_count":7},{"key_as_string":"2017-02-09T17:46:00.000Z","key":1486662360000,"doc_count":15},{"key_as_string":"2017-02-09T17:47:00.000Z","key":1486662420000,"doc_count":11},{"key_as_string":"2017-02-09T17:48:00.000Z","key":1486662480000,"doc_count":10},{"key_as_string":"2017-02-09T17:49:00.000Z","key":1486662540000,"doc_count":13},{"key_as_string":"2017-02-09T17:50:00.000Z","key":1486662600000,"doc_count":11},{"key_as_string":"2017-02-09T17:51:00.000Z","key":1486662660000,"doc_count":19},{"key_as_string":"2017-02-09T17:52:00.000Z","key":1486662720000,"doc_count":8},{"key_as_string":"2017-02-09T17:53:00.000Z","key":1486662780000,"doc_count":12},{"key_as_string":"2017-02-09T17:54:00.000Z","key":1486662840000,"doc_count":15},{"key_as_string":"2017-02-09T17:55:00.000Z","key":1486662900000,"doc_count":9},{"key_as_string":"2017-02-09T17:56:00.000Z","key":1486662960000,"doc_count":11},{"key_as_string":"2017-02-09T17:57:00.000Z","key":1486663020000,"doc_count":14},{"key_as_string":"2017-02-09T17:58:00.000Z","key":1486663080000,"doc_count":11},{"key_as_string":"2017-02-09T17:59:00.000Z","key":1486663140000,"doc_count":18},{"key_as_string":"2017-02-09T18:00:00.000Z","key":1486663200000,"doc_count":14},{"key_as_string":"2017-02-09T18:01:00.000Z","key":1486663260000,"doc_count":11},{"key_as_string":"2017-02-09T18:02:00.000Z","key":1486663320000,"doc_count":11},{"key_as_string":"2017-02-09T18:03:00.000Z","key":1486663380000,"doc_count":13},{"key_as_string":"2017-02-09T18:04:00.000Z","key":1486663440000,"doc_count":15},{"key_as_string":"2017-02-09T18:05:00.000Z","key":1486663500000,"doc_count":10},{"key_as_string":"2017-02-09T18:06:00.000Z","key":1486663560000,"doc_count":17},{"key_as_string":"2017-02-09T18:07:00.000Z","key":1486663620000,"doc_count":14},{"key_as_string":"2017-02-09T18:08:00.000Z","key":1486663680000,"doc_count":11},{"key_as_string":"2017-02-09T18:09:00.000Z","key":1486663740000,"doc_count":15},{"key_as_string":"2017-02-09T18:10:00.000Z","key":1486663800000,"doc_count":12},{"key_as_string":"2017-02-09T18:11:00.000Z","key":1486663860000,"doc_count":7},{"key_as_string":"2017-02-09T18:12:00.000Z","key":1486663920000,"doc_count":17},{"key_as_string":"2017-02-09T18:13:00.000Z","key":1486663980000,"doc_count":14},{"key_as_string":"2017-02-09T18:14:00.000Z","key":1486664040000,"doc_count":10},{"key_as_string":"2017-02-09T18:15:00.000Z","key":1486664100000,"doc_count":13},{"key_as_string":"2017-02-09T18:16:00.000Z","key":1486664160000,"doc_count":18},{"key_as_string":"2017-02-09T18:17:00.000Z","key":1486664220000,"doc_count":7},{"key_as_string":"2017-02-09T18:18:00.000Z","key":1486664280000,"doc_count":17},{"key_as_string":"2017-02-09T18:19:00.000Z","key":1486664340000,"doc_count":10},{"key_as_string":"2017-02-09T18:20:00.000Z","key":1486664400000,"doc_count":7},{"key_as_string":"2017-02-09T18:21:00.000Z","key":1486664460000,"doc_count":12},{"key_as_string":"2017-02-09T18:22:00.000Z","key":1486664520000,"doc_count":14},{"key_as_string":"2017-02-09T18:23:00.000Z","key":1486664580000,"doc_count":14},{"key_as_string":"2017-02-09T18:24:00.000Z","key":1486664640000,"doc_count":11},{"key_as_string":"2017-02-09T18:25:00.000Z","key":1486664700000,"doc_count":13},{"key_as_string":"2017-02-09T18:26:00.000Z","key":1486664760000,"doc_count":15},{"key_as_string":"2017-02-09T18:27:00.000Z","key":1486664820000,"doc_count":6},{"key_as_string":"2017-02-09T18:28:00.000Z","key":1486664880000,"doc_count":12},{"key_as_string":"2017-02-09T18:29:00.000Z","key":1486664940000,"doc_count":15},{"key_as_string":"2017-02-09T18:30:00.000Z","key":1486665000000,"doc_count":9},{"key_as_string":"2017-02-09T18:31:00.000Z","key":1486665060000,"doc_count":13},{"key_as_string":"2017-02-09T18:32:00.000Z","key":1486665120000,"doc_count":13},{"key_as_string":"2017-02-09T18:33:00.000Z","key":1486665180000,"doc_count":14},{"key_as_string":"2017-02-09T18:34:00.000Z","key":1486665240000,"doc_count":8},{"key_as_string":"2017-02-09T18:35:00.000Z","key":1486665300000,"doc_count":9},{"key_as_string":"2017-02-09T18:36:00.000Z","key":1486665360000,"doc_count":11},{"key_as_string":"2017-02-09T18:37:00.000Z","key":1486665420000,"doc_count":12},{"key_as_string":"2017-02-09T18:38:00.000Z","key":1486665480000,"doc_count":11},{"key_as_string":"2017-02-09T18:39:00.000Z","key":1486665540000,"doc_count":12},{"key_as_string":"2017-02-09T18:40:00.000Z","key":1486665600000,"doc_count":16},{"key_as_string":"2017-02-09T18:41:00.000Z","key":1486665660000,"doc_count":7},{"key_as_string":"2017-02-09T18:42:00.000Z","key":1486665720000,"doc_count":13},{"key_as_string":"2017-02-09T18:43:00.000Z","key":1486665780000,"doc_count":13},{"key_as_string":"2017-02-09T18:44:00.000Z","key":1486665840000,"doc_count":14},{"key_as_string":"2017-02-09T18:45:00.000Z","key":1486665900000,"doc_count":7},{"key_as_string":"2017-02-09T18:46:00.000Z","key":1486665960000,"doc_count":9},{"key_as_string":"2017-02-09T18:47:00.000Z","key":1486666020000,"doc_count":11},{"key_as_string":"2017-02-09T18:48:00.000Z","key":1486666080000,"doc_count":11},{"key_as_string":"2017-02-09T18:49:00.000Z","key":1486666140000,"doc_count":11},{"key_as_string":"2017-02-09T18:50:00.000Z","key":1486666200000,"doc_count":8},{"key_as_string":"2017-02-09T18:51:00.000Z","key":1486666260000,"doc_count":15},{"key_as_string":"2017-02-09T18:52:00.000Z","key":1486666320000,"doc_count":8},{"key_as_string":"2017-02-09T18:53:00.000Z","key":1486666380000,"doc_count":13},{"key_as_string":"2017-02-09T18:54:00.000Z","key":1486666440000,"doc_count":13},{"key_as_string":"2017-02-09T18:55:00.000Z","key":1486666500000,"doc_count":11},{"key_as_string":"2017-02-09T18:56:00.000Z","key":1486666560000,"doc_count":16},{"key_as_string":"2017-02-09T18:57:00.000Z","key":1486666620000,"doc_count":6},{"key_as_string":"2017-02-09T18:58:00.000Z","key":1486666680000,"doc_count":12},{"key_as_string":"2017-02-09T18:59:00.000Z","key":1486666740000,"doc_count":12},{"key_as_string":"2017-02-09T19:00:00.000Z","key":1486666800000,"doc_count":9},{"key_as_string":"2017-02-09T19:01:00.000Z","key":1486666860000,"doc_count":12},{"key_as_string":"2017-02-09T19:02:00.000Z","key":1486666920000,"doc_count":11},{"key_as_string":"2017-02-09T19:03:00.000Z","key":1486666980000,"doc_count":14},{"key_as_string":"2017-02-09T19:04:00.000Z","key":1486667040000,"doc_count":10},{"key_as_string":"2017-02-09T19:05:00.000Z","key":1486667100000,"doc_count":9},{"key_as_string":"2017-02-09T19:06:00.000Z","key":1486667160000,"doc_count":8},{"key_as_string":"2017-02-09T19:07:00.000Z","key":1486667220000,"doc_count":19},{"key_as_string":"2017-02-09T19:08:00.000Z","key":1486667280000,"doc_count":8},{"key_as_string":"2017-02-09T19:09:00.000Z","key":1486667340000,"doc_count":12},{"key_as_string":"2017-02-09T19:10:00.000Z","key":1486667400000,"doc_count":10},{"key_as_string":"2017-02-09T19:11:00.000Z","key":1486667460000,"doc_count":9},{"key_as_string":"2017-02-09T19:12:00.000Z","key":1486667520000,"doc_count":10},{"key_as_string":"2017-02-09T19:13:00.000Z","key":1486667580000,"doc_count":7},{"key_as_string":"2017-02-09T19:14:00.000Z","key":1486667640000,"doc_count":7},{"key_as_string":"2017-02-09T19:15:00.000Z","key":1486667700000,"doc_count":15},{"key_as_string":"2017-02-09T19:16:00.000Z","key":1486667760000,"doc_count":8},{"key_as_string":"2017-02-09T19:17:00.000Z","key":1486667820000,"doc_count":9},{"key_as_string":"2017-02-09T19:18:00.000Z","key":1486667880000,"doc_count":14},{"key_as_string":"2017-02-09T19:19:00.000Z","key":1486667940000,"doc_count":10},{"key_as_string":"2017-02-09T19:20:00.000Z","key":1486668000000,"doc_count":8},{"key_as_string":"2017-02-09T19:21:00.000Z","key":1486668060000,"doc_count":19},{"key_as_string":"2017-02-09T19:22:00.000Z","key":1486668120000,"doc_count":7},{"key_as_string":"2017-02-09T19:23:00.000Z","key":1486668180000,"doc_count":9},{"key_as_string":"2017-02-09T19:24:00.000Z","key":1486668240000,"doc_count":9},{"key_as_string":"2017-02-09T19:25:00.000Z","key":1486668300000,"doc_count":14},{"key_as_string":"2017-02-09T19:26:00.000Z","key":1486668360000,"doc_count":15},{"key_as_string":"2017-02-09T19:27:00.000Z","key":1486668420000,"doc_count":13},{"key_as_string":"2017-02-09T19:28:00.000Z","key":1486668480000,"doc_count":15},{"key_as_string":"2017-02-09T19:29:00.000Z","key":1486668540000,"doc_count":9},{"key_as_string":"2017-02-09T19:30:00.000Z","key":1486668600000,"doc_count":7},{"key_as_string":"2017-02-09T19:31:00.000Z","key":1486668660000,"doc_count":9},{"key_as_string":"2017-02-09T19:32:00.000Z","key":1486668720000,"doc_count":6},{"key_as_string":"2017-02-09T19:33:00.000Z","key":1486668780000,"doc_count":16},{"key_as_string":"2017-02-09T19:34:00.000Z","key":1486668840000,"doc_count":14},{"key_as_string":"2017-02-09T19:35:00.000Z","key":1486668900000,"doc_count":12},{"key_as_string":"2017-02-09T19:36:00.000Z","key":1486668960000,"doc_count":12},{"key_as_string":"2017-02-09T19:37:00.000Z","key":1486669020000,"doc_count":11},{"key_as_string":"2017-02-09T19:38:00.000Z","key":1486669080000,"doc_count":12},{"key_as_string":"2017-02-09T19:39:00.000Z","key":1486669140000,"doc_count":13},{"key_as_string":"2017-02-09T19:40:00.000Z","key":1486669200000,"doc_count":6},{"key_as_string":"2017-02-09T19:41:00.000Z","key":1486669260000,"doc_count":14},{"key_as_string":"2017-02-09T19:42:00.000Z","key":1486669320000,"doc_count":15},{"key_as_string":"2017-02-09T19:43:00.000Z","key":1486669380000,"doc_count":9},{"key_as_string":"2017-02-09T19:44:00.000Z","key":1486669440000,"doc_count":7},{"key_as_string":"2017-02-09T19:45:00.000Z","key":1486669500000,"doc_count":15},{"key_as_string":"2017-02-09T19:46:00.000Z","key":1486669560000,"doc_count":10},{"key_as_string":"2017-02-09T19:47:00.000Z","key":1486669620000,"doc_count":9},{"key_as_string":"2017-02-09T19:48:00.000Z","key":1486669680000,"doc_count":9},{"key_as_string":"2017-02-09T19:49:00.000Z","key":1486669740000,"doc_count":14},{"key_as_string":"2017-02-09T19:50:00.000Z","key":1486669800000,"doc_count":8},{"key_as_string":"2017-02-09T19:51:00.000Z","key":1486669860000,"doc_count":12},{"key_as_string":"2017-02-09T19:52:00.000Z","key":1486669920000,"doc_count":8},{"key_as_string":"2017-02-09T19:53:00.000Z","key":1486669980000,"doc_count":6},{"key_as_string":"2017-02-09T19:54:00.000Z","key":1486670040000,"doc_count":13},{"key_as_string":"2017-02-09T19:55:00.000Z","key":1486670100000,"doc_count":14},{"key_as_string":"2017-02-09T19:56:00.000Z","key":1486670160000,"doc_count":10},{"key_as_string":"2017-02-09T19:57:00.000Z","key":1486670220000,"doc_count":15},{"key_as_string":"2017-02-09T19:58:00.000Z","key":1486670280000,"doc_count":11},{"key_as_string":"2017-02-09T19:59:00.000Z","key":1486670340000,"doc_count":11},{"key_as_string":"2017-02-09T20:00:00.000Z","key":1486670400000,"doc_count":14},{"key_as_string":"2017-02-09T20:01:00.000Z","key":1486670460000,"doc_count":16},{"key_as_string":"2017-02-09T20:02:00.000Z","key":1486670520000,"doc_count":11},{"key_as_string":"2017-02-09T20:03:00.000Z","key":1486670580000,"doc_count":11},{"key_as_string":"2017-02-09T20:04:00.000Z","key":1486670640000,"doc_count":14},{"key_as_string":"2017-02-09T20:05:00.000Z","key":1486670700000,"doc_count":6},{"key_as_string":"2017-02-09T20:06:00.000Z","key":1486670760000,"doc_count":12},{"key_as_string":"2017-02-09T20:07:00.000Z","key":1486670820000,"doc_count":14},{"key_as_string":"2017-02-09T20:08:00.000Z","key":1486670880000,"doc_count":11},{"key_as_string":"2017-02-09T20:09:00.000Z","key":1486670940000,"doc_count":15},{"key_as_string":"2017-02-09T20:10:00.000Z","key":1486671000000,"doc_count":14},{"key_as_string":"2017-02-09T20:11:00.000Z","key":1486671060000,"doc_count":13},{"key_as_string":"2017-02-09T20:12:00.000Z","key":1486671120000,"doc_count":10},{"key_as_string":"2017-02-09T20:13:00.000Z","key":1486671180000,"doc_count":12},{"key_as_string":"2017-02-09T20:14:00.000Z","key":1486671240000,"doc_count":10},{"key_as_string":"2017-02-09T20:15:00.000Z","key":1486671300000,"doc_count":9},{"key_as_string":"2017-02-09T20:16:00.000Z","key":1486671360000,"doc_count":12},{"key_as_string":"2017-02-09T20:17:00.000Z","key":1486671420000,"doc_count":9},{"key_as_string":"2017-02-09T20:18:00.000Z","key":1486671480000,"doc_count":14},{"key_as_string":"2017-02-09T20:19:00.000Z","key":1486671540000,"doc_count":12},{"key_as_string":"2017-02-09T20:20:00.000Z","key":1486671600000,"doc_count":11},{"key_as_string":"2017-02-09T20:21:00.000Z","key":1486671660000,"doc_count":10},{"key_as_string":"2017-02-09T20:22:00.000Z","key":1486671720000,"doc_count":14},{"key_as_string":"2017-02-09T20:23:00.000Z","key":1486671780000,"doc_count":10},{"key_as_string":"2017-02-09T20:24:00.000Z","key":1486671840000,"doc_count":12},{"key_as_string":"2017-02-09T20:25:00.000Z","key":1486671900000,"doc_count":8},{"key_as_string":"2017-02-09T20:26:00.000Z","key":1486671960000,"doc_count":14},{"key_as_string":"2017-02-09T20:27:00.000Z","key":1486672020000,"doc_count":7},{"key_as_string":"2017-02-09T20:28:00.000Z","key":1486672080000,"doc_count":12},{"key_as_string":"2017-02-09T20:29:00.000Z","key":1486672140000,"doc_count":11},{"key_as_string":"2017-02-09T20:30:00.000Z","key":1486672200000,"doc_count":15},{"key_as_string":"2017-02-09T20:31:00.000Z","key":1486672260000,"doc_count":11},{"key_as_string":"2017-02-09T20:32:00.000Z","key":1486672320000,"doc_count":11},{"key_as_string":"2017-02-09T20:33:00.000Z","key":1486672380000,"doc_count":7},{"key_as_string":"2017-02-09T20:34:00.000Z","key":1486672440000,"doc_count":10},{"key_as_string":"2017-02-09T20:35:00.000Z","key":1486672500000,"doc_count":13},{"key_as_string":"2017-02-09T20:36:00.000Z","key":1486672560000,"doc_count":6},{"key_as_string":"2017-02-09T20:37:00.000Z","key":1486672620000,"doc_count":9},{"key_as_string":"2017-02-09T20:38:00.000Z","key":1486672680000,"doc_count":7},{"key_as_string":"2017-02-09T20:39:00.000Z","key":1486672740000,"doc_count":10},{"key_as_string":"2017-02-09T20:40:00.000Z","key":1486672800000,"doc_count":8},{"key_as_string":"2017-02-09T20:41:00.000Z","key":1486672860000,"doc_count":12},{"key_as_string":"2017-02-09T20:42:00.000Z","key":1486672920000,"doc_count":6},{"key_as_string":"2017-02-09T20:43:00.000Z","key":1486672980000,"doc_count":14},{"key_as_string":"2017-02-09T20:44:00.000Z","key":1486673040000,"doc_count":6},{"key_as_string":"2017-02-09T20:45:00.000Z","key":1486673100000,"doc_count":8},{"key_as_string":"2017-02-09T20:46:00.000Z","key":1486673160000,"doc_count":14},{"key_as_string":"2017-02-09T20:47:00.000Z","key":1486673220000,"doc_count":10},{"key_as_string":"2017-02-09T20:48:00.000Z","key":1486673280000,"doc_count":9},{"key_as_string":"2017-02-09T20:49:00.000Z","key":1486673340000,"doc_count":11},{"key_as_string":"2017-02-09T20:50:00.000Z","key":1486673400000,"doc_count":14},{"key_as_string":"2017-02-09T20:51:00.000Z","key":1486673460000,"doc_count":6},{"key_as_string":"2017-02-09T20:52:00.000Z","key":1486673520000,"doc_count":9},{"key_as_string":"2017-02-09T20:53:00.000Z","key":1486673580000,"doc_count":10},{"key_as_string":"2017-02-09T20:54:00.000Z","key":1486673640000,"doc_count":13},{"key_as_string":"2017-02-09T20:55:00.000Z","key":1486673700000,"doc_count":11},{"key_as_string":"2017-02-09T20:56:00.000Z","key":1486673760000,"doc_count":6},{"key_as_string":"2017-02-09T20:57:00.000Z","key":1486673820000,"doc_count":10},{"key_as_string":"2017-02-09T20:58:00.000Z","key":1486673880000,"doc_count":10},{"key_as_string":"2017-02-09T20:59:00.000Z","key":1486673940000,"doc_count":9},{"key_as_string":"2017-02-09T21:00:00.000Z","key":1486674000000,"doc_count":17},{"key_as_string":"2017-02-09T21:01:00.000Z","key":1486674060000,"doc_count":11},{"key_as_string":"2017-02-09T21:02:00.000Z","key":1486674120000,"doc_count":10},{"key_as_string":"2017-02-09T21:03:00.000Z","key":1486674180000,"doc_count":9},{"key_as_string":"2017-02-09T21:04:00.000Z","key":1486674240000,"doc_count":14},{"key_as_string":"2017-02-09T21:05:00.000Z","key":1486674300000,"doc_count":10},{"key_as_string":"2017-02-09T21:06:00.000Z","key":1486674360000,"doc_count":12},{"key_as_string":"2017-02-09T21:07:00.000Z","key":1486674420000,"doc_count":9},{"key_as_string":"2017-02-09T21:08:00.000Z","key":1486674480000,"doc_count":11},{"key_as_string":"2017-02-09T21:09:00.000Z","key":1486674540000,"doc_count":11},{"key_as_string":"2017-02-09T21:10:00.000Z","key":1486674600000,"doc_count":11},{"key_as_string":"2017-02-09T21:11:00.000Z","key":1486674660000,"doc_count":10},{"key_as_string":"2017-02-09T21:12:00.000Z","key":1486674720000,"doc_count":9},{"key_as_string":"2017-02-09T21:13:00.000Z","key":1486674780000,"doc_count":13},{"key_as_string":"2017-02-09T21:14:00.000Z","key":1486674840000,"doc_count":9},{"key_as_string":"2017-02-09T21:15:00.000Z","key":1486674900000,"doc_count":8},{"key_as_string":"2017-02-09T21:16:00.000Z","key":1486674960000,"doc_count":11},{"key_as_string":"2017-02-09T21:17:00.000Z","key":1486675020000,"doc_count":12},{"key_as_string":"2017-02-09T21:18:00.000Z","key":1486675080000,"doc_count":7},{"key_as_string":"2017-02-09T21:19:00.000Z","key":1486675140000,"doc_count":14},{"key_as_string":"2017-02-09T21:20:00.000Z","key":1486675200000,"doc_count":15},{"key_as_string":"2017-02-09T21:21:00.000Z","key":1486675260000,"doc_count":5},{"key_as_string":"2017-02-09T21:22:00.000Z","key":1486675320000,"doc_count":8},{"key_as_string":"2017-02-09T21:23:00.000Z","key":1486675380000,"doc_count":15},{"key_as_string":"2017-02-09T21:24:00.000Z","key":1486675440000,"doc_count":9},{"key_as_string":"2017-02-09T21:25:00.000Z","key":1486675500000,"doc_count":14},{"key_as_string":"2017-02-09T21:26:00.000Z","key":1486675560000,"doc_count":12},{"key_as_string":"2017-02-09T21:27:00.000Z","key":1486675620000,"doc_count":8},{"key_as_string":"2017-02-09T21:28:00.000Z","key":1486675680000,"doc_count":12},{"key_as_string":"2017-02-09T21:29:00.000Z","key":1486675740000,"doc_count":10},{"key_as_string":"2017-02-09T21:30:00.000Z","key":1486675800000,"doc_count":10},{"key_as_string":"2017-02-09T21:31:00.000Z","key":1486675860000,"doc_count":11},{"key_as_string":"2017-02-09T21:32:00.000Z","key":1486675920000,"doc_count":10},{"key_as_string":"2017-02-09T21:33:00.000Z","key":1486675980000,"doc_count":9},{"key_as_string":"2017-02-09T21:34:00.000Z","key":1486676040000,"doc_count":10},{"key_as_string":"2017-02-09T21:35:00.000Z","key":1486676100000,"doc_count":11},{"key_as_string":"2017-02-09T21:36:00.000Z","key":1486676160000,"doc_count":11},{"key_as_string":"2017-02-09T21:37:00.000Z","key":1486676220000,"doc_count":8},{"key_as_string":"2017-02-09T21:38:00.000Z","key":1486676280000,"doc_count":12},{"key_as_string":"2017-02-09T21:39:00.000Z","key":1486676340000,"doc_count":9},{"key_as_string":"2017-02-09T21:40:00.000Z","key":1486676400000,"doc_count":8},{"key_as_string":"2017-02-09T21:41:00.000Z","key":1486676460000,"doc_count":10},{"key_as_string":"2017-02-09T21:42:00.000Z","key":1486676520000,"doc_count":8},{"key_as_string":"2017-02-09T21:43:00.000Z","key":1486676580000,"doc_count":12},{"key_as_string":"2017-02-09T21:44:00.000Z","key":1486676640000,"doc_count":10},{"key_as_string":"2017-02-09T21:45:00.000Z","key":1486676700000,"doc_count":14},{"key_as_string":"2017-02-09T21:46:00.000Z","key":1486676760000,"doc_count":11},{"key_as_string":"2017-02-09T21:47:00.000Z","key":1486676820000,"doc_count":8},{"key_as_string":"2017-02-09T21:48:00.000Z","key":1486676880000,"doc_count":12},{"key_as_string":"2017-02-09T21:49:00.000Z","key":1486676940000,"doc_count":12},{"key_as_string":"2017-02-09T21:50:00.000Z","key":1486677000000,"doc_count":9},{"key_as_string":"2017-02-09T21:51:00.000Z","key":1486677060000,"doc_count":11},{"key_as_string":"2017-02-09T21:52:00.000Z","key":1486677120000,"doc_count":10},{"key_as_string":"2017-02-09T21:53:00.000Z","key":1486677180000,"doc_count":11},{"key_as_string":"2017-02-09T21:54:00.000Z","key":1486677240000,"doc_count":14},{"key_as_string":"2017-02-09T21:55:00.000Z","key":1486677300000,"doc_count":9},{"key_as_string":"2017-02-09T21:56:00.000Z","key":1486677360000,"doc_count":8},{"key_as_string":"2017-02-09T21:57:00.000Z","key":1486677420000,"doc_count":10},{"key_as_string":"2017-02-09T21:58:00.000Z","key":1486677480000,"doc_count":8},{"key_as_string":"2017-02-09T21:59:00.000Z","key":1486677540000,"doc_count":16},{"key_as_string":"2017-02-09T22:00:00.000Z","key":1486677600000,"doc_count":11},{"key_as_string":"2017-02-09T22:01:00.000Z","key":1486677660000,"doc_count":7},{"key_as_string":"2017-02-09T22:02:00.000Z","key":1486677720000,"doc_count":13},{"key_as_string":"2017-02-09T22:03:00.000Z","key":1486677780000,"doc_count":12},{"key_as_string":"2017-02-09T22:04:00.000Z","key":1486677840000,"doc_count":9},{"key_as_string":"2017-02-09T22:05:00.000Z","key":1486677900000,"doc_count":14},{"key_as_string":"2017-02-09T22:06:00.000Z","key":1486677960000,"doc_count":8},{"key_as_string":"2017-02-09T22:07:00.000Z","key":1486678020000,"doc_count":9},{"key_as_string":"2017-02-09T22:08:00.000Z","key":1486678080000,"doc_count":15},{"key_as_string":"2017-02-09T22:09:00.000Z","key":1486678140000,"doc_count":10},{"key_as_string":"2017-02-09T22:10:00.000Z","key":1486678200000,"doc_count":7},{"key_as_string":"2017-02-09T22:11:00.000Z","key":1486678260000,"doc_count":11},{"key_as_string":"2017-02-09T22:12:00.000Z","key":1486678320000,"doc_count":8},{"key_as_string":"2017-02-09T22:13:00.000Z","key":1486678380000,"doc_count":13},{"key_as_string":"2017-02-09T22:14:00.000Z","key":1486678440000,"doc_count":12},{"key_as_string":"2017-02-09T22:15:00.000Z","key":1486678500000,"doc_count":6},{"key_as_string":"2017-02-09T22:16:00.000Z","key":1486678560000,"doc_count":9},{"key_as_string":"2017-02-09T22:17:00.000Z","key":1486678620000,"doc_count":13},{"key_as_string":"2017-02-09T22:18:00.000Z","key":1486678680000,"doc_count":12},{"key_as_string":"2017-02-09T22:19:00.000Z","key":1486678740000,"doc_count":9},{"key_as_string":"2017-02-09T22:20:00.000Z","key":1486678800000,"doc_count":7},{"key_as_string":"2017-02-09T22:21:00.000Z","key":1486678860000,"doc_count":8},{"key_as_string":"2017-02-09T22:22:00.000Z","key":1486678920000,"doc_count":12},{"key_as_string":"2017-02-09T22:23:00.000Z","key":1486678980000,"doc_count":7},{"key_as_string":"2017-02-09T22:24:00.000Z","key":1486679040000,"doc_count":15},{"key_as_string":"2017-02-09T22:25:00.000Z","key":1486679100000,"doc_count":12},{"key_as_string":"2017-02-09T22:26:00.000Z","key":1486679160000,"doc_count":12},{"key_as_string":"2017-02-09T22:27:00.000Z","key":1486679220000,"doc_count":12},{"key_as_string":"2017-02-09T22:28:00.000Z","key":1486679280000,"doc_count":11},{"key_as_string":"2017-02-09T22:29:00.000Z","key":1486679340000,"doc_count":7},{"key_as_string":"2017-02-09T22:30:00.000Z","key":1486679400000,"doc_count":9},{"key_as_string":"2017-02-09T22:31:00.000Z","key":1486679460000,"doc_count":10},{"key_as_string":"2017-02-09T22:32:00.000Z","key":1486679520000,"doc_count":14},{"key_as_string":"2017-02-09T22:33:00.000Z","key":1486679580000,"doc_count":10},{"key_as_string":"2017-02-09T22:34:00.000Z","key":1486679640000,"doc_count":14},{"key_as_string":"2017-02-09T22:35:00.000Z","key":1486679700000,"doc_count":13},{"key_as_string":"2017-02-09T22:36:00.000Z","key":1486679760000,"doc_count":9},{"key_as_string":"2017-02-09T22:37:00.000Z","key":1486679820000,"doc_count":7},{"key_as_string":"2017-02-09T22:38:00.000Z","key":1486679880000,"doc_count":11},{"key_as_string":"2017-02-09T22:39:00.000Z","key":1486679940000,"doc_count":8},{"key_as_string":"2017-02-09T22:40:00.000Z","key":1486680000000,"doc_count":9},{"key_as_string":"2017-02-09T22:41:00.000Z","key":1486680060000,"doc_count":9},{"key_as_string":"2017-02-09T22:42:00.000Z","key":1486680120000,"doc_count":15},{"key_as_string":"2017-02-09T22:43:00.000Z","key":1486680180000,"doc_count":8},{"key_as_string":"2017-02-09T22:44:00.000Z","key":1486680240000,"doc_count":7},{"key_as_string":"2017-02-09T22:45:00.000Z","key":1486680300000,"doc_count":10},{"key_as_string":"2017-02-09T22:46:00.000Z","key":1486680360000,"doc_count":13},{"key_as_string":"2017-02-09T22:47:00.000Z","key":1486680420000,"doc_count":14},{"key_as_string":"2017-02-09T22:48:00.000Z","key":1486680480000,"doc_count":7},{"key_as_string":"2017-02-09T22:49:00.000Z","key":1486680540000,"doc_count":8},{"key_as_string":"2017-02-09T22:50:00.000Z","key":1486680600000,"doc_count":12},{"key_as_string":"2017-02-09T22:51:00.000Z","key":1486680660000,"doc_count":9},{"key_as_string":"2017-02-09T22:52:00.000Z","key":1486680720000,"doc_count":9},{"key_as_string":"2017-02-09T22:53:00.000Z","key":1486680780000,"doc_count":6},{"key_as_string":"2017-02-09T22:54:00.000Z","key":1486680840000,"doc_count":17},{"key_as_string":"2017-02-09T22:55:00.000Z","key":1486680900000,"doc_count":10},{"key_as_string":"2017-02-09T22:56:00.000Z","key":1486680960000,"doc_count":9},{"key_as_string":"2017-02-09T22:57:00.000Z","key":1486681020000,"doc_count":12},{"key_as_string":"2017-02-09T22:58:00.000Z","key":1486681080000,"doc_count":10},{"key_as_string":"2017-02-09T22:59:00.000Z","key":1486681140000,"doc_count":11},{"key_as_string":"2017-02-09T23:00:00.000Z","key":1486681200000,"doc_count":6},{"key_as_string":"2017-02-09T23:01:00.000Z","key":1486681260000,"doc_count":11},{"key_as_string":"2017-02-09T23:02:00.000Z","key":1486681320000,"doc_count":11},{"key_as_string":"2017-02-09T23:03:00.000Z","key":1486681380000,"doc_count":13},{"key_as_string":"2017-02-09T23:04:00.000Z","key":1486681440000,"doc_count":16},{"key_as_string":"2017-02-09T23:05:00.000Z","key":1486681500000,"doc_count":10},{"key_as_string":"2017-02-09T23:06:00.000Z","key":1486681560000,"doc_count":9},{"key_as_string":"2017-02-09T23:07:00.000Z","key":1486681620000,"doc_count":6},{"key_as_string":"2017-02-09T23:08:00.000Z","key":1486681680000,"doc_count":11},{"key_as_string":"2017-02-09T23:09:00.000Z","key":1486681740000,"doc_count":12},{"key_as_string":"2017-02-09T23:10:00.000Z","key":1486681800000,"doc_count":4},{"key_as_string":"2017-02-09T23:11:00.000Z","key":1486681860000,"doc_count":12},{"key_as_string":"2017-02-09T23:12:00.000Z","key":1486681920000,"doc_count":12},{"key_as_string":"2017-02-09T23:13:00.000Z","key":1486681980000,"doc_count":8},{"key_as_string":"2017-02-09T23:14:00.000Z","key":1486682040000,"doc_count":12},{"key_as_string":"2017-02-09T23:15:00.000Z","key":1486682100000,"doc_count":8},{"key_as_string":"2017-02-09T23:16:00.000Z","key":1486682160000,"doc_count":11},{"key_as_string":"2017-02-09T23:17:00.000Z","key":1486682220000,"doc_count":12},{"key_as_string":"2017-02-09T23:18:00.000Z","key":1486682280000,"doc_count":6},{"key_as_string":"2017-02-09T23:19:00.000Z","key":1486682340000,"doc_count":13},{"key_as_string":"2017-02-09T23:20:00.000Z","key":1486682400000,"doc_count":9},{"key_as_string":"2017-02-09T23:21:00.000Z","key":1486682460000,"doc_count":7},{"key_as_string":"2017-02-09T23:22:00.000Z","key":1486682520000,"doc_count":18},{"key_as_string":"2017-02-09T23:23:00.000Z","key":1486682580000,"doc_count":6},{"key_as_string":"2017-02-09T23:24:00.000Z","key":1486682640000,"doc_count":10},{"key_as_string":"2017-02-09T23:25:00.000Z","key":1486682700000,"doc_count":10},{"key_as_string":"2017-02-09T23:26:00.000Z","key":1486682760000,"doc_count":13},{"key_as_string":"2017-02-09T23:27:00.000Z","key":1486682820000,"doc_count":12},{"key_as_string":"2017-02-09T23:28:00.000Z","key":1486682880000,"doc_count":14},{"key_as_string":"2017-02-09T23:29:00.000Z","key":1486682940000,"doc_count":7},{"key_as_string":"2017-02-09T23:30:00.000Z","key":1486683000000,"doc_count":8},{"key_as_string":"2017-02-09T23:31:00.000Z","key":1486683060000,"doc_count":13},{"key_as_string":"2017-02-09T23:32:00.000Z","key":1486683120000,"doc_count":7},{"key_as_string":"2017-02-09T23:33:00.000Z","key":1486683180000,"doc_count":10},{"key_as_string":"2017-02-09T23:34:00.000Z","key":1486683240000,"doc_count":9},{"key_as_string":"2017-02-09T23:35:00.000Z","key":1486683300000,"doc_count":8},{"key_as_string":"2017-02-09T23:36:00.000Z","key":1486683360000,"doc_count":11},{"key_as_string":"2017-02-09T23:37:00.000Z","key":1486683420000,"doc_count":14},{"key_as_string":"2017-02-09T23:38:00.000Z","key":1486683480000,"doc_count":11},{"key_as_string":"2017-02-09T23:39:00.000Z","key":1486683540000,"doc_count":8},{"key_as_string":"2017-02-09T23:40:00.000Z","key":1486683600000,"doc_count":12},{"key_as_string":"2017-02-09T23:41:00.000Z","key":1486683660000,"doc_count":12},{"key_as_string":"2017-02-09T23:42:00.000Z","key":1486683720000,"doc_count":9},{"key_as_string":"2017-02-09T23:43:00.000Z","key":1486683780000,"doc_count":16},{"key_as_string":"2017-02-09T23:44:00.000Z","key":1486683840000,"doc_count":11},{"key_as_string":"2017-02-09T23:45:00.000Z","key":1486683900000,"doc_count":7},{"key_as_string":"2017-02-09T23:46:00.000Z","key":1486683960000,"doc_count":12},{"key_as_string":"2017-02-09T23:47:00.000Z","key":1486684020000,"doc_count":15},{"key_as_string":"2017-02-09T23:48:00.000Z","key":1486684080000,"doc_count":7},{"key_as_string":"2017-02-09T23:49:00.000Z","key":1486684140000,"doc_count":10},{"key_as_string":"2017-02-09T23:50:00.000Z","key":1486684200000,"doc_count":15},{"key_as_string":"2017-02-09T23:51:00.000Z","key":1486684260000,"doc_count":12},{"key_as_string":"2017-02-09T23:52:00.000Z","key":1486684320000,"doc_count":9},{"key_as_string":"2017-02-09T23:53:00.000Z","key":1486684380000,"doc_count":7},{"key_as_string":"2017-02-09T23:54:00.000Z","key":1486684440000,"doc_count":8},{"key_as_string":"2017-02-09T23:55:00.000Z","key":1486684500000,"doc_count":8},{"key_as_string":"2017-02-09T23:56:00.000Z","key":1486684560000,"doc_count":13},{"key_as_string":"2017-02-09T23:57:00.000Z","key":1486684620000,"doc_count":12},{"key_as_string":"2017-02-09T23:58:00.000Z","key":1486684680000,"doc_count":12},{"key_as_string":"2017-02-09T23:59:00.000Z","key":1486684740000,"doc_count":9},{"key_as_string":"2017-02-10T00:00:00.000Z","key":1486684800000,"doc_count":8},{"key_as_string":"2017-02-10T00:01:00.000Z","key":1486684860000,"doc_count":10},{"key_as_string":"2017-02-10T00:02:00.000Z","key":1486684920000,"doc_count":12},{"key_as_string":"2017-02-10T00:03:00.000Z","key":1486684980000,"doc_count":11},{"key_as_string":"2017-02-10T00:04:00.000Z","key":1486685040000,"doc_count":11},{"key_as_string":"2017-02-10T00:05:00.000Z","key":1486685100000,"doc_count":12},{"key_as_string":"2017-02-10T00:06:00.000Z","key":1486685160000,"doc_count":9},{"key_as_string":"2017-02-10T00:07:00.000Z","key":1486685220000,"doc_count":11},{"key_as_string":"2017-02-10T00:08:00.000Z","key":1486685280000,"doc_count":8},{"key_as_string":"2017-02-10T00:09:00.000Z","key":1486685340000,"doc_count":11},{"key_as_string":"2017-02-10T00:10:00.000Z","key":1486685400000,"doc_count":10},{"key_as_string":"2017-02-10T00:11:00.000Z","key":1486685460000,"doc_count":14},{"key_as_string":"2017-02-10T00:12:00.000Z","key":1486685520000,"doc_count":5},{"key_as_string":"2017-02-10T00:13:00.000Z","key":1486685580000,"doc_count":6},{"key_as_string":"2017-02-10T00:14:00.000Z","key":1486685640000,"doc_count":8},{"key_as_string":"2017-02-10T00:15:00.000Z","key":1486685700000,"doc_count":11},{"key_as_string":"2017-02-10T00:16:00.000Z","key":1486685760000,"doc_count":15},{"key_as_string":"2017-02-10T00:17:00.000Z","key":1486685820000,"doc_count":8},{"key_as_string":"2017-02-10T00:18:00.000Z","key":1486685880000,"doc_count":12},{"key_as_string":"2017-02-10T00:19:00.000Z","key":1486685940000,"doc_count":7},{"key_as_string":"2017-02-10T00:20:00.000Z","key":1486686000000,"doc_count":11},{"key_as_string":"2017-02-10T00:21:00.000Z","key":1486686060000,"doc_count":20},{"key_as_string":"2017-02-10T00:22:00.000Z","key":1486686120000,"doc_count":7},{"key_as_string":"2017-02-10T00:23:00.000Z","key":1486686180000,"doc_count":11},{"key_as_string":"2017-02-10T00:24:00.000Z","key":1486686240000,"doc_count":8},{"key_as_string":"2017-02-10T00:25:00.000Z","key":1486686300000,"doc_count":9},{"key_as_string":"2017-02-10T00:26:00.000Z","key":1486686360000,"doc_count":15},{"key_as_string":"2017-02-10T00:27:00.000Z","key":1486686420000,"doc_count":12},{"key_as_string":"2017-02-10T00:28:00.000Z","key":1486686480000,"doc_count":13},{"key_as_string":"2017-02-10T00:29:00.000Z","key":1486686540000,"doc_count":10},{"key_as_string":"2017-02-10T00:30:00.000Z","key":1486686600000,"doc_count":14},{"key_as_string":"2017-02-10T00:31:00.000Z","key":1486686660000,"doc_count":9},{"key_as_string":"2017-02-10T00:32:00.000Z","key":1486686720000,"doc_count":6},{"key_as_string":"2017-02-10T00:33:00.000Z","key":1486686780000,"doc_count":13},{"key_as_string":"2017-02-10T00:34:00.000Z","key":1486686840000,"doc_count":8},{"key_as_string":"2017-02-10T00:35:00.000Z","key":1486686900000,"doc_count":12},{"key_as_string":"2017-02-10T00:36:00.000Z","key":1486686960000,"doc_count":10},{"key_as_string":"2017-02-10T00:37:00.000Z","key":1486687020000,"doc_count":11},{"key_as_string":"2017-02-10T00:38:00.000Z","key":1486687080000,"doc_count":12},{"key_as_string":"2017-02-10T00:39:00.000Z","key":1486687140000,"doc_count":11},{"key_as_string":"2017-02-10T00:40:00.000Z","key":1486687200000,"doc_count":9},{"key_as_string":"2017-02-10T00:41:00.000Z","key":1486687260000,"doc_count":15},{"key_as_string":"2017-02-10T00:42:00.000Z","key":1486687320000,"doc_count":11},{"key_as_string":"2017-02-10T00:43:00.000Z","key":1486687380000,"doc_count":10},{"key_as_string":"2017-02-10T00:44:00.000Z","key":1486687440000,"doc_count":6},{"key_as_string":"2017-02-10T00:45:00.000Z","key":1486687500000,"doc_count":9},{"key_as_string":"2017-02-10T00:46:00.000Z","key":1486687560000,"doc_count":6},{"key_as_string":"2017-02-10T00:47:00.000Z","key":1486687620000,"doc_count":15},{"key_as_string":"2017-02-10T00:48:00.000Z","key":1486687680000,"doc_count":5},{"key_as_string":"2017-02-10T00:49:00.000Z","key":1486687740000,"doc_count":8},{"key_as_string":"2017-02-10T00:50:00.000Z","key":1486687800000,"doc_count":14},{"key_as_string":"2017-02-10T00:51:00.000Z","key":1486687860000,"doc_count":8},{"key_as_string":"2017-02-10T00:52:00.000Z","key":1486687920000,"doc_count":10},{"key_as_string":"2017-02-10T00:53:00.000Z","key":1486687980000,"doc_count":11},{"key_as_string":"2017-02-10T00:54:00.000Z","key":1486688040000,"doc_count":11},{"key_as_string":"2017-02-10T00:55:00.000Z","key":1486688100000,"doc_count":14},{"key_as_string":"2017-02-10T00:56:00.000Z","key":1486688160000,"doc_count":11},{"key_as_string":"2017-02-10T00:57:00.000Z","key":1486688220000,"doc_count":9},{"key_as_string":"2017-02-10T00:58:00.000Z","key":1486688280000,"doc_count":10},{"key_as_string":"2017-02-10T00:59:00.000Z","key":1486688340000,"doc_count":12},{"key_as_string":"2017-02-10T01:00:00.000Z","key":1486688400000,"doc_count":8},{"key_as_string":"2017-02-10T01:01:00.000Z","key":1486688460000,"doc_count":14},{"key_as_string":"2017-02-10T01:02:00.000Z","key":1486688520000,"doc_count":10},{"key_as_string":"2017-02-10T01:03:00.000Z","key":1486688580000,"doc_count":7},{"key_as_string":"2017-02-10T01:04:00.000Z","key":1486688640000,"doc_count":14},{"key_as_string":"2017-02-10T01:05:00.000Z","key":1486688700000,"doc_count":5},{"key_as_string":"2017-02-10T01:06:00.000Z","key":1486688760000,"doc_count":19},{"key_as_string":"2017-02-10T01:07:00.000Z","key":1486688820000,"doc_count":7},{"key_as_string":"2017-02-10T01:08:00.000Z","key":1486688880000,"doc_count":14},{"key_as_string":"2017-02-10T01:09:00.000Z","key":1486688940000,"doc_count":6},{"key_as_string":"2017-02-10T01:10:00.000Z","key":1486689000000,"doc_count":11},{"key_as_string":"2017-02-10T01:11:00.000Z","key":1486689060000,"doc_count":16},{"key_as_string":"2017-02-10T01:12:00.000Z","key":1486689120000,"doc_count":11},{"key_as_string":"2017-02-10T01:13:00.000Z","key":1486689180000,"doc_count":6},{"key_as_string":"2017-02-10T01:14:00.000Z","key":1486689240000,"doc_count":10},{"key_as_string":"2017-02-10T01:15:00.000Z","key":1486689300000,"doc_count":10},{"key_as_string":"2017-02-10T01:16:00.000Z","key":1486689360000,"doc_count":10},{"key_as_string":"2017-02-10T01:17:00.000Z","key":1486689420000,"doc_count":11},{"key_as_string":"2017-02-10T01:18:00.000Z","key":1486689480000,"doc_count":7},{"key_as_string":"2017-02-10T01:19:00.000Z","key":1486689540000,"doc_count":8},{"key_as_string":"2017-02-10T01:20:00.000Z","key":1486689600000,"doc_count":17},{"key_as_string":"2017-02-10T01:21:00.000Z","key":1486689660000,"doc_count":12},{"key_as_string":"2017-02-10T01:22:00.000Z","key":1486689720000,"doc_count":12},{"key_as_string":"2017-02-10T01:23:00.000Z","key":1486689780000,"doc_count":12},{"key_as_string":"2017-02-10T01:24:00.000Z","key":1486689840000,"doc_count":13},{"key_as_string":"2017-02-10T01:25:00.000Z","key":1486689900000,"doc_count":9},{"key_as_string":"2017-02-10T01:26:00.000Z","key":1486689960000,"doc_count":8},{"key_as_string":"2017-02-10T01:27:00.000Z","key":1486690020000,"doc_count":9},{"key_as_string":"2017-02-10T01:28:00.000Z","key":1486690080000,"doc_count":10},{"key_as_string":"2017-02-10T01:29:00.000Z","key":1486690140000,"doc_count":11},{"key_as_string":"2017-02-10T01:30:00.000Z","key":1486690200000,"doc_count":5},{"key_as_string":"2017-02-10T01:31:00.000Z","key":1486690260000,"doc_count":12},{"key_as_string":"2017-02-10T01:32:00.000Z","key":1486690320000,"doc_count":8},{"key_as_string":"2017-02-10T01:33:00.000Z","key":1486690380000,"doc_count":8},{"key_as_string":"2017-02-10T01:34:00.000Z","key":1486690440000,"doc_count":11},{"key_as_string":"2017-02-10T01:35:00.000Z","key":1486690500000,"doc_count":7},{"key_as_string":"2017-02-10T01:36:00.000Z","key":1486690560000,"doc_count":16},{"key_as_string":"2017-02-10T01:37:00.000Z","key":1486690620000,"doc_count":9},{"key_as_string":"2017-02-10T01:38:00.000Z","key":1486690680000,"doc_count":3},{"key_as_string":"2017-02-10T01:39:00.000Z","key":1486690740000,"doc_count":14},{"key_as_string":"2017-02-10T01:40:00.000Z","key":1486690800000,"doc_count":3},{"key_as_string":"2017-02-10T01:41:00.000Z","key":1486690860000,"doc_count":14},{"key_as_string":"2017-02-10T01:42:00.000Z","key":1486690920000,"doc_count":11},{"key_as_string":"2017-02-10T01:43:00.000Z","key":1486690980000,"doc_count":8},{"key_as_string":"2017-02-10T01:44:00.000Z","key":1486691040000,"doc_count":13},{"key_as_string":"2017-02-10T01:45:00.000Z","key":1486691100000,"doc_count":6},{"key_as_string":"2017-02-10T01:46:00.000Z","key":1486691160000,"doc_count":16},{"key_as_string":"2017-02-10T01:47:00.000Z","key":1486691220000,"doc_count":12},{"key_as_string":"2017-02-10T01:48:00.000Z","key":1486691280000,"doc_count":8},{"key_as_string":"2017-02-10T01:49:00.000Z","key":1486691340000,"doc_count":17},{"key_as_string":"2017-02-10T01:50:00.000Z","key":1486691400000,"doc_count":9},{"key_as_string":"2017-02-10T01:51:00.000Z","key":1486691460000,"doc_count":11},{"key_as_string":"2017-02-10T01:52:00.000Z","key":1486691520000,"doc_count":10},{"key_as_string":"2017-02-10T01:53:00.000Z","key":1486691580000,"doc_count":13},{"key_as_string":"2017-02-10T01:54:00.000Z","key":1486691640000,"doc_count":11},{"key_as_string":"2017-02-10T01:55:00.000Z","key":1486691700000,"doc_count":9},{"key_as_string":"2017-02-10T01:56:00.000Z","key":1486691760000,"doc_count":11},{"key_as_string":"2017-02-10T01:57:00.000Z","key":1486691820000,"doc_count":8},{"key_as_string":"2017-02-10T01:58:00.000Z","key":1486691880000,"doc_count":14},{"key_as_string":"2017-02-10T01:59:00.000Z","key":1486691940000,"doc_count":8},{"key_as_string":"2017-02-10T02:00:00.000Z","key":1486692000000,"doc_count":4},{"key_as_string":"2017-02-10T02:01:00.000Z","key":1486692060000,"doc_count":13},{"key_as_string":"2017-02-10T02:02:00.000Z","key":1486692120000,"doc_count":8},{"key_as_string":"2017-02-10T02:03:00.000Z","key":1486692180000,"doc_count":9},{"key_as_string":"2017-02-10T02:04:00.000Z","key":1486692240000,"doc_count":9},{"key_as_string":"2017-02-10T02:05:00.000Z","key":1486692300000,"doc_count":14},{"key_as_string":"2017-02-10T02:06:00.000Z","key":1486692360000,"doc_count":7},{"key_as_string":"2017-02-10T02:07:00.000Z","key":1486692420000,"doc_count":9},{"key_as_string":"2017-02-10T02:08:00.000Z","key":1486692480000,"doc_count":16},{"key_as_string":"2017-02-10T02:09:00.000Z","key":1486692540000,"doc_count":10},{"key_as_string":"2017-02-10T02:10:00.000Z","key":1486692600000,"doc_count":11},{"key_as_string":"2017-02-10T02:11:00.000Z","key":1486692660000,"doc_count":7},{"key_as_string":"2017-02-10T02:12:00.000Z","key":1486692720000,"doc_count":12},{"key_as_string":"2017-02-10T02:13:00.000Z","key":1486692780000,"doc_count":11},{"key_as_string":"2017-02-10T02:14:00.000Z","key":1486692840000,"doc_count":5},{"key_as_string":"2017-02-10T02:15:00.000Z","key":1486692900000,"doc_count":14},{"key_as_string":"2017-02-10T02:16:00.000Z","key":1486692960000,"doc_count":13},{"key_as_string":"2017-02-10T02:17:00.000Z","key":1486693020000,"doc_count":7},{"key_as_string":"2017-02-10T02:18:00.000Z","key":1486693080000,"doc_count":11},{"key_as_string":"2017-02-10T02:19:00.000Z","key":1486693140000,"doc_count":10},{"key_as_string":"2017-02-10T02:20:00.000Z","key":1486693200000,"doc_count":8},{"key_as_string":"2017-02-10T02:21:00.000Z","key":1486693260000,"doc_count":9},{"key_as_string":"2017-02-10T02:22:00.000Z","key":1486693320000,"doc_count":12},{"key_as_string":"2017-02-10T02:23:00.000Z","key":1486693380000,"doc_count":14},{"key_as_string":"2017-02-10T02:24:00.000Z","key":1486693440000,"doc_count":8},{"key_as_string":"2017-02-10T02:25:00.000Z","key":1486693500000,"doc_count":10},{"key_as_string":"2017-02-10T02:26:00.000Z","key":1486693560000,"doc_count":10},{"key_as_string":"2017-02-10T02:27:00.000Z","key":1486693620000,"doc_count":8},{"key_as_string":"2017-02-10T02:28:00.000Z","key":1486693680000,"doc_count":7},{"key_as_string":"2017-02-10T02:29:00.000Z","key":1486693740000,"doc_count":7},{"key_as_string":"2017-02-10T02:30:00.000Z","key":1486693800000,"doc_count":13},{"key_as_string":"2017-02-10T02:31:00.000Z","key":1486693860000,"doc_count":7},{"key_as_string":"2017-02-10T02:32:00.000Z","key":1486693920000,"doc_count":14},{"key_as_string":"2017-02-10T02:33:00.000Z","key":1486693980000,"doc_count":8},{"key_as_string":"2017-02-10T02:34:00.000Z","key":1486694040000,"doc_count":10},{"key_as_string":"2017-02-10T02:35:00.000Z","key":1486694100000,"doc_count":10},{"key_as_string":"2017-02-10T02:36:00.000Z","key":1486694160000,"doc_count":10},{"key_as_string":"2017-02-10T02:37:00.000Z","key":1486694220000,"doc_count":13},{"key_as_string":"2017-02-10T02:38:00.000Z","key":1486694280000,"doc_count":7},{"key_as_string":"2017-02-10T02:39:00.000Z","key":1486694340000,"doc_count":13},{"key_as_string":"2017-02-10T02:40:00.000Z","key":1486694400000,"doc_count":14},{"key_as_string":"2017-02-10T02:41:00.000Z","key":1486694460000,"doc_count":12},{"key_as_string":"2017-02-10T02:42:00.000Z","key":1486694520000,"doc_count":9},{"key_as_string":"2017-02-10T02:43:00.000Z","key":1486694580000,"doc_count":9},{"key_as_string":"2017-02-10T02:44:00.000Z","key":1486694640000,"doc_count":10},{"key_as_string":"2017-02-10T02:45:00.000Z","key":1486694700000,"doc_count":15},{"key_as_string":"2017-02-10T02:46:00.000Z","key":1486694760000,"doc_count":8},{"key_as_string":"2017-02-10T02:47:00.000Z","key":1486694820000,"doc_count":11},{"key_as_string":"2017-02-10T02:48:00.000Z","key":1486694880000,"doc_count":12},{"key_as_string":"2017-02-10T02:49:00.000Z","key":1486694940000,"doc_count":10},{"key_as_string":"2017-02-10T02:50:00.000Z","key":1486695000000,"doc_count":9},{"key_as_string":"2017-02-10T02:51:00.000Z","key":1486695060000,"doc_count":16},{"key_as_string":"2017-02-10T02:52:00.000Z","key":1486695120000,"doc_count":8},{"key_as_string":"2017-02-10T02:53:00.000Z","key":1486695180000,"doc_count":9},{"key_as_string":"2017-02-10T02:54:00.000Z","key":1486695240000,"doc_count":15},{"key_as_string":"2017-02-10T02:55:00.000Z","key":1486695300000,"doc_count":9},{"key_as_string":"2017-02-10T02:56:00.000Z","key":1486695360000,"doc_count":9},{"key_as_string":"2017-02-10T02:57:00.000Z","key":1486695420000,"doc_count":9},{"key_as_string":"2017-02-10T02:58:00.000Z","key":1486695480000,"doc_count":10},{"key_as_string":"2017-02-10T02:59:00.000Z","key":1486695540000,"doc_count":10},{"key_as_string":"2017-02-10T03:00:00.000Z","key":1486695600000,"doc_count":16},{"key_as_string":"2017-02-10T03:01:00.000Z","key":1486695660000,"doc_count":7},{"key_as_string":"2017-02-10T03:02:00.000Z","key":1486695720000,"doc_count":11},{"key_as_string":"2017-02-10T03:03:00.000Z","key":1486695780000,"doc_count":15},{"key_as_string":"2017-02-10T03:04:00.000Z","key":1486695840000,"doc_count":7},{"key_as_string":"2017-02-10T03:05:00.000Z","key":1486695900000,"doc_count":9},{"key_as_string":"2017-02-10T03:06:00.000Z","key":1486695960000,"doc_count":9},{"key_as_string":"2017-02-10T03:07:00.000Z","key":1486696020000,"doc_count":10},{"key_as_string":"2017-02-10T03:08:00.000Z","key":1486696080000,"doc_count":10},{"key_as_string":"2017-02-10T03:09:00.000Z","key":1486696140000,"doc_count":18},{"key_as_string":"2017-02-10T03:10:00.000Z","key":1486696200000,"doc_count":13},{"key_as_string":"2017-02-10T03:11:00.000Z","key":1486696260000,"doc_count":8},{"key_as_string":"2017-02-10T03:12:00.000Z","key":1486696320000,"doc_count":11},{"key_as_string":"2017-02-10T03:13:00.000Z","key":1486696380000,"doc_count":9},{"key_as_string":"2017-02-10T03:14:00.000Z","key":1486696440000,"doc_count":10},{"key_as_string":"2017-02-10T03:15:00.000Z","key":1486696500000,"doc_count":16},{"key_as_string":"2017-02-10T03:16:00.000Z","key":1486696560000,"doc_count":10},{"key_as_string":"2017-02-10T03:17:00.000Z","key":1486696620000,"doc_count":13},{"key_as_string":"2017-02-10T03:18:00.000Z","key":1486696680000,"doc_count":7},{"key_as_string":"2017-02-10T03:19:00.000Z","key":1486696740000,"doc_count":13},{"key_as_string":"2017-02-10T03:20:00.000Z","key":1486696800000,"doc_count":9},{"key_as_string":"2017-02-10T03:21:00.000Z","key":1486696860000,"doc_count":8},{"key_as_string":"2017-02-10T03:22:00.000Z","key":1486696920000,"doc_count":16},{"key_as_string":"2017-02-10T03:23:00.000Z","key":1486696980000,"doc_count":9},{"key_as_string":"2017-02-10T03:24:00.000Z","key":1486697040000,"doc_count":14},{"key_as_string":"2017-02-10T03:25:00.000Z","key":1486697100000,"doc_count":11},{"key_as_string":"2017-02-10T03:26:00.000Z","key":1486697160000,"doc_count":11},{"key_as_string":"2017-02-10T03:27:00.000Z","key":1486697220000,"doc_count":9},{"key_as_string":"2017-02-10T03:28:00.000Z","key":1486697280000,"doc_count":9},{"key_as_string":"2017-02-10T03:29:00.000Z","key":1486697340000,"doc_count":11},{"key_as_string":"2017-02-10T03:30:00.000Z","key":1486697400000,"doc_count":7},{"key_as_string":"2017-02-10T03:31:00.000Z","key":1486697460000,"doc_count":14},{"key_as_string":"2017-02-10T03:32:00.000Z","key":1486697520000,"doc_count":7},{"key_as_string":"2017-02-10T03:33:00.000Z","key":1486697580000,"doc_count":14},{"key_as_string":"2017-02-10T03:34:00.000Z","key":1486697640000,"doc_count":8},{"key_as_string":"2017-02-10T03:35:00.000Z","key":1486697700000,"doc_count":8},{"key_as_string":"2017-02-10T03:36:00.000Z","key":1486697760000,"doc_count":15},{"key_as_string":"2017-02-10T03:37:00.000Z","key":1486697820000,"doc_count":9},{"key_as_string":"2017-02-10T03:38:00.000Z","key":1486697880000,"doc_count":12},{"key_as_string":"2017-02-10T03:39:00.000Z","key":1486697940000,"doc_count":15},{"key_as_string":"2017-02-10T03:40:00.000Z","key":1486698000000,"doc_count":12},{"key_as_string":"2017-02-10T03:41:00.000Z","key":1486698060000,"doc_count":12},{"key_as_string":"2017-02-10T03:42:00.000Z","key":1486698120000,"doc_count":10},{"key_as_string":"2017-02-10T03:43:00.000Z","key":1486698180000,"doc_count":11},{"key_as_string":"2017-02-10T03:44:00.000Z","key":1486698240000,"doc_count":12},{"key_as_string":"2017-02-10T03:45:00.000Z","key":1486698300000,"doc_count":9},{"key_as_string":"2017-02-10T03:46:00.000Z","key":1486698360000,"doc_count":13},{"key_as_string":"2017-02-10T03:47:00.000Z","key":1486698420000,"doc_count":8},{"key_as_string":"2017-02-10T03:48:00.000Z","key":1486698480000,"doc_count":15},{"key_as_string":"2017-02-10T03:49:00.000Z","key":1486698540000,"doc_count":8},{"key_as_string":"2017-02-10T03:50:00.000Z","key":1486698600000,"doc_count":12},{"key_as_string":"2017-02-10T03:51:00.000Z","key":1486698660000,"doc_count":16},{"key_as_string":"2017-02-10T03:52:00.000Z","key":1486698720000,"doc_count":7},{"key_as_string":"2017-02-10T03:53:00.000Z","key":1486698780000,"doc_count":12},{"key_as_string":"2017-02-10T03:54:00.000Z","key":1486698840000,"doc_count":8},{"key_as_string":"2017-02-10T03:55:00.000Z","key":1486698900000,"doc_count":7},{"key_as_string":"2017-02-10T03:56:00.000Z","key":1486698960000,"doc_count":13},{"key_as_string":"2017-02-10T03:57:00.000Z","key":1486699020000,"doc_count":8},{"key_as_string":"2017-02-10T03:58:00.000Z","key":1486699080000,"doc_count":10},{"key_as_string":"2017-02-10T03:59:00.000Z","key":1486699140000,"doc_count":13},{"key_as_string":"2017-02-10T04:00:00.000Z","key":1486699200000,"doc_count":11},{"key_as_string":"2017-02-10T04:01:00.000Z","key":1486699260000,"doc_count":10},{"key_as_string":"2017-02-10T04:02:00.000Z","key":1486699320000,"doc_count":12},{"key_as_string":"2017-02-10T04:03:00.000Z","key":1486699380000,"doc_count":9},{"key_as_string":"2017-02-10T04:04:00.000Z","key":1486699440000,"doc_count":13},{"key_as_string":"2017-02-10T04:05:00.000Z","key":1486699500000,"doc_count":8},{"key_as_string":"2017-02-10T04:06:00.000Z","key":1486699560000,"doc_count":15},{"key_as_string":"2017-02-10T04:07:00.000Z","key":1486699620000,"doc_count":11},{"key_as_string":"2017-02-10T04:08:00.000Z","key":1486699680000,"doc_count":11},{"key_as_string":"2017-02-10T04:09:00.000Z","key":1486699740000,"doc_count":16},{"key_as_string":"2017-02-10T04:10:00.000Z","key":1486699800000,"doc_count":8},{"key_as_string":"2017-02-10T04:11:00.000Z","key":1486699860000,"doc_count":15},{"key_as_string":"2017-02-10T04:12:00.000Z","key":1486699920000,"doc_count":8},{"key_as_string":"2017-02-10T04:13:00.000Z","key":1486699980000,"doc_count":11},{"key_as_string":"2017-02-10T04:14:00.000Z","key":1486700040000,"doc_count":11},{"key_as_string":"2017-02-10T04:15:00.000Z","key":1486700100000,"doc_count":13},{"key_as_string":"2017-02-10T04:16:00.000Z","key":1486700160000,"doc_count":6},{"key_as_string":"2017-02-10T04:17:00.000Z","key":1486700220000,"doc_count":11},{"key_as_string":"2017-02-10T04:18:00.000Z","key":1486700280000,"doc_count":11},{"key_as_string":"2017-02-10T04:19:00.000Z","key":1486700340000,"doc_count":9},{"key_as_string":"2017-02-10T04:20:00.000Z","key":1486700400000,"doc_count":17},{"key_as_string":"2017-02-10T04:21:00.000Z","key":1486700460000,"doc_count":7},{"key_as_string":"2017-02-10T04:22:00.000Z","key":1486700520000,"doc_count":14},{"key_as_string":"2017-02-10T04:23:00.000Z","key":1486700580000,"doc_count":13},{"key_as_string":"2017-02-10T04:24:00.000Z","key":1486700640000,"doc_count":7},{"key_as_string":"2017-02-10T04:25:00.000Z","key":1486700700000,"doc_count":11},{"key_as_string":"2017-02-10T04:26:00.000Z","key":1486700760000,"doc_count":12},{"key_as_string":"2017-02-10T04:27:00.000Z","key":1486700820000,"doc_count":11},{"key_as_string":"2017-02-10T04:28:00.000Z","key":1486700880000,"doc_count":9},{"key_as_string":"2017-02-10T04:29:00.000Z","key":1486700940000,"doc_count":9},{"key_as_string":"2017-02-10T04:30:00.000Z","key":1486701000000,"doc_count":10},{"key_as_string":"2017-02-10T04:31:00.000Z","key":1486701060000,"doc_count":10},{"key_as_string":"2017-02-10T04:32:00.000Z","key":1486701120000,"doc_count":10},{"key_as_string":"2017-02-10T04:33:00.000Z","key":1486701180000,"doc_count":11},{"key_as_string":"2017-02-10T04:34:00.000Z","key":1486701240000,"doc_count":13},{"key_as_string":"2017-02-10T04:35:00.000Z","key":1486701300000,"doc_count":6},{"key_as_string":"2017-02-10T04:36:00.000Z","key":1486701360000,"doc_count":8},{"key_as_string":"2017-02-10T04:37:00.000Z","key":1486701420000,"doc_count":14},{"key_as_string":"2017-02-10T04:38:00.000Z","key":1486701480000,"doc_count":9},{"key_as_string":"2017-02-10T04:39:00.000Z","key":1486701540000,"doc_count":12},{"key_as_string":"2017-02-10T04:40:00.000Z","key":1486701600000,"doc_count":10},{"key_as_string":"2017-02-10T04:41:00.000Z","key":1486701660000,"doc_count":9},{"key_as_string":"2017-02-10T04:42:00.000Z","key":1486701720000,"doc_count":10},{"key_as_string":"2017-02-10T04:43:00.000Z","key":1486701780000,"doc_count":9},{"key_as_string":"2017-02-10T04:44:00.000Z","key":1486701840000,"doc_count":11},{"key_as_string":"2017-02-10T04:45:00.000Z","key":1486701900000,"doc_count":10},{"key_as_string":"2017-02-10T04:46:00.000Z","key":1486701960000,"doc_count":19},{"key_as_string":"2017-02-10T04:47:00.000Z","key":1486702020000,"doc_count":11},{"key_as_string":"2017-02-10T04:48:00.000Z","key":1486702080000,"doc_count":10},{"key_as_string":"2017-02-10T04:49:00.000Z","key":1486702140000,"doc_count":10},{"key_as_string":"2017-02-10T04:50:00.000Z","key":1486702200000,"doc_count":10},{"key_as_string":"2017-02-10T04:51:00.000Z","key":1486702260000,"doc_count":11},{"key_as_string":"2017-02-10T04:52:00.000Z","key":1486702320000,"doc_count":17},{"key_as_string":"2017-02-10T04:53:00.000Z","key":1486702380000,"doc_count":6},{"key_as_string":"2017-02-10T04:54:00.000Z","key":1486702440000,"doc_count":7},{"key_as_string":"2017-02-10T04:55:00.000Z","key":1486702500000,"doc_count":10},{"key_as_string":"2017-02-10T04:56:00.000Z","key":1486702560000,"doc_count":10},{"key_as_string":"2017-02-10T04:57:00.000Z","key":1486702620000,"doc_count":10},{"key_as_string":"2017-02-10T04:58:00.000Z","key":1486702680000,"doc_count":11},{"key_as_string":"2017-02-10T04:59:00.000Z","key":1486702740000,"doc_count":10},{"key_as_string":"2017-02-10T05:00:00.000Z","key":1486702800000,"doc_count":8},{"key_as_string":"2017-02-10T05:01:00.000Z","key":1486702860000,"doc_count":13},{"key_as_string":"2017-02-10T05:02:00.000Z","key":1486702920000,"doc_count":11},{"key_as_string":"2017-02-10T05:03:00.000Z","key":1486702980000,"doc_count":7},{"key_as_string":"2017-02-10T05:04:00.000Z","key":1486703040000,"doc_count":10},{"key_as_string":"2017-02-10T05:05:00.000Z","key":1486703100000,"doc_count":10},{"key_as_string":"2017-02-10T05:06:00.000Z","key":1486703160000,"doc_count":10},{"key_as_string":"2017-02-10T05:07:00.000Z","key":1486703220000,"doc_count":9},{"key_as_string":"2017-02-10T05:08:00.000Z","key":1486703280000,"doc_count":9},{"key_as_string":"2017-02-10T05:09:00.000Z","key":1486703340000,"doc_count":15},{"key_as_string":"2017-02-10T05:10:00.000Z","key":1486703400000,"doc_count":12},{"key_as_string":"2017-02-10T05:11:00.000Z","key":1486703460000,"doc_count":11},{"key_as_string":"2017-02-10T05:12:00.000Z","key":1486703520000,"doc_count":11},{"key_as_string":"2017-02-10T05:13:00.000Z","key":1486703580000,"doc_count":10},{"key_as_string":"2017-02-10T05:14:00.000Z","key":1486703640000,"doc_count":14},{"key_as_string":"2017-02-10T05:15:00.000Z","key":1486703700000,"doc_count":9},{"key_as_string":"2017-02-10T05:16:00.000Z","key":1486703760000,"doc_count":11},{"key_as_string":"2017-02-10T05:17:00.000Z","key":1486703820000,"doc_count":8},{"key_as_string":"2017-02-10T05:18:00.000Z","key":1486703880000,"doc_count":11},{"key_as_string":"2017-02-10T05:19:00.000Z","key":1486703940000,"doc_count":7},{"key_as_string":"2017-02-10T05:20:00.000Z","key":1486704000000,"doc_count":13},{"key_as_string":"2017-02-10T05:21:00.000Z","key":1486704060000,"doc_count":10},{"key_as_string":"2017-02-10T05:22:00.000Z","key":1486704120000,"doc_count":13},{"key_as_string":"2017-02-10T05:23:00.000Z","key":1486704180000,"doc_count":11},{"key_as_string":"2017-02-10T05:24:00.000Z","key":1486704240000,"doc_count":10},{"key_as_string":"2017-02-10T05:25:00.000Z","key":1486704300000,"doc_count":16},{"key_as_string":"2017-02-10T05:26:00.000Z","key":1486704360000,"doc_count":10},{"key_as_string":"2017-02-10T05:27:00.000Z","key":1486704420000,"doc_count":12},{"key_as_string":"2017-02-10T05:28:00.000Z","key":1486704480000,"doc_count":15},{"key_as_string":"2017-02-10T05:29:00.000Z","key":1486704540000,"doc_count":9},{"key_as_string":"2017-02-10T05:30:00.000Z","key":1486704600000,"doc_count":11},{"key_as_string":"2017-02-10T05:31:00.000Z","key":1486704660000,"doc_count":8},{"key_as_string":"2017-02-10T05:32:00.000Z","key":1486704720000,"doc_count":10},{"key_as_string":"2017-02-10T05:33:00.000Z","key":1486704780000,"doc_count":12},{"key_as_string":"2017-02-10T05:34:00.000Z","key":1486704840000,"doc_count":11},{"key_as_string":"2017-02-10T05:35:00.000Z","key":1486704900000,"doc_count":8},{"key_as_string":"2017-02-10T05:36:00.000Z","key":1486704960000,"doc_count":12},{"key_as_string":"2017-02-10T05:37:00.000Z","key":1486705020000,"doc_count":8},{"key_as_string":"2017-02-10T05:38:00.000Z","key":1486705080000,"doc_count":8},{"key_as_string":"2017-02-10T05:39:00.000Z","key":1486705140000,"doc_count":12},{"key_as_string":"2017-02-10T05:40:00.000Z","key":1486705200000,"doc_count":14},{"key_as_string":"2017-02-10T05:41:00.000Z","key":1486705260000,"doc_count":15},{"key_as_string":"2017-02-10T05:42:00.000Z","key":1486705320000,"doc_count":5},{"key_as_string":"2017-02-10T05:43:00.000Z","key":1486705380000,"doc_count":11},{"key_as_string":"2017-02-10T05:44:00.000Z","key":1486705440000,"doc_count":12},{"key_as_string":"2017-02-10T05:45:00.000Z","key":1486705500000,"doc_count":6},{"key_as_string":"2017-02-10T05:46:00.000Z","key":1486705560000,"doc_count":12},{"key_as_string":"2017-02-10T05:47:00.000Z","key":1486705620000,"doc_count":10},{"key_as_string":"2017-02-10T05:48:00.000Z","key":1486705680000,"doc_count":12},{"key_as_string":"2017-02-10T05:49:00.000Z","key":1486705740000,"doc_count":17},{"key_as_string":"2017-02-10T05:50:00.000Z","key":1486705800000,"doc_count":10},{"key_as_string":"2017-02-10T05:51:00.000Z","key":1486705860000,"doc_count":13},{"key_as_string":"2017-02-10T05:52:00.000Z","key":1486705920000,"doc_count":13},{"key_as_string":"2017-02-10T05:53:00.000Z","key":1486705980000,"doc_count":10},{"key_as_string":"2017-02-10T05:54:00.000Z","key":1486706040000,"doc_count":10},{"key_as_string":"2017-02-10T05:55:00.000Z","key":1486706100000,"doc_count":15},{"key_as_string":"2017-02-10T05:56:00.000Z","key":1486706160000,"doc_count":8},{"key_as_string":"2017-02-10T05:57:00.000Z","key":1486706220000,"doc_count":10},{"key_as_string":"2017-02-10T05:58:00.000Z","key":1486706280000,"doc_count":11},{"key_as_string":"2017-02-10T05:59:00.000Z","key":1486706340000,"doc_count":10},{"key_as_string":"2017-02-10T06:00:00.000Z","key":1486706400000,"doc_count":19},{"key_as_string":"2017-02-10T06:01:00.000Z","key":1486706460000,"doc_count":13},{"key_as_string":"2017-02-10T06:02:00.000Z","key":1486706520000,"doc_count":14},{"key_as_string":"2017-02-10T06:03:00.000Z","key":1486706580000,"doc_count":11},{"key_as_string":"2017-02-10T06:04:00.000Z","key":1486706640000,"doc_count":13},{"key_as_string":"2017-02-10T06:05:00.000Z","key":1486706700000,"doc_count":6},{"key_as_string":"2017-02-10T06:06:00.000Z","key":1486706760000,"doc_count":12},{"key_as_string":"2017-02-10T06:07:00.000Z","key":1486706820000,"doc_count":14},{"key_as_string":"2017-02-10T06:08:00.000Z","key":1486706880000,"doc_count":12},{"key_as_string":"2017-02-10T06:09:00.000Z","key":1486706940000,"doc_count":14},{"key_as_string":"2017-02-10T06:10:00.000Z","key":1486707000000,"doc_count":8},{"key_as_string":"2017-02-10T06:11:00.000Z","key":1486707060000,"doc_count":6},{"key_as_string":"2017-02-10T06:12:00.000Z","key":1486707120000,"doc_count":17},{"key_as_string":"2017-02-10T06:13:00.000Z","key":1486707180000,"doc_count":14},{"key_as_string":"2017-02-10T06:14:00.000Z","key":1486707240000,"doc_count":5},{"key_as_string":"2017-02-10T06:15:00.000Z","key":1486707300000,"doc_count":17},{"key_as_string":"2017-02-10T06:16:00.000Z","key":1486707360000,"doc_count":9},{"key_as_string":"2017-02-10T06:17:00.000Z","key":1486707420000,"doc_count":13},{"key_as_string":"2017-02-10T06:18:00.000Z","key":1486707480000,"doc_count":8},{"key_as_string":"2017-02-10T06:19:00.000Z","key":1486707540000,"doc_count":14},{"key_as_string":"2017-02-10T06:20:00.000Z","key":1486707600000,"doc_count":8},{"key_as_string":"2017-02-10T06:21:00.000Z","key":1486707660000,"doc_count":11},{"key_as_string":"2017-02-10T06:22:00.000Z","key":1486707720000,"doc_count":15},{"key_as_string":"2017-02-10T06:23:00.000Z","key":1486707780000,"doc_count":16},{"key_as_string":"2017-02-10T06:24:00.000Z","key":1486707840000,"doc_count":11},{"key_as_string":"2017-02-10T06:25:00.000Z","key":1486707900000,"doc_count":10},{"key_as_string":"2017-02-10T06:26:00.000Z","key":1486707960000,"doc_count":8},{"key_as_string":"2017-02-10T06:27:00.000Z","key":1486708020000,"doc_count":18},{"key_as_string":"2017-02-10T06:28:00.000Z","key":1486708080000,"doc_count":11},{"key_as_string":"2017-02-10T06:29:00.000Z","key":1486708140000,"doc_count":10},{"key_as_string":"2017-02-10T06:30:00.000Z","key":1486708200000,"doc_count":9},{"key_as_string":"2017-02-10T06:31:00.000Z","key":1486708260000,"doc_count":13},{"key_as_string":"2017-02-10T06:32:00.000Z","key":1486708320000,"doc_count":10},{"key_as_string":"2017-02-10T06:33:00.000Z","key":1486708380000,"doc_count":12},{"key_as_string":"2017-02-10T06:34:00.000Z","key":1486708440000,"doc_count":10},{"key_as_string":"2017-02-10T06:35:00.000Z","key":1486708500000,"doc_count":9},{"key_as_string":"2017-02-10T06:36:00.000Z","key":1486708560000,"doc_count":17},{"key_as_string":"2017-02-10T06:37:00.000Z","key":1486708620000,"doc_count":15},{"key_as_string":"2017-02-10T06:38:00.000Z","key":1486708680000,"doc_count":11},{"key_as_string":"2017-02-10T06:39:00.000Z","key":1486708740000,"doc_count":12},{"key_as_string":"2017-02-10T06:40:00.000Z","key":1486708800000,"doc_count":11},{"key_as_string":"2017-02-10T06:41:00.000Z","key":1486708860000,"doc_count":13},{"key_as_string":"2017-02-10T06:42:00.000Z","key":1486708920000,"doc_count":10},{"key_as_string":"2017-02-10T06:43:00.000Z","key":1486708980000,"doc_count":12},{"key_as_string":"2017-02-10T06:44:00.000Z","key":1486709040000,"doc_count":14},{"key_as_string":"2017-02-10T06:45:00.000Z","key":1486709100000,"doc_count":7},{"key_as_string":"2017-02-10T06:46:00.000Z","key":1486709160000,"doc_count":17},{"key_as_string":"2017-02-10T06:47:00.000Z","key":1486709220000,"doc_count":9},{"key_as_string":"2017-02-10T06:48:00.000Z","key":1486709280000,"doc_count":12},{"key_as_string":"2017-02-10T06:49:00.000Z","key":1486709340000,"doc_count":14},{"key_as_string":"2017-02-10T06:50:00.000Z","key":1486709400000,"doc_count":15},{"key_as_string":"2017-02-10T06:51:00.000Z","key":1486709460000,"doc_count":12},{"key_as_string":"2017-02-10T06:52:00.000Z","key":1486709520000,"doc_count":8},{"key_as_string":"2017-02-10T06:53:00.000Z","key":1486709580000,"doc_count":13},{"key_as_string":"2017-02-10T06:54:00.000Z","key":1486709640000,"doc_count":15},{"key_as_string":"2017-02-10T06:55:00.000Z","key":1486709700000,"doc_count":8},{"key_as_string":"2017-02-10T06:56:00.000Z","key":1486709760000,"doc_count":7},{"key_as_string":"2017-02-10T06:57:00.000Z","key":1486709820000,"doc_count":18},{"key_as_string":"2017-02-10T06:58:00.000Z","key":1486709880000,"doc_count":11},{"key_as_string":"2017-02-10T06:59:00.000Z","key":1486709940000,"doc_count":14},{"key_as_string":"2017-02-10T07:00:00.000Z","key":1486710000000,"doc_count":8},{"key_as_string":"2017-02-10T07:01:00.000Z","key":1486710060000,"doc_count":12},{"key_as_string":"2017-02-10T07:02:00.000Z","key":1486710120000,"doc_count":14},{"key_as_string":"2017-02-10T07:03:00.000Z","key":1486710180000,"doc_count":9},{"key_as_string":"2017-02-10T07:04:00.000Z","key":1486710240000,"doc_count":13},{"key_as_string":"2017-02-10T07:05:00.000Z","key":1486710300000,"doc_count":12},{"key_as_string":"2017-02-10T07:06:00.000Z","key":1486710360000,"doc_count":15},{"key_as_string":"2017-02-10T07:07:00.000Z","key":1486710420000,"doc_count":19},{"key_as_string":"2017-02-10T07:08:00.000Z","key":1486710480000,"doc_count":11},{"key_as_string":"2017-02-10T07:09:00.000Z","key":1486710540000,"doc_count":16},{"key_as_string":"2017-02-10T07:10:00.000Z","key":1486710600000,"doc_count":11},{"key_as_string":"2017-02-10T07:11:00.000Z","key":1486710660000,"doc_count":13},{"key_as_string":"2017-02-10T07:12:00.000Z","key":1486710720000,"doc_count":12},{"key_as_string":"2017-02-10T07:13:00.000Z","key":1486710780000,"doc_count":9},{"key_as_string":"2017-02-10T07:14:00.000Z","key":1486710840000,"doc_count":21},{"key_as_string":"2017-02-10T07:15:00.000Z","key":1486710900000,"doc_count":11},{"key_as_string":"2017-02-10T07:16:00.000Z","key":1486710960000,"doc_count":6},{"key_as_string":"2017-02-10T07:17:00.000Z","key":1486711020000,"doc_count":16},{"key_as_string":"2017-02-10T07:18:00.000Z","key":1486711080000,"doc_count":15},{"key_as_string":"2017-02-10T07:19:00.000Z","key":1486711140000,"doc_count":13},{"key_as_string":"2017-02-10T07:20:00.000Z","key":1486711200000,"doc_count":15},{"key_as_string":"2017-02-10T07:21:00.000Z","key":1486711260000,"doc_count":14},{"key_as_string":"2017-02-10T07:22:00.000Z","key":1486711320000,"doc_count":14},{"key_as_string":"2017-02-10T07:23:00.000Z","key":1486711380000,"doc_count":13},{"key_as_string":"2017-02-10T07:24:00.000Z","key":1486711440000,"doc_count":10},{"key_as_string":"2017-02-10T07:25:00.000Z","key":1486711500000,"doc_count":11},{"key_as_string":"2017-02-10T07:26:00.000Z","key":1486711560000,"doc_count":12},{"key_as_string":"2017-02-10T07:27:00.000Z","key":1486711620000,"doc_count":15},{"key_as_string":"2017-02-10T07:28:00.000Z","key":1486711680000,"doc_count":7},{"key_as_string":"2017-02-10T07:29:00.000Z","key":1486711740000,"doc_count":18},{"key_as_string":"2017-02-10T07:30:00.000Z","key":1486711800000,"doc_count":14},{"key_as_string":"2017-02-10T07:31:00.000Z","key":1486711860000,"doc_count":12},{"key_as_string":"2017-02-10T07:32:00.000Z","key":1486711920000,"doc_count":16},{"key_as_string":"2017-02-10T07:33:00.000Z","key":1486711980000,"doc_count":12},{"key_as_string":"2017-02-10T07:34:00.000Z","key":1486712040000,"doc_count":15},{"key_as_string":"2017-02-10T07:35:00.000Z","key":1486712100000,"doc_count":12},{"key_as_string":"2017-02-10T07:36:00.000Z","key":1486712160000,"doc_count":10},{"key_as_string":"2017-02-10T07:37:00.000Z","key":1486712220000,"doc_count":12},{"key_as_string":"2017-02-10T07:38:00.000Z","key":1486712280000,"doc_count":13},{"key_as_string":"2017-02-10T07:39:00.000Z","key":1486712340000,"doc_count":8},{"key_as_string":"2017-02-10T07:40:00.000Z","key":1486712400000,"doc_count":11},{"key_as_string":"2017-02-10T07:41:00.000Z","key":1486712460000,"doc_count":13},{"key_as_string":"2017-02-10T07:42:00.000Z","key":1486712520000,"doc_count":19},{"key_as_string":"2017-02-10T07:43:00.000Z","key":1486712580000,"doc_count":10},{"key_as_string":"2017-02-10T07:44:00.000Z","key":1486712640000,"doc_count":19},{"key_as_string":"2017-02-10T07:45:00.000Z","key":1486712700000,"doc_count":8},{"key_as_string":"2017-02-10T07:46:00.000Z","key":1486712760000,"doc_count":13},{"key_as_string":"2017-02-10T07:47:00.000Z","key":1486712820000,"doc_count":17},{"key_as_string":"2017-02-10T07:48:00.000Z","key":1486712880000,"doc_count":14},{"key_as_string":"2017-02-10T07:49:00.000Z","key":1486712940000,"doc_count":13},{"key_as_string":"2017-02-10T07:50:00.000Z","key":1486713000000,"doc_count":14},{"key_as_string":"2017-02-10T07:51:00.000Z","key":1486713060000,"doc_count":11},{"key_as_string":"2017-02-10T07:52:00.000Z","key":1486713120000,"doc_count":16},{"key_as_string":"2017-02-10T07:53:00.000Z","key":1486713180000,"doc_count":13},{"key_as_string":"2017-02-10T07:54:00.000Z","key":1486713240000,"doc_count":12},{"key_as_string":"2017-02-10T07:55:00.000Z","key":1486713300000,"doc_count":13},{"key_as_string":"2017-02-10T07:56:00.000Z","key":1486713360000,"doc_count":11},{"key_as_string":"2017-02-10T07:57:00.000Z","key":1486713420000,"doc_count":9},{"key_as_string":"2017-02-10T07:58:00.000Z","key":1486713480000,"doc_count":11},{"key_as_string":"2017-02-10T07:59:00.000Z","key":1486713540000,"doc_count":14},{"key_as_string":"2017-02-10T08:00:00.000Z","key":1486713600000,"doc_count":9},{"key_as_string":"2017-02-10T08:01:00.000Z","key":1486713660000,"doc_count":13},{"key_as_string":"2017-02-10T08:02:00.000Z","key":1486713720000,"doc_count":13},{"key_as_string":"2017-02-10T08:03:00.000Z","key":1486713780000,"doc_count":12},{"key_as_string":"2017-02-10T08:04:00.000Z","key":1486713840000,"doc_count":19},{"key_as_string":"2017-02-10T08:05:00.000Z","key":1486713900000,"doc_count":9},{"key_as_string":"2017-02-10T08:06:00.000Z","key":1486713960000,"doc_count":15},{"key_as_string":"2017-02-10T08:07:00.000Z","key":1486714020000,"doc_count":9},{"key_as_string":"2017-02-10T08:08:00.000Z","key":1486714080000,"doc_count":15},{"key_as_string":"2017-02-10T08:09:00.000Z","key":1486714140000,"doc_count":11},{"key_as_string":"2017-02-10T08:10:00.000Z","key":1486714200000,"doc_count":10},{"key_as_string":"2017-02-10T08:11:00.000Z","key":1486714260000,"doc_count":13},{"key_as_string":"2017-02-10T08:12:00.000Z","key":1486714320000,"doc_count":8},{"key_as_string":"2017-02-10T08:13:00.000Z","key":1486714380000,"doc_count":13},{"key_as_string":"2017-02-10T08:14:00.000Z","key":1486714440000,"doc_count":15},{"key_as_string":"2017-02-10T08:15:00.000Z","key":1486714500000,"doc_count":17},{"key_as_string":"2017-02-10T08:16:00.000Z","key":1486714560000,"doc_count":13},{"key_as_string":"2017-02-10T08:17:00.000Z","key":1486714620000,"doc_count":17},{"key_as_string":"2017-02-10T08:18:00.000Z","key":1486714680000,"doc_count":12},{"key_as_string":"2017-02-10T08:19:00.000Z","key":1486714740000,"doc_count":17},{"key_as_string":"2017-02-10T08:20:00.000Z","key":1486714800000,"doc_count":15},{"key_as_string":"2017-02-10T08:21:00.000Z","key":1486714860000,"doc_count":12},{"key_as_string":"2017-02-10T08:22:00.000Z","key":1486714920000,"doc_count":12},{"key_as_string":"2017-02-10T08:23:00.000Z","key":1486714980000,"doc_count":14},{"key_as_string":"2017-02-10T08:24:00.000Z","key":1486715040000,"doc_count":15},{"key_as_string":"2017-02-10T08:25:00.000Z","key":1486715100000,"doc_count":13},{"key_as_string":"2017-02-10T08:26:00.000Z","key":1486715160000,"doc_count":11},{"key_as_string":"2017-02-10T08:27:00.000Z","key":1486715220000,"doc_count":15},{"key_as_string":"2017-02-10T08:28:00.000Z","key":1486715280000,"doc_count":11},{"key_as_string":"2017-02-10T08:29:00.000Z","key":1486715340000,"doc_count":13},{"key_as_string":"2017-02-10T08:30:00.000Z","key":1486715400000,"doc_count":12},{"key_as_string":"2017-02-10T08:31:00.000Z","key":1486715460000,"doc_count":12},{"key_as_string":"2017-02-10T08:32:00.000Z","key":1486715520000,"doc_count":16},{"key_as_string":"2017-02-10T08:33:00.000Z","key":1486715580000,"doc_count":12},{"key_as_string":"2017-02-10T08:34:00.000Z","key":1486715640000,"doc_count":12},{"key_as_string":"2017-02-10T08:35:00.000Z","key":1486715700000,"doc_count":13},{"key_as_string":"2017-02-10T08:36:00.000Z","key":1486715760000,"doc_count":11},{"key_as_string":"2017-02-10T08:37:00.000Z","key":1486715820000,"doc_count":17},{"key_as_string":"2017-02-10T08:38:00.000Z","key":1486715880000,"doc_count":14},{"key_as_string":"2017-02-10T08:39:00.000Z","key":1486715940000,"doc_count":7},{"key_as_string":"2017-02-10T08:40:00.000Z","key":1486716000000,"doc_count":22},{"key_as_string":"2017-02-10T08:41:00.000Z","key":1486716060000,"doc_count":12},{"key_as_string":"2017-02-10T08:42:00.000Z","key":1486716120000,"doc_count":17},{"key_as_string":"2017-02-10T08:43:00.000Z","key":1486716180000,"doc_count":13},{"key_as_string":"2017-02-10T08:44:00.000Z","key":1486716240000,"doc_count":17},{"key_as_string":"2017-02-10T08:45:00.000Z","key":1486716300000,"doc_count":14},{"key_as_string":"2017-02-10T08:46:00.000Z","key":1486716360000,"doc_count":11},{"key_as_string":"2017-02-10T08:47:00.000Z","key":1486716420000,"doc_count":18},{"key_as_string":"2017-02-10T08:48:00.000Z","key":1486716480000,"doc_count":11},{"key_as_string":"2017-02-10T08:49:00.000Z","key":1486716540000,"doc_count":18},{"key_as_string":"2017-02-10T08:50:00.000Z","key":1486716600000,"doc_count":13},{"key_as_string":"2017-02-10T08:51:00.000Z","key":1486716660000,"doc_count":12},{"key_as_string":"2017-02-10T08:52:00.000Z","key":1486716720000,"doc_count":13},{"key_as_string":"2017-02-10T08:53:00.000Z","key":1486716780000,"doc_count":14},{"key_as_string":"2017-02-10T08:54:00.000Z","key":1486716840000,"doc_count":18},{"key_as_string":"2017-02-10T08:55:00.000Z","key":1486716900000,"doc_count":10},{"key_as_string":"2017-02-10T08:56:00.000Z","key":1486716960000,"doc_count":17},{"key_as_string":"2017-02-10T08:57:00.000Z","key":1486717020000,"doc_count":13},{"key_as_string":"2017-02-10T08:58:00.000Z","key":1486717080000,"doc_count":14},{"key_as_string":"2017-02-10T08:59:00.000Z","key":1486717140000,"doc_count":11},{"key_as_string":"2017-02-10T09:00:00.000Z","key":1486717200000,"doc_count":11},{"key_as_string":"2017-02-10T09:01:00.000Z","key":1486717260000,"doc_count":12},{"key_as_string":"2017-02-10T09:02:00.000Z","key":1486717320000,"doc_count":10},{"key_as_string":"2017-02-10T09:03:00.000Z","key":1486717380000,"doc_count":16},{"key_as_string":"2017-02-10T09:04:00.000Z","key":1486717440000,"doc_count":9},{"key_as_string":"2017-02-10T09:05:00.000Z","key":1486717500000,"doc_count":15},{"key_as_string":"2017-02-10T09:06:00.000Z","key":1486717560000,"doc_count":15},{"key_as_string":"2017-02-10T09:07:00.000Z","key":1486717620000,"doc_count":17},{"key_as_string":"2017-02-10T09:08:00.000Z","key":1486717680000,"doc_count":10},{"key_as_string":"2017-02-10T09:09:00.000Z","key":1486717740000,"doc_count":14},{"key_as_string":"2017-02-10T09:10:00.000Z","key":1486717800000,"doc_count":12},{"key_as_string":"2017-02-10T09:11:00.000Z","key":1486717860000,"doc_count":16},{"key_as_string":"2017-02-10T09:12:00.000Z","key":1486717920000,"doc_count":9},{"key_as_string":"2017-02-10T09:13:00.000Z","key":1486717980000,"doc_count":14},{"key_as_string":"2017-02-10T09:14:00.000Z","key":1486718040000,"doc_count":14},{"key_as_string":"2017-02-10T09:15:00.000Z","key":1486718100000,"doc_count":18},{"key_as_string":"2017-02-10T09:16:00.000Z","key":1486718160000,"doc_count":16},{"key_as_string":"2017-02-10T09:17:00.000Z","key":1486718220000,"doc_count":16},{"key_as_string":"2017-02-10T09:18:00.000Z","key":1486718280000,"doc_count":13},{"key_as_string":"2017-02-10T09:19:00.000Z","key":1486718340000,"doc_count":11},{"key_as_string":"2017-02-10T09:20:00.000Z","key":1486718400000,"doc_count":14},{"key_as_string":"2017-02-10T09:21:00.000Z","key":1486718460000,"doc_count":12},{"key_as_string":"2017-02-10T09:22:00.000Z","key":1486718520000,"doc_count":10},{"key_as_string":"2017-02-10T09:23:00.000Z","key":1486718580000,"doc_count":12},{"key_as_string":"2017-02-10T09:24:00.000Z","key":1486718640000,"doc_count":15},{"key_as_string":"2017-02-10T09:25:00.000Z","key":1486718700000,"doc_count":9},{"key_as_string":"2017-02-10T09:26:00.000Z","key":1486718760000,"doc_count":16},{"key_as_string":"2017-02-10T09:27:00.000Z","key":1486718820000,"doc_count":17},{"key_as_string":"2017-02-10T09:28:00.000Z","key":1486718880000,"doc_count":12},{"key_as_string":"2017-02-10T09:29:00.000Z","key":1486718940000,"doc_count":10},{"key_as_string":"2017-02-10T09:30:00.000Z","key":1486719000000,"doc_count":12},{"key_as_string":"2017-02-10T09:31:00.000Z","key":1486719060000,"doc_count":13},{"key_as_string":"2017-02-10T09:32:00.000Z","key":1486719120000,"doc_count":13},{"key_as_string":"2017-02-10T09:33:00.000Z","key":1486719180000,"doc_count":11},{"key_as_string":"2017-02-10T09:34:00.000Z","key":1486719240000,"doc_count":11},{"key_as_string":"2017-02-10T09:35:00.000Z","key":1486719300000,"doc_count":15},{"key_as_string":"2017-02-10T09:36:00.000Z","key":1486719360000,"doc_count":10},{"key_as_string":"2017-02-10T09:37:00.000Z","key":1486719420000,"doc_count":12},{"key_as_string":"2017-02-10T09:38:00.000Z","key":1486719480000,"doc_count":19},{"key_as_string":"2017-02-10T09:39:00.000Z","key":1486719540000,"doc_count":12},{"key_as_string":"2017-02-10T09:40:00.000Z","key":1486719600000,"doc_count":19},{"key_as_string":"2017-02-10T09:41:00.000Z","key":1486719660000,"doc_count":16},{"key_as_string":"2017-02-10T09:42:00.000Z","key":1486719720000,"doc_count":19},{"key_as_string":"2017-02-10T09:43:00.000Z","key":1486719780000,"doc_count":7},{"key_as_string":"2017-02-10T09:44:00.000Z","key":1486719840000,"doc_count":18},{"key_as_string":"2017-02-10T09:45:00.000Z","key":1486719900000,"doc_count":12},{"key_as_string":"2017-02-10T09:46:00.000Z","key":1486719960000,"doc_count":14},{"key_as_string":"2017-02-10T09:47:00.000Z","key":1486720020000,"doc_count":14},{"key_as_string":"2017-02-10T09:48:00.000Z","key":1486720080000,"doc_count":11},{"key_as_string":"2017-02-10T09:49:00.000Z","key":1486720140000,"doc_count":14},{"key_as_string":"2017-02-10T09:50:00.000Z","key":1486720200000,"doc_count":9},{"key_as_string":"2017-02-10T09:51:00.000Z","key":1486720260000,"doc_count":17},{"key_as_string":"2017-02-10T09:52:00.000Z","key":1486720320000,"doc_count":20},{"key_as_string":"2017-02-10T09:53:00.000Z","key":1486720380000,"doc_count":13},{"key_as_string":"2017-02-10T09:54:00.000Z","key":1486720440000,"doc_count":7},{"key_as_string":"2017-02-10T09:55:00.000Z","key":1486720500000,"doc_count":16},{"key_as_string":"2017-02-10T09:56:00.000Z","key":1486720560000,"doc_count":13},{"key_as_string":"2017-02-10T09:57:00.000Z","key":1486720620000,"doc_count":12},{"key_as_string":"2017-02-10T09:58:00.000Z","key":1486720680000,"doc_count":13},{"key_as_string":"2017-02-10T09:59:00.000Z","key":1486720740000,"doc_count":10},{"key_as_string":"2017-02-10T10:00:00.000Z","key":1486720800000,"doc_count":15},{"key_as_string":"2017-02-10T10:01:00.000Z","key":1486720860000,"doc_count":13},{"key_as_string":"2017-02-10T10:02:00.000Z","key":1486720920000,"doc_count":23},{"key_as_string":"2017-02-10T10:03:00.000Z","key":1486720980000,"doc_count":11},{"key_as_string":"2017-02-10T10:04:00.000Z","key":1486721040000,"doc_count":11},{"key_as_string":"2017-02-10T10:05:00.000Z","key":1486721100000,"doc_count":17},{"key_as_string":"2017-02-10T10:06:00.000Z","key":1486721160000,"doc_count":10},{"key_as_string":"2017-02-10T10:07:00.000Z","key":1486721220000,"doc_count":15},{"key_as_string":"2017-02-10T10:08:00.000Z","key":1486721280000,"doc_count":13},{"key_as_string":"2017-02-10T10:09:00.000Z","key":1486721340000,"doc_count":15},{"key_as_string":"2017-02-10T10:10:00.000Z","key":1486721400000,"doc_count":17},{"key_as_string":"2017-02-10T10:11:00.000Z","key":1486721460000,"doc_count":12},{"key_as_string":"2017-02-10T10:12:00.000Z","key":1486721520000,"doc_count":11},{"key_as_string":"2017-02-10T10:13:00.000Z","key":1486721580000,"doc_count":19},{"key_as_string":"2017-02-10T10:14:00.000Z","key":1486721640000,"doc_count":12},{"key_as_string":"2017-02-10T10:15:00.000Z","key":1486721700000,"doc_count":17},{"key_as_string":"2017-02-10T10:16:00.000Z","key":1486721760000,"doc_count":13},{"key_as_string":"2017-02-10T10:17:00.000Z","key":1486721820000,"doc_count":15},{"key_as_string":"2017-02-10T10:18:00.000Z","key":1486721880000,"doc_count":13},{"key_as_string":"2017-02-10T10:19:00.000Z","key":1486721940000,"doc_count":15},{"key_as_string":"2017-02-10T10:20:00.000Z","key":1486722000000,"doc_count":15},{"key_as_string":"2017-02-10T10:21:00.000Z","key":1486722060000,"doc_count":15},{"key_as_string":"2017-02-10T10:22:00.000Z","key":1486722120000,"doc_count":14},{"key_as_string":"2017-02-10T10:23:00.000Z","key":1486722180000,"doc_count":18},{"key_as_string":"2017-02-10T10:24:00.000Z","key":1486722240000,"doc_count":16},{"key_as_string":"2017-02-10T10:25:00.000Z","key":1486722300000,"doc_count":11},{"key_as_string":"2017-02-10T10:26:00.000Z","key":1486722360000,"doc_count":12},{"key_as_string":"2017-02-10T10:27:00.000Z","key":1486722420000,"doc_count":21},{"key_as_string":"2017-02-10T10:28:00.000Z","key":1486722480000,"doc_count":12},{"key_as_string":"2017-02-10T10:29:00.000Z","key":1486722540000,"doc_count":10},{"key_as_string":"2017-02-10T10:30:00.000Z","key":1486722600000,"doc_count":13},{"key_as_string":"2017-02-10T10:31:00.000Z","key":1486722660000,"doc_count":12},{"key_as_string":"2017-02-10T10:32:00.000Z","key":1486722720000,"doc_count":14},{"key_as_string":"2017-02-10T10:33:00.000Z","key":1486722780000,"doc_count":10},{"key_as_string":"2017-02-10T10:34:00.000Z","key":1486722840000,"doc_count":16},{"key_as_string":"2017-02-10T10:35:00.000Z","key":1486722900000,"doc_count":10},{"key_as_string":"2017-02-10T10:36:00.000Z","key":1486722960000,"doc_count":19},{"key_as_string":"2017-02-10T10:37:00.000Z","key":1486723020000,"doc_count":6},{"key_as_string":"2017-02-10T10:38:00.000Z","key":1486723080000,"doc_count":21},{"key_as_string":"2017-02-10T10:39:00.000Z","key":1486723140000,"doc_count":13},{"key_as_string":"2017-02-10T10:40:00.000Z","key":1486723200000,"doc_count":18},{"key_as_string":"2017-02-10T10:41:00.000Z","key":1486723260000,"doc_count":12},{"key_as_string":"2017-02-10T10:42:00.000Z","key":1486723320000,"doc_count":16},{"key_as_string":"2017-02-10T10:43:00.000Z","key":1486723380000,"doc_count":6},{"key_as_string":"2017-02-10T10:44:00.000Z","key":1486723440000,"doc_count":16},{"key_as_string":"2017-02-10T10:45:00.000Z","key":1486723500000,"doc_count":10},{"key_as_string":"2017-02-10T10:46:00.000Z","key":1486723560000,"doc_count":17},{"key_as_string":"2017-02-10T10:47:00.000Z","key":1486723620000,"doc_count":11},{"key_as_string":"2017-02-10T10:48:00.000Z","key":1486723680000,"doc_count":17},{"key_as_string":"2017-02-10T10:49:00.000Z","key":1486723740000,"doc_count":11},{"key_as_string":"2017-02-10T10:50:00.000Z","key":1486723800000,"doc_count":12},{"key_as_string":"2017-02-10T10:51:00.000Z","key":1486723860000,"doc_count":12},{"key_as_string":"2017-02-10T10:52:00.000Z","key":1486723920000,"doc_count":18},{"key_as_string":"2017-02-10T10:53:00.000Z","key":1486723980000,"doc_count":15},{"key_as_string":"2017-02-10T10:54:00.000Z","key":1486724040000,"doc_count":15},{"key_as_string":"2017-02-10T10:55:00.000Z","key":1486724100000,"doc_count":16},{"key_as_string":"2017-02-10T10:56:00.000Z","key":1486724160000,"doc_count":8},{"key_as_string":"2017-02-10T10:57:00.000Z","key":1486724220000,"doc_count":19},{"key_as_string":"2017-02-10T10:58:00.000Z","key":1486724280000,"doc_count":12},{"key_as_string":"2017-02-10T10:59:00.000Z","key":1486724340000,"doc_count":16},{"key_as_string":"2017-02-10T11:00:00.000Z","key":1486724400000,"doc_count":16},{"key_as_string":"2017-02-10T11:01:00.000Z","key":1486724460000,"doc_count":11},{"key_as_string":"2017-02-10T11:02:00.000Z","key":1486724520000,"doc_count":11},{"key_as_string":"2017-02-10T11:03:00.000Z","key":1486724580000,"doc_count":16},{"key_as_string":"2017-02-10T11:04:00.000Z","key":1486724640000,"doc_count":15},{"key_as_string":"2017-02-10T11:05:00.000Z","key":1486724700000,"doc_count":12},{"key_as_string":"2017-02-10T11:06:00.000Z","key":1486724760000,"doc_count":20},{"key_as_string":"2017-02-10T11:07:00.000Z","key":1486724820000,"doc_count":14},{"key_as_string":"2017-02-10T11:08:00.000Z","key":1486724880000,"doc_count":11},{"key_as_string":"2017-02-10T11:09:00.000Z","key":1486724940000,"doc_count":11},{"key_as_string":"2017-02-10T11:10:00.000Z","key":1486725000000,"doc_count":11},{"key_as_string":"2017-02-10T11:11:00.000Z","key":1486725060000,"doc_count":16},{"key_as_string":"2017-02-10T11:12:00.000Z","key":1486725120000,"doc_count":12},{"key_as_string":"2017-02-10T11:13:00.000Z","key":1486725180000,"doc_count":15},{"key_as_string":"2017-02-10T11:14:00.000Z","key":1486725240000,"doc_count":12},{"key_as_string":"2017-02-10T11:15:00.000Z","key":1486725300000,"doc_count":12},{"key_as_string":"2017-02-10T11:16:00.000Z","key":1486725360000,"doc_count":17},{"key_as_string":"2017-02-10T11:17:00.000Z","key":1486725420000,"doc_count":15},{"key_as_string":"2017-02-10T11:18:00.000Z","key":1486725480000,"doc_count":12},{"key_as_string":"2017-02-10T11:19:00.000Z","key":1486725540000,"doc_count":13},{"key_as_string":"2017-02-10T11:20:00.000Z","key":1486725600000,"doc_count":12},{"key_as_string":"2017-02-10T11:21:00.000Z","key":1486725660000,"doc_count":12},{"key_as_string":"2017-02-10T11:22:00.000Z","key":1486725720000,"doc_count":9},{"key_as_string":"2017-02-10T11:23:00.000Z","key":1486725780000,"doc_count":15},{"key_as_string":"2017-02-10T11:24:00.000Z","key":1486725840000,"doc_count":21},{"key_as_string":"2017-02-10T11:25:00.000Z","key":1486725900000,"doc_count":13},{"key_as_string":"2017-02-10T11:26:00.000Z","key":1486725960000,"doc_count":15},{"key_as_string":"2017-02-10T11:27:00.000Z","key":1486726020000,"doc_count":14},{"key_as_string":"2017-02-10T11:28:00.000Z","key":1486726080000,"doc_count":10},{"key_as_string":"2017-02-10T11:29:00.000Z","key":1486726140000,"doc_count":16},{"key_as_string":"2017-02-10T11:30:00.000Z","key":1486726200000,"doc_count":12},{"key_as_string":"2017-02-10T11:31:00.000Z","key":1486726260000,"doc_count":18},{"key_as_string":"2017-02-10T11:32:00.000Z","key":1486726320000,"doc_count":11},{"key_as_string":"2017-02-10T11:33:00.000Z","key":1486726380000,"doc_count":15},{"key_as_string":"2017-02-10T11:34:00.000Z","key":1486726440000,"doc_count":16},{"key_as_string":"2017-02-10T11:35:00.000Z","key":1486726500000,"doc_count":19},{"key_as_string":"2017-02-10T11:36:00.000Z","key":1486726560000,"doc_count":9},{"key_as_string":"2017-02-10T11:37:00.000Z","key":1486726620000,"doc_count":14},{"key_as_string":"2017-02-10T11:38:00.000Z","key":1486726680000,"doc_count":12},{"key_as_string":"2017-02-10T11:39:00.000Z","key":1486726740000,"doc_count":15},{"key_as_string":"2017-02-10T11:40:00.000Z","key":1486726800000,"doc_count":13},{"key_as_string":"2017-02-10T11:41:00.000Z","key":1486726860000,"doc_count":14},{"key_as_string":"2017-02-10T11:42:00.000Z","key":1486726920000,"doc_count":11},{"key_as_string":"2017-02-10T11:43:00.000Z","key":1486726980000,"doc_count":16},{"key_as_string":"2017-02-10T11:44:00.000Z","key":1486727040000,"doc_count":10},{"key_as_string":"2017-02-10T11:45:00.000Z","key":1486727100000,"doc_count":12},{"key_as_string":"2017-02-10T11:46:00.000Z","key":1486727160000,"doc_count":9},{"key_as_string":"2017-02-10T11:47:00.000Z","key":1486727220000,"doc_count":16},{"key_as_string":"2017-02-10T11:48:00.000Z","key":1486727280000,"doc_count":8},{"key_as_string":"2017-02-10T11:49:00.000Z","key":1486727340000,"doc_count":13},{"key_as_string":"2017-02-10T11:50:00.000Z","key":1486727400000,"doc_count":16},{"key_as_string":"2017-02-10T11:51:00.000Z","key":1486727460000,"doc_count":13},{"key_as_string":"2017-02-10T11:52:00.000Z","key":1486727520000,"doc_count":12},{"key_as_string":"2017-02-10T11:53:00.000Z","key":1486727580000,"doc_count":23},{"key_as_string":"2017-02-10T11:54:00.000Z","key":1486727640000,"doc_count":11},{"key_as_string":"2017-02-10T11:55:00.000Z","key":1486727700000,"doc_count":12},{"key_as_string":"2017-02-10T11:56:00.000Z","key":1486727760000,"doc_count":14},{"key_as_string":"2017-02-10T11:57:00.000Z","key":1486727820000,"doc_count":15},{"key_as_string":"2017-02-10T11:58:00.000Z","key":1486727880000,"doc_count":14},{"key_as_string":"2017-02-10T11:59:00.000Z","key":1486727940000,"doc_count":14},{"key_as_string":"2017-02-10T12:00:00.000Z","key":1486728000000,"doc_count":13},{"key_as_string":"2017-02-10T12:01:00.000Z","key":1486728060000,"doc_count":15},{"key_as_string":"2017-02-10T12:02:00.000Z","key":1486728120000,"doc_count":10},{"key_as_string":"2017-02-10T12:03:00.000Z","key":1486728180000,"doc_count":14},{"key_as_string":"2017-02-10T12:04:00.000Z","key":1486728240000,"doc_count":14},{"key_as_string":"2017-02-10T12:05:00.000Z","key":1486728300000,"doc_count":15},{"key_as_string":"2017-02-10T12:06:00.000Z","key":1486728360000,"doc_count":10},{"key_as_string":"2017-02-10T12:07:00.000Z","key":1486728420000,"doc_count":16},{"key_as_string":"2017-02-10T12:08:00.000Z","key":1486728480000,"doc_count":17},{"key_as_string":"2017-02-10T12:09:00.000Z","key":1486728540000,"doc_count":11},{"key_as_string":"2017-02-10T12:10:00.000Z","key":1486728600000,"doc_count":16},{"key_as_string":"2017-02-10T12:11:00.000Z","key":1486728660000,"doc_count":16},{"key_as_string":"2017-02-10T12:12:00.000Z","key":1486728720000,"doc_count":8},{"key_as_string":"2017-02-10T12:13:00.000Z","key":1486728780000,"doc_count":19},{"key_as_string":"2017-02-10T12:14:00.000Z","key":1486728840000,"doc_count":21},{"key_as_string":"2017-02-10T12:15:00.000Z","key":1486728900000,"doc_count":15},{"key_as_string":"2017-02-10T12:16:00.000Z","key":1486728960000,"doc_count":12},{"key_as_string":"2017-02-10T12:17:00.000Z","key":1486729020000,"doc_count":16},{"key_as_string":"2017-02-10T12:18:00.000Z","key":1486729080000,"doc_count":14},{"key_as_string":"2017-02-10T12:19:00.000Z","key":1486729140000,"doc_count":17},{"key_as_string":"2017-02-10T12:20:00.000Z","key":1486729200000,"doc_count":16},{"key_as_string":"2017-02-10T12:21:00.000Z","key":1486729260000,"doc_count":13},{"key_as_string":"2017-02-10T12:22:00.000Z","key":1486729320000,"doc_count":9},{"key_as_string":"2017-02-10T12:23:00.000Z","key":1486729380000,"doc_count":14},{"key_as_string":"2017-02-10T12:24:00.000Z","key":1486729440000,"doc_count":14},{"key_as_string":"2017-02-10T12:25:00.000Z","key":1486729500000,"doc_count":12},{"key_as_string":"2017-02-10T12:26:00.000Z","key":1486729560000,"doc_count":15},{"key_as_string":"2017-02-10T12:27:00.000Z","key":1486729620000,"doc_count":11},{"key_as_string":"2017-02-10T12:28:00.000Z","key":1486729680000,"doc_count":11},{"key_as_string":"2017-02-10T12:29:00.000Z","key":1486729740000,"doc_count":13},{"key_as_string":"2017-02-10T12:30:00.000Z","key":1486729800000,"doc_count":9},{"key_as_string":"2017-02-10T12:31:00.000Z","key":1486729860000,"doc_count":16},{"key_as_string":"2017-02-10T12:32:00.000Z","key":1486729920000,"doc_count":7},{"key_as_string":"2017-02-10T12:33:00.000Z","key":1486729980000,"doc_count":15},{"key_as_string":"2017-02-10T12:34:00.000Z","key":1486730040000,"doc_count":10},{"key_as_string":"2017-02-10T12:35:00.000Z","key":1486730100000,"doc_count":15},{"key_as_string":"2017-02-10T12:36:00.000Z","key":1486730160000,"doc_count":15},{"key_as_string":"2017-02-10T12:37:00.000Z","key":1486730220000,"doc_count":17},{"key_as_string":"2017-02-10T12:38:00.000Z","key":1486730280000,"doc_count":15},{"key_as_string":"2017-02-10T12:39:00.000Z","key":1486730340000,"doc_count":13},{"key_as_string":"2017-02-10T12:40:00.000Z","key":1486730400000,"doc_count":12},{"key_as_string":"2017-02-10T12:41:00.000Z","key":1486730460000,"doc_count":10},{"key_as_string":"2017-02-10T12:42:00.000Z","key":1486730520000,"doc_count":11},{"key_as_string":"2017-02-10T12:43:00.000Z","key":1486730580000,"doc_count":15},{"key_as_string":"2017-02-10T12:44:00.000Z","key":1486730640000,"doc_count":14},{"key_as_string":"2017-02-10T12:45:00.000Z","key":1486730700000,"doc_count":15},{"key_as_string":"2017-02-10T12:46:00.000Z","key":1486730760000,"doc_count":12},{"key_as_string":"2017-02-10T12:47:00.000Z","key":1486730820000,"doc_count":14},{"key_as_string":"2017-02-10T12:48:00.000Z","key":1486730880000,"doc_count":15},{"key_as_string":"2017-02-10T12:49:00.000Z","key":1486730940000,"doc_count":14},{"key_as_string":"2017-02-10T12:50:00.000Z","key":1486731000000,"doc_count":14},{"key_as_string":"2017-02-10T12:51:00.000Z","key":1486731060000,"doc_count":13},{"key_as_string":"2017-02-10T12:52:00.000Z","key":1486731120000,"doc_count":17},{"key_as_string":"2017-02-10T12:53:00.000Z","key":1486731180000,"doc_count":10},{"key_as_string":"2017-02-10T12:54:00.000Z","key":1486731240000,"doc_count":12},{"key_as_string":"2017-02-10T12:55:00.000Z","key":1486731300000,"doc_count":16},{"key_as_string":"2017-02-10T12:56:00.000Z","key":1486731360000,"doc_count":8},{"key_as_string":"2017-02-10T12:57:00.000Z","key":1486731420000,"doc_count":12},{"key_as_string":"2017-02-10T12:58:00.000Z","key":1486731480000,"doc_count":22},{"key_as_string":"2017-02-10T12:59:00.000Z","key":1486731540000,"doc_count":13},{"key_as_string":"2017-02-10T13:00:00.000Z","key":1486731600000,"doc_count":15},{"key_as_string":"2017-02-10T13:01:00.000Z","key":1486731660000,"doc_count":8},{"key_as_string":"2017-02-10T13:02:00.000Z","key":1486731720000,"doc_count":19},{"key_as_string":"2017-02-10T13:03:00.000Z","key":1486731780000,"doc_count":9},{"key_as_string":"2017-02-10T13:04:00.000Z","key":1486731840000,"doc_count":12},{"key_as_string":"2017-02-10T13:05:00.000Z","key":1486731900000,"doc_count":15},{"key_as_string":"2017-02-10T13:06:00.000Z","key":1486731960000,"doc_count":15},{"key_as_string":"2017-02-10T13:07:00.000Z","key":1486732020000,"doc_count":16},{"key_as_string":"2017-02-10T13:08:00.000Z","key":1486732080000,"doc_count":14},{"key_as_string":"2017-02-10T13:09:00.000Z","key":1486732140000,"doc_count":15},{"key_as_string":"2017-02-10T13:10:00.000Z","key":1486732200000,"doc_count":9},{"key_as_string":"2017-02-10T13:11:00.000Z","key":1486732260000,"doc_count":14},{"key_as_string":"2017-02-10T13:12:00.000Z","key":1486732320000,"doc_count":15},{"key_as_string":"2017-02-10T13:13:00.000Z","key":1486732380000,"doc_count":12},{"key_as_string":"2017-02-10T13:14:00.000Z","key":1486732440000,"doc_count":13},{"key_as_string":"2017-02-10T13:15:00.000Z","key":1486732500000,"doc_count":22},{"key_as_string":"2017-02-10T13:16:00.000Z","key":1486732560000,"doc_count":9},{"key_as_string":"2017-02-10T13:17:00.000Z","key":1486732620000,"doc_count":13},{"key_as_string":"2017-02-10T13:18:00.000Z","key":1486732680000,"doc_count":14},{"key_as_string":"2017-02-10T13:19:00.000Z","key":1486732740000,"doc_count":9},{"key_as_string":"2017-02-10T13:20:00.000Z","key":1486732800000,"doc_count":11},{"key_as_string":"2017-02-10T13:21:00.000Z","key":1486732860000,"doc_count":12},{"key_as_string":"2017-02-10T13:22:00.000Z","key":1486732920000,"doc_count":16},{"key_as_string":"2017-02-10T13:23:00.000Z","key":1486732980000,"doc_count":15},{"key_as_string":"2017-02-10T13:24:00.000Z","key":1486733040000,"doc_count":9},{"key_as_string":"2017-02-10T13:25:00.000Z","key":1486733100000,"doc_count":12},{"key_as_string":"2017-02-10T13:26:00.000Z","key":1486733160000,"doc_count":14},{"key_as_string":"2017-02-10T13:27:00.000Z","key":1486733220000,"doc_count":17},{"key_as_string":"2017-02-10T13:28:00.000Z","key":1486733280000,"doc_count":5},{"key_as_string":"2017-02-10T13:29:00.000Z","key":1486733340000,"doc_count":16},{"key_as_string":"2017-02-10T13:30:00.000Z","key":1486733400000,"doc_count":9},{"key_as_string":"2017-02-10T13:31:00.000Z","key":1486733460000,"doc_count":17},{"key_as_string":"2017-02-10T13:32:00.000Z","key":1486733520000,"doc_count":12},{"key_as_string":"2017-02-10T13:33:00.000Z","key":1486733580000,"doc_count":10},{"key_as_string":"2017-02-10T13:34:00.000Z","key":1486733640000,"doc_count":18},{"key_as_string":"2017-02-10T13:35:00.000Z","key":1486733700000,"doc_count":5},{"key_as_string":"2017-02-10T13:36:00.000Z","key":1486733760000,"doc_count":13},{"key_as_string":"2017-02-10T13:37:00.000Z","key":1486733820000,"doc_count":18},{"key_as_string":"2017-02-10T13:38:00.000Z","key":1486733880000,"doc_count":17},{"key_as_string":"2017-02-10T13:39:00.000Z","key":1486733940000,"doc_count":15},{"key_as_string":"2017-02-10T13:40:00.000Z","key":1486734000000,"doc_count":16},{"key_as_string":"2017-02-10T13:41:00.000Z","key":1486734060000,"doc_count":8},{"key_as_string":"2017-02-10T13:42:00.000Z","key":1486734120000,"doc_count":17},{"key_as_string":"2017-02-10T13:43:00.000Z","key":1486734180000,"doc_count":14},{"key_as_string":"2017-02-10T13:44:00.000Z","key":1486734240000,"doc_count":14},{"key_as_string":"2017-02-10T13:45:00.000Z","key":1486734300000,"doc_count":17},{"key_as_string":"2017-02-10T13:46:00.000Z","key":1486734360000,"doc_count":14},{"key_as_string":"2017-02-10T13:47:00.000Z","key":1486734420000,"doc_count":16},{"key_as_string":"2017-02-10T13:48:00.000Z","key":1486734480000,"doc_count":16},{"key_as_string":"2017-02-10T13:49:00.000Z","key":1486734540000,"doc_count":21},{"key_as_string":"2017-02-10T13:50:00.000Z","key":1486734600000,"doc_count":12},{"key_as_string":"2017-02-10T13:51:00.000Z","key":1486734660000,"doc_count":12},{"key_as_string":"2017-02-10T13:52:00.000Z","key":1486734720000,"doc_count":8},{"key_as_string":"2017-02-10T13:53:00.000Z","key":1486734780000,"doc_count":15},{"key_as_string":"2017-02-10T13:54:00.000Z","key":1486734840000,"doc_count":13},{"key_as_string":"2017-02-10T13:55:00.000Z","key":1486734900000,"doc_count":14},{"key_as_string":"2017-02-10T13:56:00.000Z","key":1486734960000,"doc_count":13},{"key_as_string":"2017-02-10T13:57:00.000Z","key":1486735020000,"doc_count":12},{"key_as_string":"2017-02-10T13:58:00.000Z","key":1486735080000,"doc_count":14},{"key_as_string":"2017-02-10T13:59:00.000Z","key":1486735140000,"doc_count":13},{"key_as_string":"2017-02-10T14:00:00.000Z","key":1486735200000,"doc_count":16},{"key_as_string":"2017-02-10T14:01:00.000Z","key":1486735260000,"doc_count":9},{"key_as_string":"2017-02-10T14:02:00.000Z","key":1486735320000,"doc_count":20},{"key_as_string":"2017-02-10T14:03:00.000Z","key":1486735380000,"doc_count":15},{"key_as_string":"2017-02-10T14:04:00.000Z","key":1486735440000,"doc_count":13},{"key_as_string":"2017-02-10T14:05:00.000Z","key":1486735500000,"doc_count":10},{"key_as_string":"2017-02-10T14:06:00.000Z","key":1486735560000,"doc_count":11},{"key_as_string":"2017-02-10T14:07:00.000Z","key":1486735620000,"doc_count":9},{"key_as_string":"2017-02-10T14:08:00.000Z","key":1486735680000,"doc_count":15},{"key_as_string":"2017-02-10T14:09:00.000Z","key":1486735740000,"doc_count":17},{"key_as_string":"2017-02-10T14:10:00.000Z","key":1486735800000,"doc_count":13},{"key_as_string":"2017-02-10T14:11:00.000Z","key":1486735860000,"doc_count":15},{"key_as_string":"2017-02-10T14:12:00.000Z","key":1486735920000,"doc_count":10},{"key_as_string":"2017-02-10T14:13:00.000Z","key":1486735980000,"doc_count":12},{"key_as_string":"2017-02-10T14:14:00.000Z","key":1486736040000,"doc_count":15},{"key_as_string":"2017-02-10T14:15:00.000Z","key":1486736100000,"doc_count":13},{"key_as_string":"2017-02-10T14:16:00.000Z","key":1486736160000,"doc_count":13},{"key_as_string":"2017-02-10T14:17:00.000Z","key":1486736220000,"doc_count":15},{"key_as_string":"2017-02-10T14:18:00.000Z","key":1486736280000,"doc_count":14},{"key_as_string":"2017-02-10T14:19:00.000Z","key":1486736340000,"doc_count":9},{"key_as_string":"2017-02-10T14:20:00.000Z","key":1486736400000,"doc_count":15},{"key_as_string":"2017-02-10T14:21:00.000Z","key":1486736460000,"doc_count":12},{"key_as_string":"2017-02-10T14:22:00.000Z","key":1486736520000,"doc_count":12},{"key_as_string":"2017-02-10T14:23:00.000Z","key":1486736580000,"doc_count":16},{"key_as_string":"2017-02-10T14:24:00.000Z","key":1486736640000,"doc_count":4},{"key_as_string":"2017-02-10T14:25:00.000Z","key":1486736700000,"doc_count":12},{"key_as_string":"2017-02-10T14:26:00.000Z","key":1486736760000,"doc_count":15},{"key_as_string":"2017-02-10T14:27:00.000Z","key":1486736820000,"doc_count":17},{"key_as_string":"2017-02-10T14:28:00.000Z","key":1486736880000,"doc_count":15},{"key_as_string":"2017-02-10T14:29:00.000Z","key":1486736940000,"doc_count":13},{"key_as_string":"2017-02-10T14:30:00.000Z","key":1486737000000,"doc_count":9},{"key_as_string":"2017-02-10T14:31:00.000Z","key":1486737060000,"doc_count":17},{"key_as_string":"2017-02-10T14:32:00.000Z","key":1486737120000,"doc_count":14},{"key_as_string":"2017-02-10T14:33:00.000Z","key":1486737180000,"doc_count":11},{"key_as_string":"2017-02-10T14:34:00.000Z","key":1486737240000,"doc_count":10},{"key_as_string":"2017-02-10T14:35:00.000Z","key":1486737300000,"doc_count":10},{"key_as_string":"2017-02-10T14:36:00.000Z","key":1486737360000,"doc_count":12},{"key_as_string":"2017-02-10T14:37:00.000Z","key":1486737420000,"doc_count":14},{"key_as_string":"2017-02-10T14:38:00.000Z","key":1486737480000,"doc_count":15},{"key_as_string":"2017-02-10T14:39:00.000Z","key":1486737540000,"doc_count":8},{"key_as_string":"2017-02-10T14:40:00.000Z","key":1486737600000,"doc_count":15},{"key_as_string":"2017-02-10T14:41:00.000Z","key":1486737660000,"doc_count":10},{"key_as_string":"2017-02-10T14:42:00.000Z","key":1486737720000,"doc_count":14},{"key_as_string":"2017-02-10T14:43:00.000Z","key":1486737780000,"doc_count":11},{"key_as_string":"2017-02-10T14:44:00.000Z","key":1486737840000,"doc_count":12},{"key_as_string":"2017-02-10T14:45:00.000Z","key":1486737900000,"doc_count":18},{"key_as_string":"2017-02-10T14:46:00.000Z","key":1486737960000,"doc_count":12},{"key_as_string":"2017-02-10T14:47:00.000Z","key":1486738020000,"doc_count":14},{"key_as_string":"2017-02-10T14:48:00.000Z","key":1486738080000,"doc_count":13},{"key_as_string":"2017-02-10T14:49:00.000Z","key":1486738140000,"doc_count":9},{"key_as_string":"2017-02-10T14:50:00.000Z","key":1486738200000,"doc_count":20},{"key_as_string":"2017-02-10T14:51:00.000Z","key":1486738260000,"doc_count":11},{"key_as_string":"2017-02-10T14:52:00.000Z","key":1486738320000,"doc_count":9},{"key_as_string":"2017-02-10T14:53:00.000Z","key":1486738380000,"doc_count":14},{"key_as_string":"2017-02-10T14:54:00.000Z","key":1486738440000,"doc_count":15},{"key_as_string":"2017-02-10T14:55:00.000Z","key":1486738500000,"doc_count":13},{"key_as_string":"2017-02-10T14:56:00.000Z","key":1486738560000,"doc_count":12},{"key_as_string":"2017-02-10T14:57:00.000Z","key":1486738620000,"doc_count":15},{"key_as_string":"2017-02-10T14:58:00.000Z","key":1486738680000,"doc_count":9},{"key_as_string":"2017-02-10T14:59:00.000Z","key":1486738740000,"doc_count":12},{"key_as_string":"2017-02-10T15:00:00.000Z","key":1486738800000,"doc_count":15},{"key_as_string":"2017-02-10T15:01:00.000Z","key":1486738860000,"doc_count":11},{"key_as_string":"2017-02-10T15:02:00.000Z","key":1486738920000,"doc_count":14},{"key_as_string":"2017-02-10T15:03:00.000Z","key":1486738980000,"doc_count":11},{"key_as_string":"2017-02-10T15:04:00.000Z","key":1486739040000,"doc_count":12},{"key_as_string":"2017-02-10T15:05:00.000Z","key":1486739100000,"doc_count":11},{"key_as_string":"2017-02-10T15:06:00.000Z","key":1486739160000,"doc_count":17},{"key_as_string":"2017-02-10T15:07:00.000Z","key":1486739220000,"doc_count":12},{"key_as_string":"2017-02-10T15:08:00.000Z","key":1486739280000,"doc_count":10},{"key_as_string":"2017-02-10T15:09:00.000Z","key":1486739340000,"doc_count":19},{"key_as_string":"2017-02-10T15:10:00.000Z","key":1486739400000,"doc_count":10},{"key_as_string":"2017-02-10T15:11:00.000Z","key":1486739460000,"doc_count":10},{"key_as_string":"2017-02-10T15:12:00.000Z","key":1486739520000,"doc_count":18},{"key_as_string":"2017-02-10T15:13:00.000Z","key":1486739580000,"doc_count":14},{"key_as_string":"2017-02-10T15:14:00.000Z","key":1486739640000,"doc_count":11},{"key_as_string":"2017-02-10T15:15:00.000Z","key":1486739700000,"doc_count":16},{"key_as_string":"2017-02-10T15:16:00.000Z","key":1486739760000,"doc_count":13},{"key_as_string":"2017-02-10T15:17:00.000Z","key":1486739820000,"doc_count":20},{"key_as_string":"2017-02-10T15:18:00.000Z","key":1486739880000,"doc_count":10},{"key_as_string":"2017-02-10T15:19:00.000Z","key":1486739940000,"doc_count":16},{"key_as_string":"2017-02-10T15:20:00.000Z","key":1486740000000,"doc_count":15},{"key_as_string":"2017-02-10T15:21:00.000Z","key":1486740060000,"doc_count":16},{"key_as_string":"2017-02-10T15:22:00.000Z","key":1486740120000,"doc_count":20},{"key_as_string":"2017-02-10T15:23:00.000Z","key":1486740180000,"doc_count":12},{"key_as_string":"2017-02-10T15:24:00.000Z","key":1486740240000,"doc_count":15},{"key_as_string":"2017-02-10T15:25:00.000Z","key":1486740300000,"doc_count":12},{"key_as_string":"2017-02-10T15:26:00.000Z","key":1486740360000,"doc_count":16},{"key_as_string":"2017-02-10T15:27:00.000Z","key":1486740420000,"doc_count":14},{"key_as_string":"2017-02-10T15:28:00.000Z","key":1486740480000,"doc_count":12},{"key_as_string":"2017-02-10T15:29:00.000Z","key":1486740540000,"doc_count":10},{"key_as_string":"2017-02-10T15:30:00.000Z","key":1486740600000,"doc_count":16},{"key_as_string":"2017-02-10T15:31:00.000Z","key":1486740660000,"doc_count":13},{"key_as_string":"2017-02-10T15:32:00.000Z","key":1486740720000,"doc_count":14},{"key_as_string":"2017-02-10T15:33:00.000Z","key":1486740780000,"doc_count":11},{"key_as_string":"2017-02-10T15:34:00.000Z","key":1486740840000,"doc_count":12},{"key_as_string":"2017-02-10T15:35:00.000Z","key":1486740900000,"doc_count":12},{"key_as_string":"2017-02-10T15:36:00.000Z","key":1486740960000,"doc_count":11},{"key_as_string":"2017-02-10T15:37:00.000Z","key":1486741020000,"doc_count":13},{"key_as_string":"2017-02-10T15:38:00.000Z","key":1486741080000,"doc_count":11},{"key_as_string":"2017-02-10T15:39:00.000Z","key":1486741140000,"doc_count":11},{"key_as_string":"2017-02-10T15:40:00.000Z","key":1486741200000,"doc_count":12},{"key_as_string":"2017-02-10T15:41:00.000Z","key":1486741260000,"doc_count":16},{"key_as_string":"2017-02-10T15:42:00.000Z","key":1486741320000,"doc_count":15},{"key_as_string":"2017-02-10T15:43:00.000Z","key":1486741380000,"doc_count":15},{"key_as_string":"2017-02-10T15:44:00.000Z","key":1486741440000,"doc_count":15},{"key_as_string":"2017-02-10T15:45:00.000Z","key":1486741500000,"doc_count":15},{"key_as_string":"2017-02-10T15:46:00.000Z","key":1486741560000,"doc_count":11},{"key_as_string":"2017-02-10T15:47:00.000Z","key":1486741620000,"doc_count":14},{"key_as_string":"2017-02-10T15:48:00.000Z","key":1486741680000,"doc_count":15},{"key_as_string":"2017-02-10T15:49:00.000Z","key":1486741740000,"doc_count":12},{"key_as_string":"2017-02-10T15:50:00.000Z","key":1486741800000,"doc_count":12},{"key_as_string":"2017-02-10T15:51:00.000Z","key":1486741860000,"doc_count":12},{"key_as_string":"2017-02-10T15:52:00.000Z","key":1486741920000,"doc_count":19},{"key_as_string":"2017-02-10T15:53:00.000Z","key":1486741980000,"doc_count":14},{"key_as_string":"2017-02-10T15:54:00.000Z","key":1486742040000,"doc_count":18},{"key_as_string":"2017-02-10T15:55:00.000Z","key":1486742100000,"doc_count":11},{"key_as_string":"2017-02-10T15:56:00.000Z","key":1486742160000,"doc_count":16},{"key_as_string":"2017-02-10T15:57:00.000Z","key":1486742220000,"doc_count":11},{"key_as_string":"2017-02-10T15:58:00.000Z","key":1486742280000,"doc_count":10},{"key_as_string":"2017-02-10T15:59:00.000Z","key":1486742340000,"doc_count":12},{"key_as_string":"2017-02-10T16:00:00.000Z","key":1486742400000,"doc_count":16},{"key_as_string":"2017-02-10T16:01:00.000Z","key":1486742460000,"doc_count":12},{"key_as_string":"2017-02-10T16:02:00.000Z","key":1486742520000,"doc_count":16},{"key_as_string":"2017-02-10T16:03:00.000Z","key":1486742580000,"doc_count":11},{"key_as_string":"2017-02-10T16:04:00.000Z","key":1486742640000,"doc_count":13},{"key_as_string":"2017-02-10T16:05:00.000Z","key":1486742700000,"doc_count":15},{"key_as_string":"2017-02-10T16:06:00.000Z","key":1486742760000,"doc_count":13},{"key_as_string":"2017-02-10T16:07:00.000Z","key":1486742820000,"doc_count":14},{"key_as_string":"2017-02-10T16:08:00.000Z","key":1486742880000,"doc_count":16},{"key_as_string":"2017-02-10T16:09:00.000Z","key":1486742940000,"doc_count":8},{"key_as_string":"2017-02-10T16:10:00.000Z","key":1486743000000,"doc_count":13},{"key_as_string":"2017-02-10T16:11:00.000Z","key":1486743060000,"doc_count":13},{"key_as_string":"2017-02-10T16:12:00.000Z","key":1486743120000,"doc_count":15},{"key_as_string":"2017-02-10T16:13:00.000Z","key":1486743180000,"doc_count":20},{"key_as_string":"2017-02-10T16:14:00.000Z","key":1486743240000,"doc_count":12},{"key_as_string":"2017-02-10T16:15:00.000Z","key":1486743300000,"doc_count":10},{"key_as_string":"2017-02-10T16:16:00.000Z","key":1486743360000,"doc_count":12},{"key_as_string":"2017-02-10T16:17:00.000Z","key":1486743420000,"doc_count":12},{"key_as_string":"2017-02-10T16:18:00.000Z","key":1486743480000,"doc_count":15},{"key_as_string":"2017-02-10T16:19:00.000Z","key":1486743540000,"doc_count":11},{"key_as_string":"2017-02-10T16:20:00.000Z","key":1486743600000,"doc_count":10},{"key_as_string":"2017-02-10T16:21:00.000Z","key":1486743660000,"doc_count":13},{"key_as_string":"2017-02-10T16:22:00.000Z","key":1486743720000,"doc_count":12},{"key_as_string":"2017-02-10T16:23:00.000Z","key":1486743780000,"doc_count":15},{"key_as_string":"2017-02-10T16:24:00.000Z","key":1486743840000,"doc_count":17},{"key_as_string":"2017-02-10T16:25:00.000Z","key":1486743900000,"doc_count":12},{"key_as_string":"2017-02-10T16:26:00.000Z","key":1486743960000,"doc_count":13},{"key_as_string":"2017-02-10T16:27:00.000Z","key":1486744020000,"doc_count":11},{"key_as_string":"2017-02-10T16:28:00.000Z","key":1486744080000,"doc_count":8},{"key_as_string":"2017-02-10T16:29:00.000Z","key":1486744140000,"doc_count":14},{"key_as_string":"2017-02-10T16:30:00.000Z","key":1486744200000,"doc_count":12},{"key_as_string":"2017-02-10T16:31:00.000Z","key":1486744260000,"doc_count":12},{"key_as_string":"2017-02-10T16:32:00.000Z","key":1486744320000,"doc_count":12},{"key_as_string":"2017-02-10T16:33:00.000Z","key":1486744380000,"doc_count":8},{"key_as_string":"2017-02-10T16:34:00.000Z","key":1486744440000,"doc_count":18},{"key_as_string":"2017-02-10T16:35:00.000Z","key":1486744500000,"doc_count":14},{"key_as_string":"2017-02-10T16:36:00.000Z","key":1486744560000,"doc_count":12},{"key_as_string":"2017-02-10T16:37:00.000Z","key":1486744620000,"doc_count":10},{"key_as_string":"2017-02-10T16:38:00.000Z","key":1486744680000,"doc_count":19},{"key_as_string":"2017-02-10T16:39:00.000Z","key":1486744740000,"doc_count":8},{"key_as_string":"2017-02-10T16:40:00.000Z","key":1486744800000,"doc_count":12},{"key_as_string":"2017-02-10T16:41:00.000Z","key":1486744860000,"doc_count":16},{"key_as_string":"2017-02-10T16:42:00.000Z","key":1486744920000,"doc_count":11},{"key_as_string":"2017-02-10T16:43:00.000Z","key":1486744980000,"doc_count":15},{"key_as_string":"2017-02-10T16:44:00.000Z","key":1486745040000,"doc_count":9},{"key_as_string":"2017-02-10T16:45:00.000Z","key":1486745100000,"doc_count":15},{"key_as_string":"2017-02-10T16:46:00.000Z","key":1486745160000,"doc_count":11},{"key_as_string":"2017-02-10T16:47:00.000Z","key":1486745220000,"doc_count":16},{"key_as_string":"2017-02-10T16:48:00.000Z","key":1486745280000,"doc_count":17},{"key_as_string":"2017-02-10T16:49:00.000Z","key":1486745340000,"doc_count":10},{"key_as_string":"2017-02-10T16:50:00.000Z","key":1486745400000,"doc_count":11},{"key_as_string":"2017-02-10T16:51:00.000Z","key":1486745460000,"doc_count":13},{"key_as_string":"2017-02-10T16:52:00.000Z","key":1486745520000,"doc_count":7},{"key_as_string":"2017-02-10T16:53:00.000Z","key":1486745580000,"doc_count":13},{"key_as_string":"2017-02-10T16:54:00.000Z","key":1486745640000,"doc_count":12},{"key_as_string":"2017-02-10T16:55:00.000Z","key":1486745700000,"doc_count":18},{"key_as_string":"2017-02-10T16:56:00.000Z","key":1486745760000,"doc_count":6},{"key_as_string":"2017-02-10T16:57:00.000Z","key":1486745820000,"doc_count":11},{"key_as_string":"2017-02-10T16:58:00.000Z","key":1486745880000,"doc_count":10},{"key_as_string":"2017-02-10T16:59:00.000Z","key":1486745940000,"doc_count":12},{"key_as_string":"2017-02-10T17:00:00.000Z","key":1486746000000,"doc_count":13},{"key_as_string":"2017-02-10T17:01:00.000Z","key":1486746060000,"doc_count":9},{"key_as_string":"2017-02-10T17:02:00.000Z","key":1486746120000,"doc_count":14},{"key_as_string":"2017-02-10T17:03:00.000Z","key":1486746180000,"doc_count":13},{"key_as_string":"2017-02-10T17:04:00.000Z","key":1486746240000,"doc_count":12},{"key_as_string":"2017-02-10T17:05:00.000Z","key":1486746300000,"doc_count":9},{"key_as_string":"2017-02-10T17:06:00.000Z","key":1486746360000,"doc_count":14},{"key_as_string":"2017-02-10T17:07:00.000Z","key":1486746420000,"doc_count":15},{"key_as_string":"2017-02-10T17:08:00.000Z","key":1486746480000,"doc_count":11},{"key_as_string":"2017-02-10T17:09:00.000Z","key":1486746540000,"doc_count":8},{"key_as_string":"2017-02-10T17:10:00.000Z","key":1486746600000,"doc_count":9},{"key_as_string":"2017-02-10T17:11:00.000Z","key":1486746660000,"doc_count":12},{"key_as_string":"2017-02-10T17:12:00.000Z","key":1486746720000,"doc_count":15},{"key_as_string":"2017-02-10T17:13:00.000Z","key":1486746780000,"doc_count":13},{"key_as_string":"2017-02-10T17:14:00.000Z","key":1486746840000,"doc_count":14},{"key_as_string":"2017-02-10T17:15:00.000Z","key":1486746900000,"doc_count":10},{"key_as_string":"2017-02-10T17:16:00.000Z","key":1486746960000,"doc_count":12},{"key_as_string":"2017-02-10T17:17:00.000Z","key":1486747020000,"doc_count":10},{"key_as_string":"2017-02-10T17:18:00.000Z","key":1486747080000,"doc_count":14},{"key_as_string":"2017-02-10T17:19:00.000Z","key":1486747140000,"doc_count":7},{"key_as_string":"2017-02-10T17:20:00.000Z","key":1486747200000,"doc_count":9},{"key_as_string":"2017-02-10T17:21:00.000Z","key":1486747260000,"doc_count":10},{"key_as_string":"2017-02-10T17:22:00.000Z","key":1486747320000,"doc_count":13},{"key_as_string":"2017-02-10T17:23:00.000Z","key":1486747380000,"doc_count":16},{"key_as_string":"2017-02-10T17:24:00.000Z","key":1486747440000,"doc_count":11},{"key_as_string":"2017-02-10T17:25:00.000Z","key":1486747500000,"doc_count":14},{"key_as_string":"2017-02-10T17:26:00.000Z","key":1486747560000,"doc_count":12},{"key_as_string":"2017-02-10T17:27:00.000Z","key":1486747620000,"doc_count":12},{"key_as_string":"2017-02-10T17:28:00.000Z","key":1486747680000,"doc_count":9},{"key_as_string":"2017-02-10T17:29:00.000Z","key":1486747740000,"doc_count":16},{"key_as_string":"2017-02-10T17:30:00.000Z","key":1486747800000,"doc_count":14},{"key_as_string":"2017-02-10T17:31:00.000Z","key":1486747860000,"doc_count":10},{"key_as_string":"2017-02-10T17:32:00.000Z","key":1486747920000,"doc_count":8},{"key_as_string":"2017-02-10T17:33:00.000Z","key":1486747980000,"doc_count":9},{"key_as_string":"2017-02-10T17:34:00.000Z","key":1486748040000,"doc_count":15},{"key_as_string":"2017-02-10T17:35:00.000Z","key":1486748100000,"doc_count":9},{"key_as_string":"2017-02-10T17:36:00.000Z","key":1486748160000,"doc_count":13},{"key_as_string":"2017-02-10T17:37:00.000Z","key":1486748220000,"doc_count":9},{"key_as_string":"2017-02-10T17:38:00.000Z","key":1486748280000,"doc_count":7},{"key_as_string":"2017-02-10T17:39:00.000Z","key":1486748340000,"doc_count":18},{"key_as_string":"2017-02-10T17:40:00.000Z","key":1486748400000,"doc_count":14},{"key_as_string":"2017-02-10T17:41:00.000Z","key":1486748460000,"doc_count":9},{"key_as_string":"2017-02-10T17:42:00.000Z","key":1486748520000,"doc_count":12},{"key_as_string":"2017-02-10T17:43:00.000Z","key":1486748580000,"doc_count":9},{"key_as_string":"2017-02-10T17:44:00.000Z","key":1486748640000,"doc_count":14},{"key_as_string":"2017-02-10T17:45:00.000Z","key":1486748700000,"doc_count":11},{"key_as_string":"2017-02-10T17:46:00.000Z","key":1486748760000,"doc_count":11},{"key_as_string":"2017-02-10T17:47:00.000Z","key":1486748820000,"doc_count":12},{"key_as_string":"2017-02-10T17:48:00.000Z","key":1486748880000,"doc_count":16},{"key_as_string":"2017-02-10T17:49:00.000Z","key":1486748940000,"doc_count":10},{"key_as_string":"2017-02-10T17:50:00.000Z","key":1486749000000,"doc_count":14},{"key_as_string":"2017-02-10T17:51:00.000Z","key":1486749060000,"doc_count":8},{"key_as_string":"2017-02-10T17:52:00.000Z","key":1486749120000,"doc_count":16},{"key_as_string":"2017-02-10T17:53:00.000Z","key":1486749180000,"doc_count":9},{"key_as_string":"2017-02-10T17:54:00.000Z","key":1486749240000,"doc_count":11},{"key_as_string":"2017-02-10T17:55:00.000Z","key":1486749300000,"doc_count":9},{"key_as_string":"2017-02-10T17:56:00.000Z","key":1486749360000,"doc_count":12},{"key_as_string":"2017-02-10T17:57:00.000Z","key":1486749420000,"doc_count":13},{"key_as_string":"2017-02-10T17:58:00.000Z","key":1486749480000,"doc_count":12},{"key_as_string":"2017-02-10T17:59:00.000Z","key":1486749540000,"doc_count":15},{"key_as_string":"2017-02-10T18:00:00.000Z","key":1486749600000,"doc_count":10},{"key_as_string":"2017-02-10T18:01:00.000Z","key":1486749660000,"doc_count":10},{"key_as_string":"2017-02-10T18:02:00.000Z","key":1486749720000,"doc_count":10},{"key_as_string":"2017-02-10T18:03:00.000Z","key":1486749780000,"doc_count":9},{"key_as_string":"2017-02-10T18:04:00.000Z","key":1486749840000,"doc_count":12},{"key_as_string":"2017-02-10T18:05:00.000Z","key":1486749900000,"doc_count":14},{"key_as_string":"2017-02-10T18:06:00.000Z","key":1486749960000,"doc_count":16},{"key_as_string":"2017-02-10T18:07:00.000Z","key":1486750020000,"doc_count":12},{"key_as_string":"2017-02-10T18:08:00.000Z","key":1486750080000,"doc_count":10},{"key_as_string":"2017-02-10T18:09:00.000Z","key":1486750140000,"doc_count":11},{"key_as_string":"2017-02-10T18:10:00.000Z","key":1486750200000,"doc_count":12},{"key_as_string":"2017-02-10T18:11:00.000Z","key":1486750260000,"doc_count":10},{"key_as_string":"2017-02-10T18:12:00.000Z","key":1486750320000,"doc_count":13},{"key_as_string":"2017-02-10T18:13:00.000Z","key":1486750380000,"doc_count":12},{"key_as_string":"2017-02-10T18:14:00.000Z","key":1486750440000,"doc_count":14},{"key_as_string":"2017-02-10T18:15:00.000Z","key":1486750500000,"doc_count":11},{"key_as_string":"2017-02-10T18:16:00.000Z","key":1486750560000,"doc_count":12},{"key_as_string":"2017-02-10T18:17:00.000Z","key":1486750620000,"doc_count":10},{"key_as_string":"2017-02-10T18:18:00.000Z","key":1486750680000,"doc_count":9},{"key_as_string":"2017-02-10T18:19:00.000Z","key":1486750740000,"doc_count":14},{"key_as_string":"2017-02-10T18:20:00.000Z","key":1486750800000,"doc_count":6},{"key_as_string":"2017-02-10T18:21:00.000Z","key":1486750860000,"doc_count":13},{"key_as_string":"2017-02-10T18:22:00.000Z","key":1486750920000,"doc_count":14},{"key_as_string":"2017-02-10T18:23:00.000Z","key":1486750980000,"doc_count":9},{"key_as_string":"2017-02-10T18:24:00.000Z","key":1486751040000,"doc_count":9},{"key_as_string":"2017-02-10T18:25:00.000Z","key":1486751100000,"doc_count":10},{"key_as_string":"2017-02-10T18:26:00.000Z","key":1486751160000,"doc_count":9},{"key_as_string":"2017-02-10T18:27:00.000Z","key":1486751220000,"doc_count":12},{"key_as_string":"2017-02-10T18:28:00.000Z","key":1486751280000,"doc_count":7},{"key_as_string":"2017-02-10T18:29:00.000Z","key":1486751340000,"doc_count":12},{"key_as_string":"2017-02-10T18:30:00.000Z","key":1486751400000,"doc_count":13},{"key_as_string":"2017-02-10T18:31:00.000Z","key":1486751460000,"doc_count":11},{"key_as_string":"2017-02-10T18:32:00.000Z","key":1486751520000,"doc_count":13},{"key_as_string":"2017-02-10T18:33:00.000Z","key":1486751580000,"doc_count":4},{"key_as_string":"2017-02-10T18:34:00.000Z","key":1486751640000,"doc_count":12},{"key_as_string":"2017-02-10T18:35:00.000Z","key":1486751700000,"doc_count":12},{"key_as_string":"2017-02-10T18:36:00.000Z","key":1486751760000,"doc_count":9},{"key_as_string":"2017-02-10T18:37:00.000Z","key":1486751820000,"doc_count":14},{"key_as_string":"2017-02-10T18:38:00.000Z","key":1486751880000,"doc_count":9},{"key_as_string":"2017-02-10T18:39:00.000Z","key":1486751940000,"doc_count":12},{"key_as_string":"2017-02-10T18:40:00.000Z","key":1486752000000,"doc_count":10},{"key_as_string":"2017-02-10T18:41:00.000Z","key":1486752060000,"doc_count":15},{"key_as_string":"2017-02-10T18:42:00.000Z","key":1486752120000,"doc_count":13},{"key_as_string":"2017-02-10T18:43:00.000Z","key":1486752180000,"doc_count":16},{"key_as_string":"2017-02-10T18:44:00.000Z","key":1486752240000,"doc_count":12},{"key_as_string":"2017-02-10T18:45:00.000Z","key":1486752300000,"doc_count":11},{"key_as_string":"2017-02-10T18:46:00.000Z","key":1486752360000,"doc_count":13},{"key_as_string":"2017-02-10T18:47:00.000Z","key":1486752420000,"doc_count":15},{"key_as_string":"2017-02-10T18:48:00.000Z","key":1486752480000,"doc_count":6},{"key_as_string":"2017-02-10T18:49:00.000Z","key":1486752540000,"doc_count":9},{"key_as_string":"2017-02-10T18:50:00.000Z","key":1486752600000,"doc_count":19},{"key_as_string":"2017-02-10T18:51:00.000Z","key":1486752660000,"doc_count":11},{"key_as_string":"2017-02-10T18:52:00.000Z","key":1486752720000,"doc_count":11},{"key_as_string":"2017-02-10T18:53:00.000Z","key":1486752780000,"doc_count":11},{"key_as_string":"2017-02-10T18:54:00.000Z","key":1486752840000,"doc_count":12},{"key_as_string":"2017-02-10T18:55:00.000Z","key":1486752900000,"doc_count":11},{"key_as_string":"2017-02-10T18:56:00.000Z","key":1486752960000,"doc_count":12},{"key_as_string":"2017-02-10T18:57:00.000Z","key":1486753020000,"doc_count":11},{"key_as_string":"2017-02-10T18:58:00.000Z","key":1486753080000,"doc_count":13},{"key_as_string":"2017-02-10T18:59:00.000Z","key":1486753140000,"doc_count":12},{"key_as_string":"2017-02-10T19:00:00.000Z","key":1486753200000,"doc_count":10},{"key_as_string":"2017-02-10T19:01:00.000Z","key":1486753260000,"doc_count":7},{"key_as_string":"2017-02-10T19:02:00.000Z","key":1486753320000,"doc_count":15},{"key_as_string":"2017-02-10T19:03:00.000Z","key":1486753380000,"doc_count":10},{"key_as_string":"2017-02-10T19:04:00.000Z","key":1486753440000,"doc_count":11},{"key_as_string":"2017-02-10T19:05:00.000Z","key":1486753500000,"doc_count":10},{"key_as_string":"2017-02-10T19:06:00.000Z","key":1486753560000,"doc_count":15},{"key_as_string":"2017-02-10T19:07:00.000Z","key":1486753620000,"doc_count":12},{"key_as_string":"2017-02-10T19:08:00.000Z","key":1486753680000,"doc_count":7},{"key_as_string":"2017-02-10T19:09:00.000Z","key":1486753740000,"doc_count":11},{"key_as_string":"2017-02-10T19:10:00.000Z","key":1486753800000,"doc_count":9},{"key_as_string":"2017-02-10T19:11:00.000Z","key":1486753860000,"doc_count":11},{"key_as_string":"2017-02-10T19:12:00.000Z","key":1486753920000,"doc_count":10},{"key_as_string":"2017-02-10T19:13:00.000Z","key":1486753980000,"doc_count":11},{"key_as_string":"2017-02-10T19:14:00.000Z","key":1486754040000,"doc_count":14},{"key_as_string":"2017-02-10T19:15:00.000Z","key":1486754100000,"doc_count":10},{"key_as_string":"2017-02-10T19:16:00.000Z","key":1486754160000,"doc_count":12},{"key_as_string":"2017-02-10T19:17:00.000Z","key":1486754220000,"doc_count":12},{"key_as_string":"2017-02-10T19:18:00.000Z","key":1486754280000,"doc_count":11},{"key_as_string":"2017-02-10T19:19:00.000Z","key":1486754340000,"doc_count":11},{"key_as_string":"2017-02-10T19:20:00.000Z","key":1486754400000,"doc_count":10},{"key_as_string":"2017-02-10T19:21:00.000Z","key":1486754460000,"doc_count":15},{"key_as_string":"2017-02-10T19:22:00.000Z","key":1486754520000,"doc_count":8},{"key_as_string":"2017-02-10T19:23:00.000Z","key":1486754580000,"doc_count":7},{"key_as_string":"2017-02-10T19:24:00.000Z","key":1486754640000,"doc_count":20},{"key_as_string":"2017-02-10T19:25:00.000Z","key":1486754700000,"doc_count":10},{"key_as_string":"2017-02-10T19:26:00.000Z","key":1486754760000,"doc_count":9},{"key_as_string":"2017-02-10T19:27:00.000Z","key":1486754820000,"doc_count":13},{"key_as_string":"2017-02-10T19:28:00.000Z","key":1486754880000,"doc_count":12},{"key_as_string":"2017-02-10T19:29:00.000Z","key":1486754940000,"doc_count":10},{"key_as_string":"2017-02-10T19:30:00.000Z","key":1486755000000,"doc_count":11},{"key_as_string":"2017-02-10T19:31:00.000Z","key":1486755060000,"doc_count":10},{"key_as_string":"2017-02-10T19:32:00.000Z","key":1486755120000,"doc_count":10},{"key_as_string":"2017-02-10T19:33:00.000Z","key":1486755180000,"doc_count":11},{"key_as_string":"2017-02-10T19:34:00.000Z","key":1486755240000,"doc_count":12},{"key_as_string":"2017-02-10T19:35:00.000Z","key":1486755300000,"doc_count":8},{"key_as_string":"2017-02-10T19:36:00.000Z","key":1486755360000,"doc_count":10},{"key_as_string":"2017-02-10T19:37:00.000Z","key":1486755420000,"doc_count":13},{"key_as_string":"2017-02-10T19:38:00.000Z","key":1486755480000,"doc_count":11},{"key_as_string":"2017-02-10T19:39:00.000Z","key":1486755540000,"doc_count":7},{"key_as_string":"2017-02-10T19:40:00.000Z","key":1486755600000,"doc_count":17},{"key_as_string":"2017-02-10T19:41:00.000Z","key":1486755660000,"doc_count":8},{"key_as_string":"2017-02-10T19:42:00.000Z","key":1486755720000,"doc_count":15},{"key_as_string":"2017-02-10T19:43:00.000Z","key":1486755780000,"doc_count":11},{"key_as_string":"2017-02-10T19:44:00.000Z","key":1486755840000,"doc_count":11},{"key_as_string":"2017-02-10T19:45:00.000Z","key":1486755900000,"doc_count":8},{"key_as_string":"2017-02-10T19:46:00.000Z","key":1486755960000,"doc_count":8},{"key_as_string":"2017-02-10T19:47:00.000Z","key":1486756020000,"doc_count":15},{"key_as_string":"2017-02-10T19:48:00.000Z","key":1486756080000,"doc_count":15},{"key_as_string":"2017-02-10T19:49:00.000Z","key":1486756140000,"doc_count":11},{"key_as_string":"2017-02-10T19:50:00.000Z","key":1486756200000,"doc_count":15},{"key_as_string":"2017-02-10T19:51:00.000Z","key":1486756260000,"doc_count":9},{"key_as_string":"2017-02-10T19:52:00.000Z","key":1486756320000,"doc_count":10},{"key_as_string":"2017-02-10T19:53:00.000Z","key":1486756380000,"doc_count":9},{"key_as_string":"2017-02-10T19:54:00.000Z","key":1486756440000,"doc_count":12},{"key_as_string":"2017-02-10T19:55:00.000Z","key":1486756500000,"doc_count":9},{"key_as_string":"2017-02-10T19:56:00.000Z","key":1486756560000,"doc_count":13},{"key_as_string":"2017-02-10T19:57:00.000Z","key":1486756620000,"doc_count":12},{"key_as_string":"2017-02-10T19:58:00.000Z","key":1486756680000,"doc_count":10},{"key_as_string":"2017-02-10T19:59:00.000Z","key":1486756740000,"doc_count":7},{"key_as_string":"2017-02-10T20:00:00.000Z","key":1486756800000,"doc_count":12},{"key_as_string":"2017-02-10T20:01:00.000Z","key":1486756860000,"doc_count":12},{"key_as_string":"2017-02-10T20:02:00.000Z","key":1486756920000,"doc_count":12},{"key_as_string":"2017-02-10T20:03:00.000Z","key":1486756980000,"doc_count":8},{"key_as_string":"2017-02-10T20:04:00.000Z","key":1486757040000,"doc_count":20},{"key_as_string":"2017-02-10T20:05:00.000Z","key":1486757100000,"doc_count":7},{"key_as_string":"2017-02-10T20:06:00.000Z","key":1486757160000,"doc_count":16},{"key_as_string":"2017-02-10T20:07:00.000Z","key":1486757220000,"doc_count":13},{"key_as_string":"2017-02-10T20:08:00.000Z","key":1486757280000,"doc_count":9},{"key_as_string":"2017-02-10T20:09:00.000Z","key":1486757340000,"doc_count":12},{"key_as_string":"2017-02-10T20:10:00.000Z","key":1486757400000,"doc_count":7},{"key_as_string":"2017-02-10T20:11:00.000Z","key":1486757460000,"doc_count":8},{"key_as_string":"2017-02-10T20:12:00.000Z","key":1486757520000,"doc_count":10},{"key_as_string":"2017-02-10T20:13:00.000Z","key":1486757580000,"doc_count":8},{"key_as_string":"2017-02-10T20:14:00.000Z","key":1486757640000,"doc_count":17},{"key_as_string":"2017-02-10T20:15:00.000Z","key":1486757700000,"doc_count":11},{"key_as_string":"2017-02-10T20:16:00.000Z","key":1486757760000,"doc_count":12},{"key_as_string":"2017-02-10T20:17:00.000Z","key":1486757820000,"doc_count":12},{"key_as_string":"2017-02-10T20:18:00.000Z","key":1486757880000,"doc_count":12},{"key_as_string":"2017-02-10T20:19:00.000Z","key":1486757940000,"doc_count":15},{"key_as_string":"2017-02-10T20:20:00.000Z","key":1486758000000,"doc_count":7},{"key_as_string":"2017-02-10T20:21:00.000Z","key":1486758060000,"doc_count":11},{"key_as_string":"2017-02-10T20:22:00.000Z","key":1486758120000,"doc_count":10},{"key_as_string":"2017-02-10T20:23:00.000Z","key":1486758180000,"doc_count":14},{"key_as_string":"2017-02-10T20:24:00.000Z","key":1486758240000,"doc_count":11},{"key_as_string":"2017-02-10T20:25:00.000Z","key":1486758300000,"doc_count":9},{"key_as_string":"2017-02-10T20:26:00.000Z","key":1486758360000,"doc_count":7},{"key_as_string":"2017-02-10T20:27:00.000Z","key":1486758420000,"doc_count":10},{"key_as_string":"2017-02-10T20:28:00.000Z","key":1486758480000,"doc_count":14},{"key_as_string":"2017-02-10T20:29:00.000Z","key":1486758540000,"doc_count":12},{"key_as_string":"2017-02-10T20:30:00.000Z","key":1486758600000,"doc_count":10},{"key_as_string":"2017-02-10T20:31:00.000Z","key":1486758660000,"doc_count":11},{"key_as_string":"2017-02-10T20:32:00.000Z","key":1486758720000,"doc_count":12},{"key_as_string":"2017-02-10T20:33:00.000Z","key":1486758780000,"doc_count":15},{"key_as_string":"2017-02-10T20:34:00.000Z","key":1486758840000,"doc_count":12},{"key_as_string":"2017-02-10T20:35:00.000Z","key":1486758900000,"doc_count":14},{"key_as_string":"2017-02-10T20:36:00.000Z","key":1486758960000,"doc_count":6},{"key_as_string":"2017-02-10T20:37:00.000Z","key":1486759020000,"doc_count":11},{"key_as_string":"2017-02-10T20:38:00.000Z","key":1486759080000,"doc_count":15},{"key_as_string":"2017-02-10T20:39:00.000Z","key":1486759140000,"doc_count":11},{"key_as_string":"2017-02-10T20:40:00.000Z","key":1486759200000,"doc_count":10},{"key_as_string":"2017-02-10T20:41:00.000Z","key":1486759260000,"doc_count":10},{"key_as_string":"2017-02-10T20:42:00.000Z","key":1486759320000,"doc_count":8},{"key_as_string":"2017-02-10T20:43:00.000Z","key":1486759380000,"doc_count":10},{"key_as_string":"2017-02-10T20:44:00.000Z","key":1486759440000,"doc_count":10},{"key_as_string":"2017-02-10T20:45:00.000Z","key":1486759500000,"doc_count":9},{"key_as_string":"2017-02-10T20:46:00.000Z","key":1486759560000,"doc_count":12},{"key_as_string":"2017-02-10T20:47:00.000Z","key":1486759620000,"doc_count":11},{"key_as_string":"2017-02-10T20:48:00.000Z","key":1486759680000,"doc_count":13},{"key_as_string":"2017-02-10T20:49:00.000Z","key":1486759740000,"doc_count":8},{"key_as_string":"2017-02-10T20:50:00.000Z","key":1486759800000,"doc_count":10},{"key_as_string":"2017-02-10T20:51:00.000Z","key":1486759860000,"doc_count":10},{"key_as_string":"2017-02-10T20:52:00.000Z","key":1486759920000,"doc_count":14},{"key_as_string":"2017-02-10T20:53:00.000Z","key":1486759980000,"doc_count":10},{"key_as_string":"2017-02-10T20:54:00.000Z","key":1486760040000,"doc_count":17},{"key_as_string":"2017-02-10T20:55:00.000Z","key":1486760100000,"doc_count":8},{"key_as_string":"2017-02-10T20:56:00.000Z","key":1486760160000,"doc_count":10},{"key_as_string":"2017-02-10T20:57:00.000Z","key":1486760220000,"doc_count":11},{"key_as_string":"2017-02-10T20:58:00.000Z","key":1486760280000,"doc_count":9},{"key_as_string":"2017-02-10T20:59:00.000Z","key":1486760340000,"doc_count":14},{"key_as_string":"2017-02-10T21:00:00.000Z","key":1486760400000,"doc_count":11},{"key_as_string":"2017-02-10T21:01:00.000Z","key":1486760460000,"doc_count":13},{"key_as_string":"2017-02-10T21:02:00.000Z","key":1486760520000,"doc_count":8},{"key_as_string":"2017-02-10T21:03:00.000Z","key":1486760580000,"doc_count":13},{"key_as_string":"2017-02-10T21:04:00.000Z","key":1486760640000,"doc_count":10},{"key_as_string":"2017-02-10T21:05:00.000Z","key":1486760700000,"doc_count":11},{"key_as_string":"2017-02-10T21:06:00.000Z","key":1486760760000,"doc_count":5},{"key_as_string":"2017-02-10T21:07:00.000Z","key":1486760820000,"doc_count":11},{"key_as_string":"2017-02-10T21:08:00.000Z","key":1486760880000,"doc_count":12},{"key_as_string":"2017-02-10T21:09:00.000Z","key":1486760940000,"doc_count":7},{"key_as_string":"2017-02-10T21:10:00.000Z","key":1486761000000,"doc_count":13},{"key_as_string":"2017-02-10T21:11:00.000Z","key":1486761060000,"doc_count":10},{"key_as_string":"2017-02-10T21:12:00.000Z","key":1486761120000,"doc_count":13},{"key_as_string":"2017-02-10T21:13:00.000Z","key":1486761180000,"doc_count":11},{"key_as_string":"2017-02-10T21:14:00.000Z","key":1486761240000,"doc_count":11},{"key_as_string":"2017-02-10T21:15:00.000Z","key":1486761300000,"doc_count":9},{"key_as_string":"2017-02-10T21:16:00.000Z","key":1486761360000,"doc_count":13},{"key_as_string":"2017-02-10T21:17:00.000Z","key":1486761420000,"doc_count":10},{"key_as_string":"2017-02-10T21:18:00.000Z","key":1486761480000,"doc_count":12},{"key_as_string":"2017-02-10T21:19:00.000Z","key":1486761540000,"doc_count":8},{"key_as_string":"2017-02-10T21:20:00.000Z","key":1486761600000,"doc_count":11},{"key_as_string":"2017-02-10T21:21:00.000Z","key":1486761660000,"doc_count":13},{"key_as_string":"2017-02-10T21:22:00.000Z","key":1486761720000,"doc_count":9},{"key_as_string":"2017-02-10T21:23:00.000Z","key":1486761780000,"doc_count":13},{"key_as_string":"2017-02-10T21:24:00.000Z","key":1486761840000,"doc_count":8},{"key_as_string":"2017-02-10T21:25:00.000Z","key":1486761900000,"doc_count":8},{"key_as_string":"2017-02-10T21:26:00.000Z","key":1486761960000,"doc_count":12},{"key_as_string":"2017-02-10T21:27:00.000Z","key":1486762020000,"doc_count":10},{"key_as_string":"2017-02-10T21:28:00.000Z","key":1486762080000,"doc_count":16},{"key_as_string":"2017-02-10T21:29:00.000Z","key":1486762140000,"doc_count":7},{"key_as_string":"2017-02-10T21:30:00.000Z","key":1486762200000,"doc_count":10},{"key_as_string":"2017-02-10T21:31:00.000Z","key":1486762260000,"doc_count":16},{"key_as_string":"2017-02-10T21:32:00.000Z","key":1486762320000,"doc_count":14},{"key_as_string":"2017-02-10T21:33:00.000Z","key":1486762380000,"doc_count":6},{"key_as_string":"2017-02-10T21:34:00.000Z","key":1486762440000,"doc_count":9},{"key_as_string":"2017-02-10T21:35:00.000Z","key":1486762500000,"doc_count":11},{"key_as_string":"2017-02-10T21:36:00.000Z","key":1486762560000,"doc_count":12},{"key_as_string":"2017-02-10T21:37:00.000Z","key":1486762620000,"doc_count":16},{"key_as_string":"2017-02-10T21:38:00.000Z","key":1486762680000,"doc_count":11},{"key_as_string":"2017-02-10T21:39:00.000Z","key":1486762740000,"doc_count":10},{"key_as_string":"2017-02-10T21:40:00.000Z","key":1486762800000,"doc_count":11},{"key_as_string":"2017-02-10T21:41:00.000Z","key":1486762860000,"doc_count":11},{"key_as_string":"2017-02-10T21:42:00.000Z","key":1486762920000,"doc_count":13},{"key_as_string":"2017-02-10T21:43:00.000Z","key":1486762980000,"doc_count":11},{"key_as_string":"2017-02-10T21:44:00.000Z","key":1486763040000,"doc_count":14},{"key_as_string":"2017-02-10T21:45:00.000Z","key":1486763100000,"doc_count":9},{"key_as_string":"2017-02-10T21:46:00.000Z","key":1486763160000,"doc_count":9},{"key_as_string":"2017-02-10T21:47:00.000Z","key":1486763220000,"doc_count":16},{"key_as_string":"2017-02-10T21:48:00.000Z","key":1486763280000,"doc_count":6},{"key_as_string":"2017-02-10T21:49:00.000Z","key":1486763340000,"doc_count":12},{"key_as_string":"2017-02-10T21:50:00.000Z","key":1486763400000,"doc_count":10},{"key_as_string":"2017-02-10T21:51:00.000Z","key":1486763460000,"doc_count":11},{"key_as_string":"2017-02-10T21:52:00.000Z","key":1486763520000,"doc_count":11},{"key_as_string":"2017-02-10T21:53:00.000Z","key":1486763580000,"doc_count":12},{"key_as_string":"2017-02-10T21:54:00.000Z","key":1486763640000,"doc_count":15},{"key_as_string":"2017-02-10T21:55:00.000Z","key":1486763700000,"doc_count":12},{"key_as_string":"2017-02-10T21:56:00.000Z","key":1486763760000,"doc_count":9},{"key_as_string":"2017-02-10T21:57:00.000Z","key":1486763820000,"doc_count":13},{"key_as_string":"2017-02-10T21:58:00.000Z","key":1486763880000,"doc_count":8},{"key_as_string":"2017-02-10T21:59:00.000Z","key":1486763940000,"doc_count":9},{"key_as_string":"2017-02-10T22:00:00.000Z","key":1486764000000,"doc_count":20},{"key_as_string":"2017-02-10T22:01:00.000Z","key":1486764060000,"doc_count":11},{"key_as_string":"2017-02-10T22:02:00.000Z","key":1486764120000,"doc_count":14},{"key_as_string":"2017-02-10T22:03:00.000Z","key":1486764180000,"doc_count":8},{"key_as_string":"2017-02-10T22:04:00.000Z","key":1486764240000,"doc_count":13},{"key_as_string":"2017-02-10T22:05:00.000Z","key":1486764300000,"doc_count":8},{"key_as_string":"2017-02-10T22:06:00.000Z","key":1486764360000,"doc_count":14},{"key_as_string":"2017-02-10T22:07:00.000Z","key":1486764420000,"doc_count":9},{"key_as_string":"2017-02-10T22:08:00.000Z","key":1486764480000,"doc_count":8},{"key_as_string":"2017-02-10T22:09:00.000Z","key":1486764540000,"doc_count":10},{"key_as_string":"2017-02-10T22:10:00.000Z","key":1486764600000,"doc_count":18},{"key_as_string":"2017-02-10T22:11:00.000Z","key":1486764660000,"doc_count":8},{"key_as_string":"2017-02-10T22:12:00.000Z","key":1486764720000,"doc_count":9},{"key_as_string":"2017-02-10T22:13:00.000Z","key":1486764780000,"doc_count":13},{"key_as_string":"2017-02-10T22:14:00.000Z","key":1486764840000,"doc_count":9},{"key_as_string":"2017-02-10T22:15:00.000Z","key":1486764900000,"doc_count":12},{"key_as_string":"2017-02-10T22:16:00.000Z","key":1486764960000,"doc_count":7},{"key_as_string":"2017-02-10T22:17:00.000Z","key":1486765020000,"doc_count":11},{"key_as_string":"2017-02-10T22:18:00.000Z","key":1486765080000,"doc_count":9},{"key_as_string":"2017-02-10T22:19:00.000Z","key":1486765140000,"doc_count":6},{"key_as_string":"2017-02-10T22:20:00.000Z","key":1486765200000,"doc_count":10},{"key_as_string":"2017-02-10T22:21:00.000Z","key":1486765260000,"doc_count":9},{"key_as_string":"2017-02-10T22:22:00.000Z","key":1486765320000,"doc_count":12},{"key_as_string":"2017-02-10T22:23:00.000Z","key":1486765380000,"doc_count":13},{"key_as_string":"2017-02-10T22:24:00.000Z","key":1486765440000,"doc_count":12},{"key_as_string":"2017-02-10T22:25:00.000Z","key":1486765500000,"doc_count":9},{"key_as_string":"2017-02-10T22:26:00.000Z","key":1486765560000,"doc_count":17},{"key_as_string":"2017-02-10T22:27:00.000Z","key":1486765620000,"doc_count":8},{"key_as_string":"2017-02-10T22:28:00.000Z","key":1486765680000,"doc_count":15},{"key_as_string":"2017-02-10T22:29:00.000Z","key":1486765740000,"doc_count":10},{"key_as_string":"2017-02-10T22:30:00.000Z","key":1486765800000,"doc_count":7},{"key_as_string":"2017-02-10T22:31:00.000Z","key":1486765860000,"doc_count":15},{"key_as_string":"2017-02-10T22:32:00.000Z","key":1486765920000,"doc_count":8},{"key_as_string":"2017-02-10T22:33:00.000Z","key":1486765980000,"doc_count":10},{"key_as_string":"2017-02-10T22:34:00.000Z","key":1486766040000,"doc_count":13},{"key_as_string":"2017-02-10T22:35:00.000Z","key":1486766100000,"doc_count":12},{"key_as_string":"2017-02-10T22:36:00.000Z","key":1486766160000,"doc_count":10},{"key_as_string":"2017-02-10T22:37:00.000Z","key":1486766220000,"doc_count":13},{"key_as_string":"2017-02-10T22:38:00.000Z","key":1486766280000,"doc_count":8},{"key_as_string":"2017-02-10T22:39:00.000Z","key":1486766340000,"doc_count":14},{"key_as_string":"2017-02-10T22:40:00.000Z","key":1486766400000,"doc_count":14},{"key_as_string":"2017-02-10T22:41:00.000Z","key":1486766460000,"doc_count":13},{"key_as_string":"2017-02-10T22:42:00.000Z","key":1486766520000,"doc_count":9},{"key_as_string":"2017-02-10T22:43:00.000Z","key":1486766580000,"doc_count":11},{"key_as_string":"2017-02-10T22:44:00.000Z","key":1486766640000,"doc_count":12},{"key_as_string":"2017-02-10T22:45:00.000Z","key":1486766700000,"doc_count":11},{"key_as_string":"2017-02-10T22:46:00.000Z","key":1486766760000,"doc_count":5},{"key_as_string":"2017-02-10T22:47:00.000Z","key":1486766820000,"doc_count":12},{"key_as_string":"2017-02-10T22:48:00.000Z","key":1486766880000,"doc_count":8},{"key_as_string":"2017-02-10T22:49:00.000Z","key":1486766940000,"doc_count":13},{"key_as_string":"2017-02-10T22:50:00.000Z","key":1486767000000,"doc_count":9},{"key_as_string":"2017-02-10T22:51:00.000Z","key":1486767060000,"doc_count":14},{"key_as_string":"2017-02-10T22:52:00.000Z","key":1486767120000,"doc_count":8},{"key_as_string":"2017-02-10T22:53:00.000Z","key":1486767180000,"doc_count":14},{"key_as_string":"2017-02-10T22:54:00.000Z","key":1486767240000,"doc_count":5},{"key_as_string":"2017-02-10T22:55:00.000Z","key":1486767300000,"doc_count":15},{"key_as_string":"2017-02-10T22:56:00.000Z","key":1486767360000,"doc_count":10},{"key_as_string":"2017-02-10T22:57:00.000Z","key":1486767420000,"doc_count":17},{"key_as_string":"2017-02-10T22:58:00.000Z","key":1486767480000,"doc_count":10},{"key_as_string":"2017-02-10T22:59:00.000Z","key":1486767540000,"doc_count":14},{"key_as_string":"2017-02-10T23:00:00.000Z","key":1486767600000,"doc_count":5},{"key_as_string":"2017-02-10T23:01:00.000Z","key":1486767660000,"doc_count":12},{"key_as_string":"2017-02-10T23:02:00.000Z","key":1486767720000,"doc_count":12},{"key_as_string":"2017-02-10T23:03:00.000Z","key":1486767780000,"doc_count":9},{"key_as_string":"2017-02-10T23:04:00.000Z","key":1486767840000,"doc_count":7},{"key_as_string":"2017-02-10T23:05:00.000Z","key":1486767900000,"doc_count":12},{"key_as_string":"2017-02-10T23:06:00.000Z","key":1486767960000,"doc_count":7},{"key_as_string":"2017-02-10T23:07:00.000Z","key":1486768020000,"doc_count":14},{"key_as_string":"2017-02-10T23:08:00.000Z","key":1486768080000,"doc_count":10},{"key_as_string":"2017-02-10T23:09:00.000Z","key":1486768140000,"doc_count":9},{"key_as_string":"2017-02-10T23:10:00.000Z","key":1486768200000,"doc_count":15},{"key_as_string":"2017-02-10T23:11:00.000Z","key":1486768260000,"doc_count":13},{"key_as_string":"2017-02-10T23:12:00.000Z","key":1486768320000,"doc_count":10},{"key_as_string":"2017-02-10T23:13:00.000Z","key":1486768380000,"doc_count":8},{"key_as_string":"2017-02-10T23:14:00.000Z","key":1486768440000,"doc_count":10},{"key_as_string":"2017-02-10T23:15:00.000Z","key":1486768500000,"doc_count":13},{"key_as_string":"2017-02-10T23:16:00.000Z","key":1486768560000,"doc_count":9},{"key_as_string":"2017-02-10T23:17:00.000Z","key":1486768620000,"doc_count":8},{"key_as_string":"2017-02-10T23:18:00.000Z","key":1486768680000,"doc_count":7},{"key_as_string":"2017-02-10T23:19:00.000Z","key":1486768740000,"doc_count":12},{"key_as_string":"2017-02-10T23:20:00.000Z","key":1486768800000,"doc_count":9},{"key_as_string":"2017-02-10T23:21:00.000Z","key":1486768860000,"doc_count":11},{"key_as_string":"2017-02-10T23:22:00.000Z","key":1486768920000,"doc_count":8},{"key_as_string":"2017-02-10T23:23:00.000Z","key":1486768980000,"doc_count":9},{"key_as_string":"2017-02-10T23:24:00.000Z","key":1486769040000,"doc_count":14},{"key_as_string":"2017-02-10T23:25:00.000Z","key":1486769100000,"doc_count":9},{"key_as_string":"2017-02-10T23:26:00.000Z","key":1486769160000,"doc_count":8},{"key_as_string":"2017-02-10T23:27:00.000Z","key":1486769220000,"doc_count":7},{"key_as_string":"2017-02-10T23:28:00.000Z","key":1486769280000,"doc_count":17},{"key_as_string":"2017-02-10T23:29:00.000Z","key":1486769340000,"doc_count":5},{"key_as_string":"2017-02-10T23:30:00.000Z","key":1486769400000,"doc_count":16},{"key_as_string":"2017-02-10T23:31:00.000Z","key":1486769460000,"doc_count":14},{"key_as_string":"2017-02-10T23:32:00.000Z","key":1486769520000,"doc_count":6},{"key_as_string":"2017-02-10T23:33:00.000Z","key":1486769580000,"doc_count":8},{"key_as_string":"2017-02-10T23:34:00.000Z","key":1486769640000,"doc_count":15},{"key_as_string":"2017-02-10T23:35:00.000Z","key":1486769700000,"doc_count":9},{"key_as_string":"2017-02-10T23:36:00.000Z","key":1486769760000,"doc_count":11},{"key_as_string":"2017-02-10T23:37:00.000Z","key":1486769820000,"doc_count":13},{"key_as_string":"2017-02-10T23:38:00.000Z","key":1486769880000,"doc_count":12},{"key_as_string":"2017-02-10T23:39:00.000Z","key":1486769940000,"doc_count":12},{"key_as_string":"2017-02-10T23:40:00.000Z","key":1486770000000,"doc_count":9},{"key_as_string":"2017-02-10T23:41:00.000Z","key":1486770060000,"doc_count":11},{"key_as_string":"2017-02-10T23:42:00.000Z","key":1486770120000,"doc_count":10},{"key_as_string":"2017-02-10T23:43:00.000Z","key":1486770180000,"doc_count":16},{"key_as_string":"2017-02-10T23:44:00.000Z","key":1486770240000,"doc_count":8},{"key_as_string":"2017-02-10T23:45:00.000Z","key":1486770300000,"doc_count":8},{"key_as_string":"2017-02-10T23:46:00.000Z","key":1486770360000,"doc_count":8},{"key_as_string":"2017-02-10T23:47:00.000Z","key":1486770420000,"doc_count":11},{"key_as_string":"2017-02-10T23:48:00.000Z","key":1486770480000,"doc_count":10},{"key_as_string":"2017-02-10T23:49:00.000Z","key":1486770540000,"doc_count":10},{"key_as_string":"2017-02-10T23:50:00.000Z","key":1486770600000,"doc_count":13},{"key_as_string":"2017-02-10T23:51:00.000Z","key":1486770660000,"doc_count":6},{"key_as_string":"2017-02-10T23:52:00.000Z","key":1486770720000,"doc_count":16},{"key_as_string":"2017-02-10T23:53:00.000Z","key":1486770780000,"doc_count":10},{"key_as_string":"2017-02-10T23:54:00.000Z","key":1486770840000,"doc_count":11},{"key_as_string":"2017-02-10T23:55:00.000Z","key":1486770900000,"doc_count":14},{"key_as_string":"2017-02-10T23:56:00.000Z","key":1486770960000,"doc_count":9},{"key_as_string":"2017-02-10T23:57:00.000Z","key":1486771020000,"doc_count":9},{"key_as_string":"2017-02-10T23:58:00.000Z","key":1486771080000,"doc_count":11},{"key_as_string":"2017-02-10T23:59:00.000Z","key":1486771140000,"doc_count":6},{"key_as_string":"2017-02-11T00:00:00.000Z","key":1486771200000,"doc_count":12},{"key_as_string":"2017-02-11T00:01:00.000Z","key":1486771260000,"doc_count":14},{"key_as_string":"2017-02-11T00:02:00.000Z","key":1486771320000,"doc_count":9},{"key_as_string":"2017-02-11T00:03:00.000Z","key":1486771380000,"doc_count":3},{"key_as_string":"2017-02-11T00:04:00.000Z","key":1486771440000,"doc_count":15},{"key_as_string":"2017-02-11T00:05:00.000Z","key":1486771500000,"doc_count":10},{"key_as_string":"2017-02-11T00:06:00.000Z","key":1486771560000,"doc_count":11},{"key_as_string":"2017-02-11T00:07:00.000Z","key":1486771620000,"doc_count":6},{"key_as_string":"2017-02-11T00:08:00.000Z","key":1486771680000,"doc_count":11},{"key_as_string":"2017-02-11T00:09:00.000Z","key":1486771740000,"doc_count":10},{"key_as_string":"2017-02-11T00:10:00.000Z","key":1486771800000,"doc_count":7},{"key_as_string":"2017-02-11T00:11:00.000Z","key":1486771860000,"doc_count":13},{"key_as_string":"2017-02-11T00:12:00.000Z","key":1486771920000,"doc_count":9},{"key_as_string":"2017-02-11T00:13:00.000Z","key":1486771980000,"doc_count":10},{"key_as_string":"2017-02-11T00:14:00.000Z","key":1486772040000,"doc_count":17},{"key_as_string":"2017-02-11T00:15:00.000Z","key":1486772100000,"doc_count":7},{"key_as_string":"2017-02-11T00:16:00.000Z","key":1486772160000,"doc_count":12},{"key_as_string":"2017-02-11T00:17:00.000Z","key":1486772220000,"doc_count":13},{"key_as_string":"2017-02-11T00:18:00.000Z","key":1486772280000,"doc_count":4},{"key_as_string":"2017-02-11T00:19:00.000Z","key":1486772340000,"doc_count":15},{"key_as_string":"2017-02-11T00:20:00.000Z","key":1486772400000,"doc_count":13},{"key_as_string":"2017-02-11T00:21:00.000Z","key":1486772460000,"doc_count":8},{"key_as_string":"2017-02-11T00:22:00.000Z","key":1486772520000,"doc_count":8},{"key_as_string":"2017-02-11T00:23:00.000Z","key":1486772580000,"doc_count":12},{"key_as_string":"2017-02-11T00:24:00.000Z","key":1486772640000,"doc_count":9},{"key_as_string":"2017-02-11T00:25:00.000Z","key":1486772700000,"doc_count":8},{"key_as_string":"2017-02-11T00:26:00.000Z","key":1486772760000,"doc_count":13},{"key_as_string":"2017-02-11T00:27:00.000Z","key":1486772820000,"doc_count":10},{"key_as_string":"2017-02-11T00:28:00.000Z","key":1486772880000,"doc_count":11},{"key_as_string":"2017-02-11T00:29:00.000Z","key":1486772940000,"doc_count":7},{"key_as_string":"2017-02-11T00:30:00.000Z","key":1486773000000,"doc_count":14},{"key_as_string":"2017-02-11T00:31:00.000Z","key":1486773060000,"doc_count":8},{"key_as_string":"2017-02-11T00:32:00.000Z","key":1486773120000,"doc_count":9},{"key_as_string":"2017-02-11T00:33:00.000Z","key":1486773180000,"doc_count":14},{"key_as_string":"2017-02-11T00:34:00.000Z","key":1486773240000,"doc_count":7},{"key_as_string":"2017-02-11T00:35:00.000Z","key":1486773300000,"doc_count":10},{"key_as_string":"2017-02-11T00:36:00.000Z","key":1486773360000,"doc_count":11},{"key_as_string":"2017-02-11T00:37:00.000Z","key":1486773420000,"doc_count":15},{"key_as_string":"2017-02-11T00:38:00.000Z","key":1486773480000,"doc_count":9},{"key_as_string":"2017-02-11T00:39:00.000Z","key":1486773540000,"doc_count":11},{"key_as_string":"2017-02-11T00:40:00.000Z","key":1486773600000,"doc_count":8},{"key_as_string":"2017-02-11T00:41:00.000Z","key":1486773660000,"doc_count":9},{"key_as_string":"2017-02-11T00:42:00.000Z","key":1486773720000,"doc_count":10},{"key_as_string":"2017-02-11T00:43:00.000Z","key":1486773780000,"doc_count":14},{"key_as_string":"2017-02-11T00:44:00.000Z","key":1486773840000,"doc_count":7},{"key_as_string":"2017-02-11T00:45:00.000Z","key":1486773900000,"doc_count":12},{"key_as_string":"2017-02-11T00:46:00.000Z","key":1486773960000,"doc_count":12},{"key_as_string":"2017-02-11T00:47:00.000Z","key":1486774020000,"doc_count":9},{"key_as_string":"2017-02-11T00:48:00.000Z","key":1486774080000,"doc_count":11},{"key_as_string":"2017-02-11T00:49:00.000Z","key":1486774140000,"doc_count":6},{"key_as_string":"2017-02-11T00:50:00.000Z","key":1486774200000,"doc_count":11},{"key_as_string":"2017-02-11T00:51:00.000Z","key":1486774260000,"doc_count":14},{"key_as_string":"2017-02-11T00:52:00.000Z","key":1486774320000,"doc_count":6},{"key_as_string":"2017-02-11T00:53:00.000Z","key":1486774380000,"doc_count":14},{"key_as_string":"2017-02-11T00:54:00.000Z","key":1486774440000,"doc_count":9},{"key_as_string":"2017-02-11T00:55:00.000Z","key":1486774500000,"doc_count":7},{"key_as_string":"2017-02-11T00:56:00.000Z","key":1486774560000,"doc_count":9},{"key_as_string":"2017-02-11T00:57:00.000Z","key":1486774620000,"doc_count":11},{"key_as_string":"2017-02-11T00:58:00.000Z","key":1486774680000,"doc_count":11},{"key_as_string":"2017-02-11T00:59:00.000Z","key":1486774740000,"doc_count":9},{"key_as_string":"2017-02-11T01:00:00.000Z","key":1486774800000,"doc_count":9},{"key_as_string":"2017-02-11T01:01:00.000Z","key":1486774860000,"doc_count":9},{"key_as_string":"2017-02-11T01:02:00.000Z","key":1486774920000,"doc_count":9},{"key_as_string":"2017-02-11T01:03:00.000Z","key":1486774980000,"doc_count":13},{"key_as_string":"2017-02-11T01:04:00.000Z","key":1486775040000,"doc_count":8},{"key_as_string":"2017-02-11T01:05:00.000Z","key":1486775100000,"doc_count":14},{"key_as_string":"2017-02-11T01:06:00.000Z","key":1486775160000,"doc_count":7},{"key_as_string":"2017-02-11T01:07:00.000Z","key":1486775220000,"doc_count":13},{"key_as_string":"2017-02-11T01:08:00.000Z","key":1486775280000,"doc_count":9},{"key_as_string":"2017-02-11T01:09:00.000Z","key":1486775340000,"doc_count":11},{"key_as_string":"2017-02-11T01:10:00.000Z","key":1486775400000,"doc_count":11},{"key_as_string":"2017-02-11T01:11:00.000Z","key":1486775460000,"doc_count":15},{"key_as_string":"2017-02-11T01:12:00.000Z","key":1486775520000,"doc_count":8},{"key_as_string":"2017-02-11T01:13:00.000Z","key":1486775580000,"doc_count":7},{"key_as_string":"2017-02-11T01:14:00.000Z","key":1486775640000,"doc_count":16},{"key_as_string":"2017-02-11T01:15:00.000Z","key":1486775700000,"doc_count":6},{"key_as_string":"2017-02-11T01:16:00.000Z","key":1486775760000,"doc_count":13},{"key_as_string":"2017-02-11T01:17:00.000Z","key":1486775820000,"doc_count":9},{"key_as_string":"2017-02-11T01:18:00.000Z","key":1486775880000,"doc_count":7},{"key_as_string":"2017-02-11T01:19:00.000Z","key":1486775940000,"doc_count":12},{"key_as_string":"2017-02-11T01:20:00.000Z","key":1486776000000,"doc_count":8},{"key_as_string":"2017-02-11T01:21:00.000Z","key":1486776060000,"doc_count":12},{"key_as_string":"2017-02-11T01:22:00.000Z","key":1486776120000,"doc_count":16},{"key_as_string":"2017-02-11T01:23:00.000Z","key":1486776180000,"doc_count":9},{"key_as_string":"2017-02-11T01:24:00.000Z","key":1486776240000,"doc_count":6},{"key_as_string":"2017-02-11T01:25:00.000Z","key":1486776300000,"doc_count":13},{"key_as_string":"2017-02-11T01:26:00.000Z","key":1486776360000,"doc_count":12},{"key_as_string":"2017-02-11T01:27:00.000Z","key":1486776420000,"doc_count":10},{"key_as_string":"2017-02-11T01:28:00.000Z","key":1486776480000,"doc_count":6},{"key_as_string":"2017-02-11T01:29:00.000Z","key":1486776540000,"doc_count":13},{"key_as_string":"2017-02-11T01:30:00.000Z","key":1486776600000,"doc_count":8},{"key_as_string":"2017-02-11T01:31:00.000Z","key":1486776660000,"doc_count":16},{"key_as_string":"2017-02-11T01:32:00.000Z","key":1486776720000,"doc_count":8},{"key_as_string":"2017-02-11T01:33:00.000Z","key":1486776780000,"doc_count":4},{"key_as_string":"2017-02-11T01:34:00.000Z","key":1486776840000,"doc_count":9},{"key_as_string":"2017-02-11T01:35:00.000Z","key":1486776900000,"doc_count":11},{"key_as_string":"2017-02-11T01:36:00.000Z","key":1486776960000,"doc_count":10},{"key_as_string":"2017-02-11T01:37:00.000Z","key":1486777020000,"doc_count":17},{"key_as_string":"2017-02-11T01:38:00.000Z","key":1486777080000,"doc_count":11},{"key_as_string":"2017-02-11T01:39:00.000Z","key":1486777140000,"doc_count":10},{"key_as_string":"2017-02-11T01:40:00.000Z","key":1486777200000,"doc_count":12},{"key_as_string":"2017-02-11T01:41:00.000Z","key":1486777260000,"doc_count":11},{"key_as_string":"2017-02-11T01:42:00.000Z","key":1486777320000,"doc_count":11},{"key_as_string":"2017-02-11T01:43:00.000Z","key":1486777380000,"doc_count":6},{"key_as_string":"2017-02-11T01:44:00.000Z","key":1486777440000,"doc_count":13},{"key_as_string":"2017-02-11T01:45:00.000Z","key":1486777500000,"doc_count":12},{"key_as_string":"2017-02-11T01:46:00.000Z","key":1486777560000,"doc_count":12},{"key_as_string":"2017-02-11T01:47:00.000Z","key":1486777620000,"doc_count":7},{"key_as_string":"2017-02-11T01:48:00.000Z","key":1486777680000,"doc_count":12},{"key_as_string":"2017-02-11T01:49:00.000Z","key":1486777740000,"doc_count":11},{"key_as_string":"2017-02-11T01:50:00.000Z","key":1486777800000,"doc_count":13},{"key_as_string":"2017-02-11T01:51:00.000Z","key":1486777860000,"doc_count":13},{"key_as_string":"2017-02-11T01:52:00.000Z","key":1486777920000,"doc_count":11},{"key_as_string":"2017-02-11T01:53:00.000Z","key":1486777980000,"doc_count":11},{"key_as_string":"2017-02-11T01:54:00.000Z","key":1486778040000,"doc_count":7},{"key_as_string":"2017-02-11T01:55:00.000Z","key":1486778100000,"doc_count":13},{"key_as_string":"2017-02-11T01:56:00.000Z","key":1486778160000,"doc_count":12},{"key_as_string":"2017-02-11T01:57:00.000Z","key":1486778220000,"doc_count":12},{"key_as_string":"2017-02-11T01:58:00.000Z","key":1486778280000,"doc_count":10},{"key_as_string":"2017-02-11T01:59:00.000Z","key":1486778340000,"doc_count":13},{"key_as_string":"2017-02-11T02:00:00.000Z","key":1486778400000,"doc_count":10},{"key_as_string":"2017-02-11T02:01:00.000Z","key":1486778460000,"doc_count":8},{"key_as_string":"2017-02-11T02:02:00.000Z","key":1486778520000,"doc_count":8},{"key_as_string":"2017-02-11T02:03:00.000Z","key":1486778580000,"doc_count":10},{"key_as_string":"2017-02-11T02:04:00.000Z","key":1486778640000,"doc_count":10},{"key_as_string":"2017-02-11T02:05:00.000Z","key":1486778700000,"doc_count":6},{"key_as_string":"2017-02-11T02:06:00.000Z","key":1486778760000,"doc_count":12},{"key_as_string":"2017-02-11T02:07:00.000Z","key":1486778820000,"doc_count":11},{"key_as_string":"2017-02-11T02:08:00.000Z","key":1486778880000,"doc_count":7},{"key_as_string":"2017-02-11T02:09:00.000Z","key":1486778940000,"doc_count":13},{"key_as_string":"2017-02-11T02:10:00.000Z","key":1486779000000,"doc_count":9},{"key_as_string":"2017-02-11T02:11:00.000Z","key":1486779060000,"doc_count":14},{"key_as_string":"2017-02-11T02:12:00.000Z","key":1486779120000,"doc_count":9},{"key_as_string":"2017-02-11T02:13:00.000Z","key":1486779180000,"doc_count":10},{"key_as_string":"2017-02-11T02:14:00.000Z","key":1486779240000,"doc_count":12},{"key_as_string":"2017-02-11T02:15:00.000Z","key":1486779300000,"doc_count":5},{"key_as_string":"2017-02-11T02:16:00.000Z","key":1486779360000,"doc_count":9},{"key_as_string":"2017-02-11T02:17:00.000Z","key":1486779420000,"doc_count":14},{"key_as_string":"2017-02-11T02:18:00.000Z","key":1486779480000,"doc_count":5},{"key_as_string":"2017-02-11T02:19:00.000Z","key":1486779540000,"doc_count":18},{"key_as_string":"2017-02-11T02:20:00.000Z","key":1486779600000,"doc_count":9},{"key_as_string":"2017-02-11T02:21:00.000Z","key":1486779660000,"doc_count":7},{"key_as_string":"2017-02-11T02:22:00.000Z","key":1486779720000,"doc_count":14},{"key_as_string":"2017-02-11T02:23:00.000Z","key":1486779780000,"doc_count":8},{"key_as_string":"2017-02-11T02:24:00.000Z","key":1486779840000,"doc_count":9},{"key_as_string":"2017-02-11T02:25:00.000Z","key":1486779900000,"doc_count":10},{"key_as_string":"2017-02-11T02:26:00.000Z","key":1486779960000,"doc_count":15},{"key_as_string":"2017-02-11T02:27:00.000Z","key":1486780020000,"doc_count":12},{"key_as_string":"2017-02-11T02:28:00.000Z","key":1486780080000,"doc_count":8},{"key_as_string":"2017-02-11T02:29:00.000Z","key":1486780140000,"doc_count":7},{"key_as_string":"2017-02-11T02:30:00.000Z","key":1486780200000,"doc_count":13},{"key_as_string":"2017-02-11T02:31:00.000Z","key":1486780260000,"doc_count":9},{"key_as_string":"2017-02-11T02:32:00.000Z","key":1486780320000,"doc_count":11},{"key_as_string":"2017-02-11T02:33:00.000Z","key":1486780380000,"doc_count":9},{"key_as_string":"2017-02-11T02:34:00.000Z","key":1486780440000,"doc_count":12},{"key_as_string":"2017-02-11T02:35:00.000Z","key":1486780500000,"doc_count":11},{"key_as_string":"2017-02-11T02:36:00.000Z","key":1486780560000,"doc_count":4},{"key_as_string":"2017-02-11T02:37:00.000Z","key":1486780620000,"doc_count":12},{"key_as_string":"2017-02-11T02:38:00.000Z","key":1486780680000,"doc_count":6},{"key_as_string":"2017-02-11T02:39:00.000Z","key":1486780740000,"doc_count":12},{"key_as_string":"2017-02-11T02:40:00.000Z","key":1486780800000,"doc_count":12},{"key_as_string":"2017-02-11T02:41:00.000Z","key":1486780860000,"doc_count":12},{"key_as_string":"2017-02-11T02:42:00.000Z","key":1486780920000,"doc_count":13},{"key_as_string":"2017-02-11T02:43:00.000Z","key":1486780980000,"doc_count":12},{"key_as_string":"2017-02-11T02:44:00.000Z","key":1486781040000,"doc_count":13},{"key_as_string":"2017-02-11T02:45:00.000Z","key":1486781100000,"doc_count":11},{"key_as_string":"2017-02-11T02:46:00.000Z","key":1486781160000,"doc_count":5},{"key_as_string":"2017-02-11T02:47:00.000Z","key":1486781220000,"doc_count":14},{"key_as_string":"2017-02-11T02:48:00.000Z","key":1486781280000,"doc_count":8},{"key_as_string":"2017-02-11T02:49:00.000Z","key":1486781340000,"doc_count":14},{"key_as_string":"2017-02-11T02:50:00.000Z","key":1486781400000,"doc_count":16},{"key_as_string":"2017-02-11T02:51:00.000Z","key":1486781460000,"doc_count":7},{"key_as_string":"2017-02-11T02:52:00.000Z","key":1486781520000,"doc_count":7},{"key_as_string":"2017-02-11T02:53:00.000Z","key":1486781580000,"doc_count":15},{"key_as_string":"2017-02-11T02:54:00.000Z","key":1486781640000,"doc_count":13},{"key_as_string":"2017-02-11T02:55:00.000Z","key":1486781700000,"doc_count":5},{"key_as_string":"2017-02-11T02:56:00.000Z","key":1486781760000,"doc_count":8},{"key_as_string":"2017-02-11T02:57:00.000Z","key":1486781820000,"doc_count":8},{"key_as_string":"2017-02-11T02:58:00.000Z","key":1486781880000,"doc_count":16},{"key_as_string":"2017-02-11T02:59:00.000Z","key":1486781940000,"doc_count":9},{"key_as_string":"2017-02-11T03:00:00.000Z","key":1486782000000,"doc_count":9},{"key_as_string":"2017-02-11T03:01:00.000Z","key":1486782060000,"doc_count":13},{"key_as_string":"2017-02-11T03:02:00.000Z","key":1486782120000,"doc_count":14},{"key_as_string":"2017-02-11T03:03:00.000Z","key":1486782180000,"doc_count":10},{"key_as_string":"2017-02-11T03:04:00.000Z","key":1486782240000,"doc_count":8},{"key_as_string":"2017-02-11T03:05:00.000Z","key":1486782300000,"doc_count":14},{"key_as_string":"2017-02-11T03:06:00.000Z","key":1486782360000,"doc_count":11},{"key_as_string":"2017-02-11T03:07:00.000Z","key":1486782420000,"doc_count":11},{"key_as_string":"2017-02-11T03:08:00.000Z","key":1486782480000,"doc_count":8},{"key_as_string":"2017-02-11T03:09:00.000Z","key":1486782540000,"doc_count":13},{"key_as_string":"2017-02-11T03:10:00.000Z","key":1486782600000,"doc_count":12},{"key_as_string":"2017-02-11T03:11:00.000Z","key":1486782660000,"doc_count":9},{"key_as_string":"2017-02-11T03:12:00.000Z","key":1486782720000,"doc_count":9},{"key_as_string":"2017-02-11T03:13:00.000Z","key":1486782780000,"doc_count":9},{"key_as_string":"2017-02-11T03:14:00.000Z","key":1486782840000,"doc_count":11},{"key_as_string":"2017-02-11T03:15:00.000Z","key":1486782900000,"doc_count":9},{"key_as_string":"2017-02-11T03:16:00.000Z","key":1486782960000,"doc_count":20},{"key_as_string":"2017-02-11T03:17:00.000Z","key":1486783020000,"doc_count":7},{"key_as_string":"2017-02-11T03:18:00.000Z","key":1486783080000,"doc_count":11},{"key_as_string":"2017-02-11T03:19:00.000Z","key":1486783140000,"doc_count":10},{"key_as_string":"2017-02-11T03:20:00.000Z","key":1486783200000,"doc_count":8},{"key_as_string":"2017-02-11T03:21:00.000Z","key":1486783260000,"doc_count":16},{"key_as_string":"2017-02-11T03:22:00.000Z","key":1486783320000,"doc_count":5},{"key_as_string":"2017-02-11T03:23:00.000Z","key":1486783380000,"doc_count":12},{"key_as_string":"2017-02-11T03:24:00.000Z","key":1486783440000,"doc_count":8},{"key_as_string":"2017-02-11T03:25:00.000Z","key":1486783500000,"doc_count":17},{"key_as_string":"2017-02-11T03:26:00.000Z","key":1486783560000,"doc_count":8},{"key_as_string":"2017-02-11T03:27:00.000Z","key":1486783620000,"doc_count":14},{"key_as_string":"2017-02-11T03:28:00.000Z","key":1486783680000,"doc_count":7},{"key_as_string":"2017-02-11T03:29:00.000Z","key":1486783740000,"doc_count":12},{"key_as_string":"2017-02-11T03:30:00.000Z","key":1486783800000,"doc_count":13},{"key_as_string":"2017-02-11T03:31:00.000Z","key":1486783860000,"doc_count":9},{"key_as_string":"2017-02-11T03:32:00.000Z","key":1486783920000,"doc_count":5},{"key_as_string":"2017-02-11T03:33:00.000Z","key":1486783980000,"doc_count":10},{"key_as_string":"2017-02-11T03:34:00.000Z","key":1486784040000,"doc_count":14},{"key_as_string":"2017-02-11T03:35:00.000Z","key":1486784100000,"doc_count":13},{"key_as_string":"2017-02-11T03:36:00.000Z","key":1486784160000,"doc_count":9},{"key_as_string":"2017-02-11T03:37:00.000Z","key":1486784220000,"doc_count":10},{"key_as_string":"2017-02-11T03:38:00.000Z","key":1486784280000,"doc_count":10},{"key_as_string":"2017-02-11T03:39:00.000Z","key":1486784340000,"doc_count":12},{"key_as_string":"2017-02-11T03:40:00.000Z","key":1486784400000,"doc_count":11},{"key_as_string":"2017-02-11T03:41:00.000Z","key":1486784460000,"doc_count":11},{"key_as_string":"2017-02-11T03:42:00.000Z","key":1486784520000,"doc_count":7},{"key_as_string":"2017-02-11T03:43:00.000Z","key":1486784580000,"doc_count":18},{"key_as_string":"2017-02-11T03:44:00.000Z","key":1486784640000,"doc_count":6},{"key_as_string":"2017-02-11T03:45:00.000Z","key":1486784700000,"doc_count":12},{"key_as_string":"2017-02-11T03:46:00.000Z","key":1486784760000,"doc_count":13},{"key_as_string":"2017-02-11T03:47:00.000Z","key":1486784820000,"doc_count":13},{"key_as_string":"2017-02-11T03:48:00.000Z","key":1486784880000,"doc_count":15},{"key_as_string":"2017-02-11T03:49:00.000Z","key":1486784940000,"doc_count":11},{"key_as_string":"2017-02-11T03:50:00.000Z","key":1486785000000,"doc_count":9},{"key_as_string":"2017-02-11T03:51:00.000Z","key":1486785060000,"doc_count":14},{"key_as_string":"2017-02-11T03:52:00.000Z","key":1486785120000,"doc_count":7},{"key_as_string":"2017-02-11T03:53:00.000Z","key":1486785180000,"doc_count":11},{"key_as_string":"2017-02-11T03:54:00.000Z","key":1486785240000,"doc_count":8},{"key_as_string":"2017-02-11T03:55:00.000Z","key":1486785300000,"doc_count":7},{"key_as_string":"2017-02-11T03:56:00.000Z","key":1486785360000,"doc_count":10},{"key_as_string":"2017-02-11T03:57:00.000Z","key":1486785420000,"doc_count":13},{"key_as_string":"2017-02-11T03:58:00.000Z","key":1486785480000,"doc_count":11},{"key_as_string":"2017-02-11T03:59:00.000Z","key":1486785540000,"doc_count":8},{"key_as_string":"2017-02-11T04:00:00.000Z","key":1486785600000,"doc_count":11},{"key_as_string":"2017-02-11T04:01:00.000Z","key":1486785660000,"doc_count":9},{"key_as_string":"2017-02-11T04:02:00.000Z","key":1486785720000,"doc_count":13},{"key_as_string":"2017-02-11T04:03:00.000Z","key":1486785780000,"doc_count":14},{"key_as_string":"2017-02-11T04:04:00.000Z","key":1486785840000,"doc_count":13},{"key_as_string":"2017-02-11T04:05:00.000Z","key":1486785900000,"doc_count":9},{"key_as_string":"2017-02-11T04:06:00.000Z","key":1486785960000,"doc_count":8},{"key_as_string":"2017-02-11T04:07:00.000Z","key":1486786020000,"doc_count":8},{"key_as_string":"2017-02-11T04:08:00.000Z","key":1486786080000,"doc_count":14},{"key_as_string":"2017-02-11T04:09:00.000Z","key":1486786140000,"doc_count":10},{"key_as_string":"2017-02-11T04:10:00.000Z","key":1486786200000,"doc_count":12},{"key_as_string":"2017-02-11T04:11:00.000Z","key":1486786260000,"doc_count":9},{"key_as_string":"2017-02-11T04:12:00.000Z","key":1486786320000,"doc_count":11},{"key_as_string":"2017-02-11T04:13:00.000Z","key":1486786380000,"doc_count":19},{"key_as_string":"2017-02-11T04:14:00.000Z","key":1486786440000,"doc_count":9},{"key_as_string":"2017-02-11T04:15:00.000Z","key":1486786500000,"doc_count":9},{"key_as_string":"2017-02-11T04:16:00.000Z","key":1486786560000,"doc_count":15},{"key_as_string":"2017-02-11T04:17:00.000Z","key":1486786620000,"doc_count":17},{"key_as_string":"2017-02-11T04:18:00.000Z","key":1486786680000,"doc_count":15},{"key_as_string":"2017-02-11T04:19:00.000Z","key":1486786740000,"doc_count":8},{"key_as_string":"2017-02-11T04:20:00.000Z","key":1486786800000,"doc_count":11},{"key_as_string":"2017-02-11T04:21:00.000Z","key":1486786860000,"doc_count":13},{"key_as_string":"2017-02-11T04:22:00.000Z","key":1486786920000,"doc_count":11},{"key_as_string":"2017-02-11T04:23:00.000Z","key":1486786980000,"doc_count":7},{"key_as_string":"2017-02-11T04:24:00.000Z","key":1486787040000,"doc_count":14},{"key_as_string":"2017-02-11T04:25:00.000Z","key":1486787100000,"doc_count":11},{"key_as_string":"2017-02-11T04:26:00.000Z","key":1486787160000,"doc_count":9},{"key_as_string":"2017-02-11T04:27:00.000Z","key":1486787220000,"doc_count":6},{"key_as_string":"2017-02-11T04:28:00.000Z","key":1486787280000,"doc_count":17},{"key_as_string":"2017-02-11T04:29:00.000Z","key":1486787340000,"doc_count":9},{"key_as_string":"2017-02-11T04:30:00.000Z","key":1486787400000,"doc_count":9},{"key_as_string":"2017-02-11T04:31:00.000Z","key":1486787460000,"doc_count":8},{"key_as_string":"2017-02-11T04:32:00.000Z","key":1486787520000,"doc_count":12},{"key_as_string":"2017-02-11T04:33:00.000Z","key":1486787580000,"doc_count":11},{"key_as_string":"2017-02-11T04:34:00.000Z","key":1486787640000,"doc_count":10},{"key_as_string":"2017-02-11T04:35:00.000Z","key":1486787700000,"doc_count":11},{"key_as_string":"2017-02-11T04:36:00.000Z","key":1486787760000,"doc_count":11},{"key_as_string":"2017-02-11T04:37:00.000Z","key":1486787820000,"doc_count":15},{"key_as_string":"2017-02-11T04:38:00.000Z","key":1486787880000,"doc_count":11},{"key_as_string":"2017-02-11T04:39:00.000Z","key":1486787940000,"doc_count":10},{"key_as_string":"2017-02-11T04:40:00.000Z","key":1486788000000,"doc_count":10},{"key_as_string":"2017-02-11T04:41:00.000Z","key":1486788060000,"doc_count":10},{"key_as_string":"2017-02-11T04:42:00.000Z","key":1486788120000,"doc_count":8},{"key_as_string":"2017-02-11T04:43:00.000Z","key":1486788180000,"doc_count":9},{"key_as_string":"2017-02-11T04:44:00.000Z","key":1486788240000,"doc_count":8},{"key_as_string":"2017-02-11T04:45:00.000Z","key":1486788300000,"doc_count":19},{"key_as_string":"2017-02-11T04:46:00.000Z","key":1486788360000,"doc_count":11},{"key_as_string":"2017-02-11T04:47:00.000Z","key":1486788420000,"doc_count":11},{"key_as_string":"2017-02-11T04:48:00.000Z","key":1486788480000,"doc_count":10},{"key_as_string":"2017-02-11T04:49:00.000Z","key":1486788540000,"doc_count":9},{"key_as_string":"2017-02-11T04:50:00.000Z","key":1486788600000,"doc_count":9},{"key_as_string":"2017-02-11T04:51:00.000Z","key":1486788660000,"doc_count":15},{"key_as_string":"2017-02-11T04:52:00.000Z","key":1486788720000,"doc_count":11},{"key_as_string":"2017-02-11T04:53:00.000Z","key":1486788780000,"doc_count":11},{"key_as_string":"2017-02-11T04:54:00.000Z","key":1486788840000,"doc_count":12},{"key_as_string":"2017-02-11T04:55:00.000Z","key":1486788900000,"doc_count":12},{"key_as_string":"2017-02-11T04:56:00.000Z","key":1486788960000,"doc_count":9},{"key_as_string":"2017-02-11T04:57:00.000Z","key":1486789020000,"doc_count":16},{"key_as_string":"2017-02-11T04:58:00.000Z","key":1486789080000,"doc_count":11},{"key_as_string":"2017-02-11T04:59:00.000Z","key":1486789140000,"doc_count":10},{"key_as_string":"2017-02-11T05:00:00.000Z","key":1486789200000,"doc_count":9},{"key_as_string":"2017-02-11T05:01:00.000Z","key":1486789260000,"doc_count":14},{"key_as_string":"2017-02-11T05:02:00.000Z","key":1486789320000,"doc_count":10},{"key_as_string":"2017-02-11T05:03:00.000Z","key":1486789380000,"doc_count":11},{"key_as_string":"2017-02-11T05:04:00.000Z","key":1486789440000,"doc_count":11},{"key_as_string":"2017-02-11T05:05:00.000Z","key":1486789500000,"doc_count":6},{"key_as_string":"2017-02-11T05:06:00.000Z","key":1486789560000,"doc_count":19},{"key_as_string":"2017-02-11T05:07:00.000Z","key":1486789620000,"doc_count":11},{"key_as_string":"2017-02-11T05:08:00.000Z","key":1486789680000,"doc_count":15},{"key_as_string":"2017-02-11T05:09:00.000Z","key":1486789740000,"doc_count":10},{"key_as_string":"2017-02-11T05:10:00.000Z","key":1486789800000,"doc_count":13},{"key_as_string":"2017-02-11T05:11:00.000Z","key":1486789860000,"doc_count":12},{"key_as_string":"2017-02-11T05:12:00.000Z","key":1486789920000,"doc_count":14},{"key_as_string":"2017-02-11T05:13:00.000Z","key":1486789980000,"doc_count":12},{"key_as_string":"2017-02-11T05:14:00.000Z","key":1486790040000,"doc_count":13},{"key_as_string":"2017-02-11T05:15:00.000Z","key":1486790100000,"doc_count":7},{"key_as_string":"2017-02-11T05:16:00.000Z","key":1486790160000,"doc_count":10},{"key_as_string":"2017-02-11T05:17:00.000Z","key":1486790220000,"doc_count":12},{"key_as_string":"2017-02-11T05:18:00.000Z","key":1486790280000,"doc_count":15},{"key_as_string":"2017-02-11T05:19:00.000Z","key":1486790340000,"doc_count":10},{"key_as_string":"2017-02-11T05:20:00.000Z","key":1486790400000,"doc_count":9},{"key_as_string":"2017-02-11T05:21:00.000Z","key":1486790460000,"doc_count":10},{"key_as_string":"2017-02-11T05:22:00.000Z","key":1486790520000,"doc_count":15},{"key_as_string":"2017-02-11T05:23:00.000Z","key":1486790580000,"doc_count":9},{"key_as_string":"2017-02-11T05:24:00.000Z","key":1486790640000,"doc_count":13},{"key_as_string":"2017-02-11T05:25:00.000Z","key":1486790700000,"doc_count":12},{"key_as_string":"2017-02-11T05:26:00.000Z","key":1486790760000,"doc_count":9},{"key_as_string":"2017-02-11T05:27:00.000Z","key":1486790820000,"doc_count":15},{"key_as_string":"2017-02-11T05:28:00.000Z","key":1486790880000,"doc_count":11},{"key_as_string":"2017-02-11T05:29:00.000Z","key":1486790940000,"doc_count":14},{"key_as_string":"2017-02-11T05:30:00.000Z","key":1486791000000,"doc_count":14},{"key_as_string":"2017-02-11T05:31:00.000Z","key":1486791060000,"doc_count":15},{"key_as_string":"2017-02-11T05:32:00.000Z","key":1486791120000,"doc_count":7},{"key_as_string":"2017-02-11T05:33:00.000Z","key":1486791180000,"doc_count":7},{"key_as_string":"2017-02-11T05:34:00.000Z","key":1486791240000,"doc_count":9},{"key_as_string":"2017-02-11T05:35:00.000Z","key":1486791300000,"doc_count":13},{"key_as_string":"2017-02-11T05:36:00.000Z","key":1486791360000,"doc_count":11},{"key_as_string":"2017-02-11T05:37:00.000Z","key":1486791420000,"doc_count":11},{"key_as_string":"2017-02-11T05:38:00.000Z","key":1486791480000,"doc_count":10},{"key_as_string":"2017-02-11T05:39:00.000Z","key":1486791540000,"doc_count":7},{"key_as_string":"2017-02-11T05:40:00.000Z","key":1486791600000,"doc_count":12},{"key_as_string":"2017-02-11T05:41:00.000Z","key":1486791660000,"doc_count":11},{"key_as_string":"2017-02-11T05:42:00.000Z","key":1486791720000,"doc_count":16},{"key_as_string":"2017-02-11T05:43:00.000Z","key":1486791780000,"doc_count":12},{"key_as_string":"2017-02-11T05:44:00.000Z","key":1486791840000,"doc_count":8},{"key_as_string":"2017-02-11T05:45:00.000Z","key":1486791900000,"doc_count":13},{"key_as_string":"2017-02-11T05:46:00.000Z","key":1486791960000,"doc_count":17},{"key_as_string":"2017-02-11T05:47:00.000Z","key":1486792020000,"doc_count":8},{"key_as_string":"2017-02-11T05:48:00.000Z","key":1486792080000,"doc_count":9},{"key_as_string":"2017-02-11T05:49:00.000Z","key":1486792140000,"doc_count":9},{"key_as_string":"2017-02-11T05:50:00.000Z","key":1486792200000,"doc_count":13},{"key_as_string":"2017-02-11T05:51:00.000Z","key":1486792260000,"doc_count":7},{"key_as_string":"2017-02-11T05:52:00.000Z","key":1486792320000,"doc_count":13},{"key_as_string":"2017-02-11T05:53:00.000Z","key":1486792380000,"doc_count":8},{"key_as_string":"2017-02-11T05:54:00.000Z","key":1486792440000,"doc_count":12},{"key_as_string":"2017-02-11T05:55:00.000Z","key":1486792500000,"doc_count":9},{"key_as_string":"2017-02-11T05:56:00.000Z","key":1486792560000,"doc_count":15},{"key_as_string":"2017-02-11T05:57:00.000Z","key":1486792620000,"doc_count":11},{"key_as_string":"2017-02-11T05:58:00.000Z","key":1486792680000,"doc_count":14},{"key_as_string":"2017-02-11T05:59:00.000Z","key":1486792740000,"doc_count":10},{"key_as_string":"2017-02-11T06:00:00.000Z","key":1486792800000,"doc_count":15},{"key_as_string":"2017-02-11T06:01:00.000Z","key":1486792860000,"doc_count":10},{"key_as_string":"2017-02-11T06:02:00.000Z","key":1486792920000,"doc_count":9},{"key_as_string":"2017-02-11T06:03:00.000Z","key":1486792980000,"doc_count":16},{"key_as_string":"2017-02-11T06:04:00.000Z","key":1486793040000,"doc_count":12},{"key_as_string":"2017-02-11T06:05:00.000Z","key":1486793100000,"doc_count":13},{"key_as_string":"2017-02-11T06:06:00.000Z","key":1486793160000,"doc_count":14},{"key_as_string":"2017-02-11T06:07:00.000Z","key":1486793220000,"doc_count":11},{"key_as_string":"2017-02-11T06:08:00.000Z","key":1486793280000,"doc_count":16},{"key_as_string":"2017-02-11T06:09:00.000Z","key":1486793340000,"doc_count":9},{"key_as_string":"2017-02-11T06:10:00.000Z","key":1486793400000,"doc_count":15},{"key_as_string":"2017-02-11T06:11:00.000Z","key":1486793460000,"doc_count":9},{"key_as_string":"2017-02-11T06:12:00.000Z","key":1486793520000,"doc_count":9},{"key_as_string":"2017-02-11T06:13:00.000Z","key":1486793580000,"doc_count":13},{"key_as_string":"2017-02-11T06:14:00.000Z","key":1486793640000,"doc_count":12},{"key_as_string":"2017-02-11T06:15:00.000Z","key":1486793700000,"doc_count":11},{"key_as_string":"2017-02-11T06:16:00.000Z","key":1486793760000,"doc_count":14},{"key_as_string":"2017-02-11T06:17:00.000Z","key":1486793820000,"doc_count":16},{"key_as_string":"2017-02-11T06:18:00.000Z","key":1486793880000,"doc_count":7},{"key_as_string":"2017-02-11T06:19:00.000Z","key":1486793940000,"doc_count":18},{"key_as_string":"2017-02-11T06:20:00.000Z","key":1486794000000,"doc_count":15},{"key_as_string":"2017-02-11T06:21:00.000Z","key":1486794060000,"doc_count":10},{"key_as_string":"2017-02-11T06:22:00.000Z","key":1486794120000,"doc_count":10},{"key_as_string":"2017-02-11T06:23:00.000Z","key":1486794180000,"doc_count":11},{"key_as_string":"2017-02-11T06:24:00.000Z","key":1486794240000,"doc_count":7},{"key_as_string":"2017-02-11T06:25:00.000Z","key":1486794300000,"doc_count":12},{"key_as_string":"2017-02-11T06:26:00.000Z","key":1486794360000,"doc_count":10},{"key_as_string":"2017-02-11T06:27:00.000Z","key":1486794420000,"doc_count":11},{"key_as_string":"2017-02-11T06:28:00.000Z","key":1486794480000,"doc_count":13},{"key_as_string":"2017-02-11T06:29:00.000Z","key":1486794540000,"doc_count":11},{"key_as_string":"2017-02-11T06:30:00.000Z","key":1486794600000,"doc_count":13},{"key_as_string":"2017-02-11T06:31:00.000Z","key":1486794660000,"doc_count":10},{"key_as_string":"2017-02-11T06:32:00.000Z","key":1486794720000,"doc_count":13},{"key_as_string":"2017-02-11T06:33:00.000Z","key":1486794780000,"doc_count":14},{"key_as_string":"2017-02-11T06:34:00.000Z","key":1486794840000,"doc_count":15},{"key_as_string":"2017-02-11T06:35:00.000Z","key":1486794900000,"doc_count":11},{"key_as_string":"2017-02-11T06:36:00.000Z","key":1486794960000,"doc_count":14},{"key_as_string":"2017-02-11T06:37:00.000Z","key":1486795020000,"doc_count":10},{"key_as_string":"2017-02-11T06:38:00.000Z","key":1486795080000,"doc_count":13},{"key_as_string":"2017-02-11T06:39:00.000Z","key":1486795140000,"doc_count":7},{"key_as_string":"2017-02-11T06:40:00.000Z","key":1486795200000,"doc_count":12},{"key_as_string":"2017-02-11T06:41:00.000Z","key":1486795260000,"doc_count":12},{"key_as_string":"2017-02-11T06:42:00.000Z","key":1486795320000,"doc_count":10},{"key_as_string":"2017-02-11T06:43:00.000Z","key":1486795380000,"doc_count":14},{"key_as_string":"2017-02-11T06:44:00.000Z","key":1486795440000,"doc_count":15},{"key_as_string":"2017-02-11T06:45:00.000Z","key":1486795500000,"doc_count":10},{"key_as_string":"2017-02-11T06:46:00.000Z","key":1486795560000,"doc_count":12},{"key_as_string":"2017-02-11T06:47:00.000Z","key":1486795620000,"doc_count":14},{"key_as_string":"2017-02-11T06:48:00.000Z","key":1486795680000,"doc_count":15},{"key_as_string":"2017-02-11T06:49:00.000Z","key":1486795740000,"doc_count":11},{"key_as_string":"2017-02-11T06:50:00.000Z","key":1486795800000,"doc_count":14},{"key_as_string":"2017-02-11T06:51:00.000Z","key":1486795860000,"doc_count":9},{"key_as_string":"2017-02-11T06:52:00.000Z","key":1486795920000,"doc_count":15},{"key_as_string":"2017-02-11T06:53:00.000Z","key":1486795980000,"doc_count":6},{"key_as_string":"2017-02-11T06:54:00.000Z","key":1486796040000,"doc_count":15},{"key_as_string":"2017-02-11T06:55:00.000Z","key":1486796100000,"doc_count":12},{"key_as_string":"2017-02-11T06:56:00.000Z","key":1486796160000,"doc_count":11},{"key_as_string":"2017-02-11T06:57:00.000Z","key":1486796220000,"doc_count":15},{"key_as_string":"2017-02-11T06:58:00.000Z","key":1486796280000,"doc_count":12},{"key_as_string":"2017-02-11T06:59:00.000Z","key":1486796340000,"doc_count":9},{"key_as_string":"2017-02-11T07:00:00.000Z","key":1486796400000,"doc_count":17},{"key_as_string":"2017-02-11T07:01:00.000Z","key":1486796460000,"doc_count":11},{"key_as_string":"2017-02-11T07:02:00.000Z","key":1486796520000,"doc_count":12},{"key_as_string":"2017-02-11T07:03:00.000Z","key":1486796580000,"doc_count":15},{"key_as_string":"2017-02-11T07:04:00.000Z","key":1486796640000,"doc_count":16},{"key_as_string":"2017-02-11T07:05:00.000Z","key":1486796700000,"doc_count":13},{"key_as_string":"2017-02-11T07:06:00.000Z","key":1486796760000,"doc_count":12},{"key_as_string":"2017-02-11T07:07:00.000Z","key":1486796820000,"doc_count":10},{"key_as_string":"2017-02-11T07:08:00.000Z","key":1486796880000,"doc_count":18},{"key_as_string":"2017-02-11T07:09:00.000Z","key":1486796940000,"doc_count":10},{"key_as_string":"2017-02-11T07:10:00.000Z","key":1486797000000,"doc_count":12},{"key_as_string":"2017-02-11T07:11:00.000Z","key":1486797060000,"doc_count":16},{"key_as_string":"2017-02-11T07:12:00.000Z","key":1486797120000,"doc_count":13},{"key_as_string":"2017-02-11T07:13:00.000Z","key":1486797180000,"doc_count":9},{"key_as_string":"2017-02-11T07:14:00.000Z","key":1486797240000,"doc_count":12},{"key_as_string":"2017-02-11T07:15:00.000Z","key":1486797300000,"doc_count":9},{"key_as_string":"2017-02-11T07:16:00.000Z","key":1486797360000,"doc_count":11},{"key_as_string":"2017-02-11T07:17:00.000Z","key":1486797420000,"doc_count":12},{"key_as_string":"2017-02-11T07:18:00.000Z","key":1486797480000,"doc_count":13},{"key_as_string":"2017-02-11T07:19:00.000Z","key":1486797540000,"doc_count":14},{"key_as_string":"2017-02-11T07:20:00.000Z","key":1486797600000,"doc_count":11},{"key_as_string":"2017-02-11T07:21:00.000Z","key":1486797660000,"doc_count":16},{"key_as_string":"2017-02-11T07:22:00.000Z","key":1486797720000,"doc_count":9},{"key_as_string":"2017-02-11T07:23:00.000Z","key":1486797780000,"doc_count":14},{"key_as_string":"2017-02-11T07:24:00.000Z","key":1486797840000,"doc_count":11},{"key_as_string":"2017-02-11T07:25:00.000Z","key":1486797900000,"doc_count":14},{"key_as_string":"2017-02-11T07:26:00.000Z","key":1486797960000,"doc_count":12},{"key_as_string":"2017-02-11T07:27:00.000Z","key":1486798020000,"doc_count":15},{"key_as_string":"2017-02-11T07:28:00.000Z","key":1486798080000,"doc_count":8},{"key_as_string":"2017-02-11T07:29:00.000Z","key":1486798140000,"doc_count":12},{"key_as_string":"2017-02-11T07:30:00.000Z","key":1486798200000,"doc_count":13},{"key_as_string":"2017-02-11T07:31:00.000Z","key":1486798260000,"doc_count":11},{"key_as_string":"2017-02-11T07:32:00.000Z","key":1486798320000,"doc_count":12},{"key_as_string":"2017-02-11T07:33:00.000Z","key":1486798380000,"doc_count":12},{"key_as_string":"2017-02-11T07:34:00.000Z","key":1486798440000,"doc_count":12},{"key_as_string":"2017-02-11T07:35:00.000Z","key":1486798500000,"doc_count":12},{"key_as_string":"2017-02-11T07:36:00.000Z","key":1486798560000,"doc_count":13},{"key_as_string":"2017-02-11T07:37:00.000Z","key":1486798620000,"doc_count":9},{"key_as_string":"2017-02-11T07:38:00.000Z","key":1486798680000,"doc_count":13},{"key_as_string":"2017-02-11T07:39:00.000Z","key":1486798740000,"doc_count":9},{"key_as_string":"2017-02-11T07:40:00.000Z","key":1486798800000,"doc_count":12},{"key_as_string":"2017-02-11T07:41:00.000Z","key":1486798860000,"doc_count":12},{"key_as_string":"2017-02-11T07:42:00.000Z","key":1486798920000,"doc_count":17},{"key_as_string":"2017-02-11T07:43:00.000Z","key":1486798980000,"doc_count":12},{"key_as_string":"2017-02-11T07:44:00.000Z","key":1486799040000,"doc_count":11},{"key_as_string":"2017-02-11T07:45:00.000Z","key":1486799100000,"doc_count":14},{"key_as_string":"2017-02-11T07:46:00.000Z","key":1486799160000,"doc_count":14},{"key_as_string":"2017-02-11T07:47:00.000Z","key":1486799220000,"doc_count":9},{"key_as_string":"2017-02-11T07:48:00.000Z","key":1486799280000,"doc_count":11},{"key_as_string":"2017-02-11T07:49:00.000Z","key":1486799340000,"doc_count":11},{"key_as_string":"2017-02-11T07:50:00.000Z","key":1486799400000,"doc_count":9},{"key_as_string":"2017-02-11T07:51:00.000Z","key":1486799460000,"doc_count":12},{"key_as_string":"2017-02-11T07:52:00.000Z","key":1486799520000,"doc_count":12},{"key_as_string":"2017-02-11T07:53:00.000Z","key":1486799580000,"doc_count":7},{"key_as_string":"2017-02-11T07:54:00.000Z","key":1486799640000,"doc_count":17},{"key_as_string":"2017-02-11T07:55:00.000Z","key":1486799700000,"doc_count":13},{"key_as_string":"2017-02-11T07:56:00.000Z","key":1486799760000,"doc_count":12},{"key_as_string":"2017-02-11T07:57:00.000Z","key":1486799820000,"doc_count":11},{"key_as_string":"2017-02-11T07:58:00.000Z","key":1486799880000,"doc_count":13},{"key_as_string":"2017-02-11T07:59:00.000Z","key":1486799940000,"doc_count":11},{"key_as_string":"2017-02-11T08:00:00.000Z","key":1486800000000,"doc_count":16},{"key_as_string":"2017-02-11T08:01:00.000Z","key":1486800060000,"doc_count":15},{"key_as_string":"2017-02-11T08:02:00.000Z","key":1486800120000,"doc_count":14},{"key_as_string":"2017-02-11T08:03:00.000Z","key":1486800180000,"doc_count":16},{"key_as_string":"2017-02-11T08:04:00.000Z","key":1486800240000,"doc_count":12},{"key_as_string":"2017-02-11T08:05:00.000Z","key":1486800300000,"doc_count":14},{"key_as_string":"2017-02-11T08:06:00.000Z","key":1486800360000,"doc_count":13},{"key_as_string":"2017-02-11T08:07:00.000Z","key":1486800420000,"doc_count":16},{"key_as_string":"2017-02-11T08:08:00.000Z","key":1486800480000,"doc_count":17},{"key_as_string":"2017-02-11T08:09:00.000Z","key":1486800540000,"doc_count":20},{"key_as_string":"2017-02-11T08:10:00.000Z","key":1486800600000,"doc_count":13},{"key_as_string":"2017-02-11T08:11:00.000Z","key":1486800660000,"doc_count":15},{"key_as_string":"2017-02-11T08:12:00.000Z","key":1486800720000,"doc_count":10},{"key_as_string":"2017-02-11T08:13:00.000Z","key":1486800780000,"doc_count":16},{"key_as_string":"2017-02-11T08:14:00.000Z","key":1486800840000,"doc_count":5},{"key_as_string":"2017-02-11T08:15:00.000Z","key":1486800900000,"doc_count":14},{"key_as_string":"2017-02-11T08:16:00.000Z","key":1486800960000,"doc_count":18},{"key_as_string":"2017-02-11T08:17:00.000Z","key":1486801020000,"doc_count":11},{"key_as_string":"2017-02-11T08:18:00.000Z","key":1486801080000,"doc_count":16},{"key_as_string":"2017-02-11T08:19:00.000Z","key":1486801140000,"doc_count":10},{"key_as_string":"2017-02-11T08:20:00.000Z","key":1486801200000,"doc_count":15},{"key_as_string":"2017-02-11T08:21:00.000Z","key":1486801260000,"doc_count":10},{"key_as_string":"2017-02-11T08:22:00.000Z","key":1486801320000,"doc_count":14},{"key_as_string":"2017-02-11T08:23:00.000Z","key":1486801380000,"doc_count":10},{"key_as_string":"2017-02-11T08:24:00.000Z","key":1486801440000,"doc_count":19},{"key_as_string":"2017-02-11T08:25:00.000Z","key":1486801500000,"doc_count":14},{"key_as_string":"2017-02-11T08:26:00.000Z","key":1486801560000,"doc_count":13},{"key_as_string":"2017-02-11T08:27:00.000Z","key":1486801620000,"doc_count":10},{"key_as_string":"2017-02-11T08:28:00.000Z","key":1486801680000,"doc_count":15},{"key_as_string":"2017-02-11T08:29:00.000Z","key":1486801740000,"doc_count":10},{"key_as_string":"2017-02-11T08:30:00.000Z","key":1486801800000,"doc_count":11},{"key_as_string":"2017-02-11T08:31:00.000Z","key":1486801860000,"doc_count":9},{"key_as_string":"2017-02-11T08:32:00.000Z","key":1486801920000,"doc_count":14},{"key_as_string":"2017-02-11T08:33:00.000Z","key":1486801980000,"doc_count":10},{"key_as_string":"2017-02-11T08:34:00.000Z","key":1486802040000,"doc_count":12},{"key_as_string":"2017-02-11T08:35:00.000Z","key":1486802100000,"doc_count":16},{"key_as_string":"2017-02-11T08:36:00.000Z","key":1486802160000,"doc_count":19},{"key_as_string":"2017-02-11T08:37:00.000Z","key":1486802220000,"doc_count":13},{"key_as_string":"2017-02-11T08:38:00.000Z","key":1486802280000,"doc_count":11},{"key_as_string":"2017-02-11T08:39:00.000Z","key":1486802340000,"doc_count":16},{"key_as_string":"2017-02-11T08:40:00.000Z","key":1486802400000,"doc_count":13},{"key_as_string":"2017-02-11T08:41:00.000Z","key":1486802460000,"doc_count":10},{"key_as_string":"2017-02-11T08:42:00.000Z","key":1486802520000,"doc_count":12},{"key_as_string":"2017-02-11T08:43:00.000Z","key":1486802580000,"doc_count":11},{"key_as_string":"2017-02-11T08:44:00.000Z","key":1486802640000,"doc_count":9},{"key_as_string":"2017-02-11T08:45:00.000Z","key":1486802700000,"doc_count":12},{"key_as_string":"2017-02-11T08:46:00.000Z","key":1486802760000,"doc_count":11},{"key_as_string":"2017-02-11T08:47:00.000Z","key":1486802820000,"doc_count":15},{"key_as_string":"2017-02-11T08:48:00.000Z","key":1486802880000,"doc_count":9},{"key_as_string":"2017-02-11T08:49:00.000Z","key":1486802940000,"doc_count":17},{"key_as_string":"2017-02-11T08:50:00.000Z","key":1486803000000,"doc_count":14},{"key_as_string":"2017-02-11T08:51:00.000Z","key":1486803060000,"doc_count":14},{"key_as_string":"2017-02-11T08:52:00.000Z","key":1486803120000,"doc_count":11},{"key_as_string":"2017-02-11T08:53:00.000Z","key":1486803180000,"doc_count":13},{"key_as_string":"2017-02-11T08:54:00.000Z","key":1486803240000,"doc_count":17},{"key_as_string":"2017-02-11T08:55:00.000Z","key":1486803300000,"doc_count":13},{"key_as_string":"2017-02-11T08:56:00.000Z","key":1486803360000,"doc_count":15},{"key_as_string":"2017-02-11T08:57:00.000Z","key":1486803420000,"doc_count":15},{"key_as_string":"2017-02-11T08:58:00.000Z","key":1486803480000,"doc_count":14},{"key_as_string":"2017-02-11T08:59:00.000Z","key":1486803540000,"doc_count":6},{"key_as_string":"2017-02-11T09:00:00.000Z","key":1486803600000,"doc_count":15},{"key_as_string":"2017-02-11T09:01:00.000Z","key":1486803660000,"doc_count":9},{"key_as_string":"2017-02-11T09:02:00.000Z","key":1486803720000,"doc_count":15},{"key_as_string":"2017-02-11T09:03:00.000Z","key":1486803780000,"doc_count":10},{"key_as_string":"2017-02-11T09:04:00.000Z","key":1486803840000,"doc_count":11},{"key_as_string":"2017-02-11T09:05:00.000Z","key":1486803900000,"doc_count":17},{"key_as_string":"2017-02-11T09:06:00.000Z","key":1486803960000,"doc_count":10},{"key_as_string":"2017-02-11T09:07:00.000Z","key":1486804020000,"doc_count":13},{"key_as_string":"2017-02-11T09:08:00.000Z","key":1486804080000,"doc_count":15},{"key_as_string":"2017-02-11T09:09:00.000Z","key":1486804140000,"doc_count":10},{"key_as_string":"2017-02-11T09:10:00.000Z","key":1486804200000,"doc_count":16},{"key_as_string":"2017-02-11T09:11:00.000Z","key":1486804260000,"doc_count":18},{"key_as_string":"2017-02-11T09:12:00.000Z","key":1486804320000,"doc_count":10},{"key_as_string":"2017-02-11T09:13:00.000Z","key":1486804380000,"doc_count":14},{"key_as_string":"2017-02-11T09:14:00.000Z","key":1486804440000,"doc_count":9},{"key_as_string":"2017-02-11T09:15:00.000Z","key":1486804500000,"doc_count":17},{"key_as_string":"2017-02-11T09:16:00.000Z","key":1486804560000,"doc_count":18},{"key_as_string":"2017-02-11T09:17:00.000Z","key":1486804620000,"doc_count":12},{"key_as_string":"2017-02-11T09:18:00.000Z","key":1486804680000,"doc_count":15},{"key_as_string":"2017-02-11T09:19:00.000Z","key":1486804740000,"doc_count":10},{"key_as_string":"2017-02-11T09:20:00.000Z","key":1486804800000,"doc_count":14},{"key_as_string":"2017-02-11T09:21:00.000Z","key":1486804860000,"doc_count":15},{"key_as_string":"2017-02-11T09:22:00.000Z","key":1486804920000,"doc_count":12},{"key_as_string":"2017-02-11T09:23:00.000Z","key":1486804980000,"doc_count":17},{"key_as_string":"2017-02-11T09:24:00.000Z","key":1486805040000,"doc_count":13},{"key_as_string":"2017-02-11T09:25:00.000Z","key":1486805100000,"doc_count":17},{"key_as_string":"2017-02-11T09:26:00.000Z","key":1486805160000,"doc_count":11},{"key_as_string":"2017-02-11T09:27:00.000Z","key":1486805220000,"doc_count":11},{"key_as_string":"2017-02-11T09:28:00.000Z","key":1486805280000,"doc_count":13},{"key_as_string":"2017-02-11T09:29:00.000Z","key":1486805340000,"doc_count":9},{"key_as_string":"2017-02-11T09:30:00.000Z","key":1486805400000,"doc_count":20},{"key_as_string":"2017-02-11T09:31:00.000Z","key":1486805460000,"doc_count":13},{"key_as_string":"2017-02-11T09:32:00.000Z","key":1486805520000,"doc_count":17},{"key_as_string":"2017-02-11T09:33:00.000Z","key":1486805580000,"doc_count":16},{"key_as_string":"2017-02-11T09:34:00.000Z","key":1486805640000,"doc_count":11},{"key_as_string":"2017-02-11T09:35:00.000Z","key":1486805700000,"doc_count":18},{"key_as_string":"2017-02-11T09:36:00.000Z","key":1486805760000,"doc_count":14},{"key_as_string":"2017-02-11T09:37:00.000Z","key":1486805820000,"doc_count":17},{"key_as_string":"2017-02-11T09:38:00.000Z","key":1486805880000,"doc_count":12},{"key_as_string":"2017-02-11T09:39:00.000Z","key":1486805940000,"doc_count":12},{"key_as_string":"2017-02-11T09:40:00.000Z","key":1486806000000,"doc_count":14},{"key_as_string":"2017-02-11T09:41:00.000Z","key":1486806060000,"doc_count":11},{"key_as_string":"2017-02-11T09:42:00.000Z","key":1486806120000,"doc_count":12},{"key_as_string":"2017-02-11T09:43:00.000Z","key":1486806180000,"doc_count":14},{"key_as_string":"2017-02-11T09:44:00.000Z","key":1486806240000,"doc_count":16},{"key_as_string":"2017-02-11T09:45:00.000Z","key":1486806300000,"doc_count":17},{"key_as_string":"2017-02-11T09:46:00.000Z","key":1486806360000,"doc_count":12},{"key_as_string":"2017-02-11T09:47:00.000Z","key":1486806420000,"doc_count":15},{"key_as_string":"2017-02-11T09:48:00.000Z","key":1486806480000,"doc_count":13},{"key_as_string":"2017-02-11T09:49:00.000Z","key":1486806540000,"doc_count":14},{"key_as_string":"2017-02-11T09:50:00.000Z","key":1486806600000,"doc_count":17},{"key_as_string":"2017-02-11T09:51:00.000Z","key":1486806660000,"doc_count":15},{"key_as_string":"2017-02-11T09:52:00.000Z","key":1486806720000,"doc_count":9},{"key_as_string":"2017-02-11T09:53:00.000Z","key":1486806780000,"doc_count":20},{"key_as_string":"2017-02-11T09:54:00.000Z","key":1486806840000,"doc_count":15},{"key_as_string":"2017-02-11T09:55:00.000Z","key":1486806900000,"doc_count":19},{"key_as_string":"2017-02-11T09:56:00.000Z","key":1486806960000,"doc_count":10},{"key_as_string":"2017-02-11T09:57:00.000Z","key":1486807020000,"doc_count":14},{"key_as_string":"2017-02-11T09:58:00.000Z","key":1486807080000,"doc_count":15},{"key_as_string":"2017-02-11T09:59:00.000Z","key":1486807140000,"doc_count":20},{"key_as_string":"2017-02-11T10:00:00.000Z","key":1486807200000,"doc_count":14},{"key_as_string":"2017-02-11T10:01:00.000Z","key":1486807260000,"doc_count":14},{"key_as_string":"2017-02-11T10:02:00.000Z","key":1486807320000,"doc_count":13},{"key_as_string":"2017-02-11T10:03:00.000Z","key":1486807380000,"doc_count":13},{"key_as_string":"2017-02-11T10:04:00.000Z","key":1486807440000,"doc_count":17},{"key_as_string":"2017-02-11T10:05:00.000Z","key":1486807500000,"doc_count":10},{"key_as_string":"2017-02-11T10:06:00.000Z","key":1486807560000,"doc_count":20},{"key_as_string":"2017-02-11T10:07:00.000Z","key":1486807620000,"doc_count":14},{"key_as_string":"2017-02-11T10:08:00.000Z","key":1486807680000,"doc_count":12},{"key_as_string":"2017-02-11T10:09:00.000Z","key":1486807740000,"doc_count":22},{"key_as_string":"2017-02-11T10:10:00.000Z","key":1486807800000,"doc_count":18},{"key_as_string":"2017-02-11T10:11:00.000Z","key":1486807860000,"doc_count":9},{"key_as_string":"2017-02-11T10:12:00.000Z","key":1486807920000,"doc_count":16},{"key_as_string":"2017-02-11T10:13:00.000Z","key":1486807980000,"doc_count":13},{"key_as_string":"2017-02-11T10:14:00.000Z","key":1486808040000,"doc_count":14},{"key_as_string":"2017-02-11T10:15:00.000Z","key":1486808100000,"doc_count":13},{"key_as_string":"2017-02-11T10:16:00.000Z","key":1486808160000,"doc_count":15},{"key_as_string":"2017-02-11T10:17:00.000Z","key":1486808220000,"doc_count":15},{"key_as_string":"2017-02-11T10:18:00.000Z","key":1486808280000,"doc_count":13},{"key_as_string":"2017-02-11T10:19:00.000Z","key":1486808340000,"doc_count":12},{"key_as_string":"2017-02-11T10:20:00.000Z","key":1486808400000,"doc_count":8},{"key_as_string":"2017-02-11T10:21:00.000Z","key":1486808460000,"doc_count":18},{"key_as_string":"2017-02-11T10:22:00.000Z","key":1486808520000,"doc_count":14},{"key_as_string":"2017-02-11T10:23:00.000Z","key":1486808580000,"doc_count":17},{"key_as_string":"2017-02-11T10:24:00.000Z","key":1486808640000,"doc_count":22},{"key_as_string":"2017-02-11T10:25:00.000Z","key":1486808700000,"doc_count":12},{"key_as_string":"2017-02-11T10:26:00.000Z","key":1486808760000,"doc_count":13},{"key_as_string":"2017-02-11T10:27:00.000Z","key":1486808820000,"doc_count":17},{"key_as_string":"2017-02-11T10:28:00.000Z","key":1486808880000,"doc_count":14},{"key_as_string":"2017-02-11T10:29:00.000Z","key":1486808940000,"doc_count":13},{"key_as_string":"2017-02-11T10:30:00.000Z","key":1486809000000,"doc_count":11},{"key_as_string":"2017-02-11T10:31:00.000Z","key":1486809060000,"doc_count":15},{"key_as_string":"2017-02-11T10:32:00.000Z","key":1486809120000,"doc_count":17},{"key_as_string":"2017-02-11T10:33:00.000Z","key":1486809180000,"doc_count":14},{"key_as_string":"2017-02-11T10:34:00.000Z","key":1486809240000,"doc_count":12},{"key_as_string":"2017-02-11T10:35:00.000Z","key":1486809300000,"doc_count":10},{"key_as_string":"2017-02-11T10:36:00.000Z","key":1486809360000,"doc_count":12},{"key_as_string":"2017-02-11T10:37:00.000Z","key":1486809420000,"doc_count":16},{"key_as_string":"2017-02-11T10:38:00.000Z","key":1486809480000,"doc_count":18},{"key_as_string":"2017-02-11T10:39:00.000Z","key":1486809540000,"doc_count":15},{"key_as_string":"2017-02-11T10:40:00.000Z","key":1486809600000,"doc_count":16},{"key_as_string":"2017-02-11T10:41:00.000Z","key":1486809660000,"doc_count":15},{"key_as_string":"2017-02-11T10:42:00.000Z","key":1486809720000,"doc_count":15},{"key_as_string":"2017-02-11T10:43:00.000Z","key":1486809780000,"doc_count":11},{"key_as_string":"2017-02-11T10:44:00.000Z","key":1486809840000,"doc_count":11},{"key_as_string":"2017-02-11T10:45:00.000Z","key":1486809900000,"doc_count":19},{"key_as_string":"2017-02-11T10:46:00.000Z","key":1486809960000,"doc_count":12},{"key_as_string":"2017-02-11T10:47:00.000Z","key":1486810020000,"doc_count":12},{"key_as_string":"2017-02-11T10:48:00.000Z","key":1486810080000,"doc_count":12},{"key_as_string":"2017-02-11T10:49:00.000Z","key":1486810140000,"doc_count":12},{"key_as_string":"2017-02-11T10:50:00.000Z","key":1486810200000,"doc_count":21},{"key_as_string":"2017-02-11T10:51:00.000Z","key":1486810260000,"doc_count":13},{"key_as_string":"2017-02-11T10:52:00.000Z","key":1486810320000,"doc_count":13},{"key_as_string":"2017-02-11T10:53:00.000Z","key":1486810380000,"doc_count":14},{"key_as_string":"2017-02-11T10:54:00.000Z","key":1486810440000,"doc_count":14},{"key_as_string":"2017-02-11T10:55:00.000Z","key":1486810500000,"doc_count":10},{"key_as_string":"2017-02-11T10:56:00.000Z","key":1486810560000,"doc_count":12},{"key_as_string":"2017-02-11T10:57:00.000Z","key":1486810620000,"doc_count":15},{"key_as_string":"2017-02-11T10:58:00.000Z","key":1486810680000,"doc_count":13},{"key_as_string":"2017-02-11T10:59:00.000Z","key":1486810740000,"doc_count":11},{"key_as_string":"2017-02-11T11:00:00.000Z","key":1486810800000,"doc_count":18},{"key_as_string":"2017-02-11T11:01:00.000Z","key":1486810860000,"doc_count":14},{"key_as_string":"2017-02-11T11:02:00.000Z","key":1486810920000,"doc_count":18},{"key_as_string":"2017-02-11T11:03:00.000Z","key":1486810980000,"doc_count":18},{"key_as_string":"2017-02-11T11:04:00.000Z","key":1486811040000,"doc_count":14},{"key_as_string":"2017-02-11T11:05:00.000Z","key":1486811100000,"doc_count":21},{"key_as_string":"2017-02-11T11:06:00.000Z","key":1486811160000,"doc_count":8},{"key_as_string":"2017-02-11T11:07:00.000Z","key":1486811220000,"doc_count":14},{"key_as_string":"2017-02-11T11:08:00.000Z","key":1486811280000,"doc_count":9},{"key_as_string":"2017-02-11T11:09:00.000Z","key":1486811340000,"doc_count":13},{"key_as_string":"2017-02-11T11:10:00.000Z","key":1486811400000,"doc_count":19},{"key_as_string":"2017-02-11T11:11:00.000Z","key":1486811460000,"doc_count":12},{"key_as_string":"2017-02-11T11:12:00.000Z","key":1486811520000,"doc_count":15},{"key_as_string":"2017-02-11T11:13:00.000Z","key":1486811580000,"doc_count":16},{"key_as_string":"2017-02-11T11:14:00.000Z","key":1486811640000,"doc_count":11},{"key_as_string":"2017-02-11T11:15:00.000Z","key":1486811700000,"doc_count":12},{"key_as_string":"2017-02-11T11:16:00.000Z","key":1486811760000,"doc_count":16},{"key_as_string":"2017-02-11T11:17:00.000Z","key":1486811820000,"doc_count":14},{"key_as_string":"2017-02-11T11:18:00.000Z","key":1486811880000,"doc_count":13},{"key_as_string":"2017-02-11T11:19:00.000Z","key":1486811940000,"doc_count":14},{"key_as_string":"2017-02-11T11:20:00.000Z","key":1486812000000,"doc_count":14},{"key_as_string":"2017-02-11T11:21:00.000Z","key":1486812060000,"doc_count":11},{"key_as_string":"2017-02-11T11:22:00.000Z","key":1486812120000,"doc_count":14},{"key_as_string":"2017-02-11T11:23:00.000Z","key":1486812180000,"doc_count":13},{"key_as_string":"2017-02-11T11:24:00.000Z","key":1486812240000,"doc_count":13},{"key_as_string":"2017-02-11T11:25:00.000Z","key":1486812300000,"doc_count":13},{"key_as_string":"2017-02-11T11:26:00.000Z","key":1486812360000,"doc_count":6},{"key_as_string":"2017-02-11T11:27:00.000Z","key":1486812420000,"doc_count":21},{"key_as_string":"2017-02-11T11:28:00.000Z","key":1486812480000,"doc_count":10},{"key_as_string":"2017-02-11T11:29:00.000Z","key":1486812540000,"doc_count":19},{"key_as_string":"2017-02-11T11:30:00.000Z","key":1486812600000,"doc_count":10},{"key_as_string":"2017-02-11T11:31:00.000Z","key":1486812660000,"doc_count":10},{"key_as_string":"2017-02-11T11:32:00.000Z","key":1486812720000,"doc_count":14},{"key_as_string":"2017-02-11T11:33:00.000Z","key":1486812780000,"doc_count":13},{"key_as_string":"2017-02-11T11:34:00.000Z","key":1486812840000,"doc_count":9},{"key_as_string":"2017-02-11T11:35:00.000Z","key":1486812900000,"doc_count":16},{"key_as_string":"2017-02-11T11:36:00.000Z","key":1486812960000,"doc_count":9},{"key_as_string":"2017-02-11T11:37:00.000Z","key":1486813020000,"doc_count":14},{"key_as_string":"2017-02-11T11:38:00.000Z","key":1486813080000,"doc_count":8},{"key_as_string":"2017-02-11T11:39:00.000Z","key":1486813140000,"doc_count":16},{"key_as_string":"2017-02-11T11:40:00.000Z","key":1486813200000,"doc_count":17},{"key_as_string":"2017-02-11T11:41:00.000Z","key":1486813260000,"doc_count":11},{"key_as_string":"2017-02-11T11:42:00.000Z","key":1486813320000,"doc_count":13},{"key_as_string":"2017-02-11T11:43:00.000Z","key":1486813380000,"doc_count":14},{"key_as_string":"2017-02-11T11:44:00.000Z","key":1486813440000,"doc_count":17},{"key_as_string":"2017-02-11T11:45:00.000Z","key":1486813500000,"doc_count":11},{"key_as_string":"2017-02-11T11:46:00.000Z","key":1486813560000,"doc_count":16},{"key_as_string":"2017-02-11T11:47:00.000Z","key":1486813620000,"doc_count":12},{"key_as_string":"2017-02-11T11:48:00.000Z","key":1486813680000,"doc_count":13},{"key_as_string":"2017-02-11T11:49:00.000Z","key":1486813740000,"doc_count":19},{"key_as_string":"2017-02-11T11:50:00.000Z","key":1486813800000,"doc_count":12},{"key_as_string":"2017-02-11T11:51:00.000Z","key":1486813860000,"doc_count":15},{"key_as_string":"2017-02-11T11:52:00.000Z","key":1486813920000,"doc_count":12},{"key_as_string":"2017-02-11T11:53:00.000Z","key":1486813980000,"doc_count":8},{"key_as_string":"2017-02-11T11:54:00.000Z","key":1486814040000,"doc_count":15},{"key_as_string":"2017-02-11T11:55:00.000Z","key":1486814100000,"doc_count":16},{"key_as_string":"2017-02-11T11:56:00.000Z","key":1486814160000,"doc_count":10},{"key_as_string":"2017-02-11T11:57:00.000Z","key":1486814220000,"doc_count":12},{"key_as_string":"2017-02-11T11:58:00.000Z","key":1486814280000,"doc_count":17},{"key_as_string":"2017-02-11T11:59:00.000Z","key":1486814340000,"doc_count":18},{"key_as_string":"2017-02-11T12:00:00.000Z","key":1486814400000,"doc_count":13},{"key_as_string":"2017-02-11T12:01:00.000Z","key":1486814460000,"doc_count":13},{"key_as_string":"2017-02-11T12:02:00.000Z","key":1486814520000,"doc_count":9},{"key_as_string":"2017-02-11T12:03:00.000Z","key":1486814580000,"doc_count":14},{"key_as_string":"2017-02-11T12:04:00.000Z","key":1486814640000,"doc_count":11},{"key_as_string":"2017-02-11T12:05:00.000Z","key":1486814700000,"doc_count":9},{"key_as_string":"2017-02-11T12:06:00.000Z","key":1486814760000,"doc_count":18},{"key_as_string":"2017-02-11T12:07:00.000Z","key":1486814820000,"doc_count":14},{"key_as_string":"2017-02-11T12:08:00.000Z","key":1486814880000,"doc_count":9},{"key_as_string":"2017-02-11T12:09:00.000Z","key":1486814940000,"doc_count":15},{"key_as_string":"2017-02-11T12:10:00.000Z","key":1486815000000,"doc_count":18},{"key_as_string":"2017-02-11T12:11:00.000Z","key":1486815060000,"doc_count":14},{"key_as_string":"2017-02-11T12:12:00.000Z","key":1486815120000,"doc_count":16},{"key_as_string":"2017-02-11T12:13:00.000Z","key":1486815180000,"doc_count":14},{"key_as_string":"2017-02-11T12:14:00.000Z","key":1486815240000,"doc_count":12},{"key_as_string":"2017-02-11T12:15:00.000Z","key":1486815300000,"doc_count":15},{"key_as_string":"2017-02-11T12:16:00.000Z","key":1486815360000,"doc_count":12},{"key_as_string":"2017-02-11T12:17:00.000Z","key":1486815420000,"doc_count":12},{"key_as_string":"2017-02-11T12:18:00.000Z","key":1486815480000,"doc_count":16},{"key_as_string":"2017-02-11T12:19:00.000Z","key":1486815540000,"doc_count":9},{"key_as_string":"2017-02-11T12:20:00.000Z","key":1486815600000,"doc_count":14},{"key_as_string":"2017-02-11T12:21:00.000Z","key":1486815660000,"doc_count":11},{"key_as_string":"2017-02-11T12:22:00.000Z","key":1486815720000,"doc_count":16},{"key_as_string":"2017-02-11T12:23:00.000Z","key":1486815780000,"doc_count":14},{"key_as_string":"2017-02-11T12:24:00.000Z","key":1486815840000,"doc_count":10},{"key_as_string":"2017-02-11T12:25:00.000Z","key":1486815900000,"doc_count":16},{"key_as_string":"2017-02-11T12:26:00.000Z","key":1486815960000,"doc_count":18},{"key_as_string":"2017-02-11T12:27:00.000Z","key":1486816020000,"doc_count":15},{"key_as_string":"2017-02-11T12:28:00.000Z","key":1486816080000,"doc_count":10},{"key_as_string":"2017-02-11T12:29:00.000Z","key":1486816140000,"doc_count":12},{"key_as_string":"2017-02-11T12:30:00.000Z","key":1486816200000,"doc_count":11},{"key_as_string":"2017-02-11T12:31:00.000Z","key":1486816260000,"doc_count":16},{"key_as_string":"2017-02-11T12:32:00.000Z","key":1486816320000,"doc_count":11},{"key_as_string":"2017-02-11T12:33:00.000Z","key":1486816380000,"doc_count":11},{"key_as_string":"2017-02-11T12:34:00.000Z","key":1486816440000,"doc_count":19},{"key_as_string":"2017-02-11T12:35:00.000Z","key":1486816500000,"doc_count":17},{"key_as_string":"2017-02-11T12:36:00.000Z","key":1486816560000,"doc_count":19},{"key_as_string":"2017-02-11T12:37:00.000Z","key":1486816620000,"doc_count":11},{"key_as_string":"2017-02-11T12:38:00.000Z","key":1486816680000,"doc_count":18},{"key_as_string":"2017-02-11T12:39:00.000Z","key":1486816740000,"doc_count":11},{"key_as_string":"2017-02-11T12:40:00.000Z","key":1486816800000,"doc_count":12},{"key_as_string":"2017-02-11T12:41:00.000Z","key":1486816860000,"doc_count":15},{"key_as_string":"2017-02-11T12:42:00.000Z","key":1486816920000,"doc_count":15},{"key_as_string":"2017-02-11T12:43:00.000Z","key":1486816980000,"doc_count":7},{"key_as_string":"2017-02-11T12:44:00.000Z","key":1486817040000,"doc_count":19},{"key_as_string":"2017-02-11T12:45:00.000Z","key":1486817100000,"doc_count":12},{"key_as_string":"2017-02-11T12:46:00.000Z","key":1486817160000,"doc_count":16},{"key_as_string":"2017-02-11T12:47:00.000Z","key":1486817220000,"doc_count":8},{"key_as_string":"2017-02-11T12:48:00.000Z","key":1486817280000,"doc_count":15},{"key_as_string":"2017-02-11T12:49:00.000Z","key":1486817340000,"doc_count":12},{"key_as_string":"2017-02-11T12:50:00.000Z","key":1486817400000,"doc_count":12},{"key_as_string":"2017-02-11T12:51:00.000Z","key":1486817460000,"doc_count":16},{"key_as_string":"2017-02-11T12:52:00.000Z","key":1486817520000,"doc_count":13},{"key_as_string":"2017-02-11T12:53:00.000Z","key":1486817580000,"doc_count":13},{"key_as_string":"2017-02-11T12:54:00.000Z","key":1486817640000,"doc_count":15},{"key_as_string":"2017-02-11T12:55:00.000Z","key":1486817700000,"doc_count":16},{"key_as_string":"2017-02-11T12:56:00.000Z","key":1486817760000,"doc_count":17},{"key_as_string":"2017-02-11T12:57:00.000Z","key":1486817820000,"doc_count":13},{"key_as_string":"2017-02-11T12:58:00.000Z","key":1486817880000,"doc_count":12},{"key_as_string":"2017-02-11T12:59:00.000Z","key":1486817940000,"doc_count":17},{"key_as_string":"2017-02-11T13:00:00.000Z","key":1486818000000,"doc_count":16},{"key_as_string":"2017-02-11T13:01:00.000Z","key":1486818060000,"doc_count":14},{"key_as_string":"2017-02-11T13:02:00.000Z","key":1486818120000,"doc_count":8},{"key_as_string":"2017-02-11T13:03:00.000Z","key":1486818180000,"doc_count":15},{"key_as_string":"2017-02-11T13:04:00.000Z","key":1486818240000,"doc_count":14},{"key_as_string":"2017-02-11T13:05:00.000Z","key":1486818300000,"doc_count":16},{"key_as_string":"2017-02-11T13:06:00.000Z","key":1486818360000,"doc_count":20},{"key_as_string":"2017-02-11T13:07:00.000Z","key":1486818420000,"doc_count":10},{"key_as_string":"2017-02-11T13:08:00.000Z","key":1486818480000,"doc_count":15},{"key_as_string":"2017-02-11T13:09:00.000Z","key":1486818540000,"doc_count":12},{"key_as_string":"2017-02-11T13:10:00.000Z","key":1486818600000,"doc_count":12},{"key_as_string":"2017-02-11T13:11:00.000Z","key":1486818660000,"doc_count":8},{"key_as_string":"2017-02-11T13:12:00.000Z","key":1486818720000,"doc_count":17},{"key_as_string":"2017-02-11T13:13:00.000Z","key":1486818780000,"doc_count":8},{"key_as_string":"2017-02-11T13:14:00.000Z","key":1486818840000,"doc_count":15},{"key_as_string":"2017-02-11T13:15:00.000Z","key":1486818900000,"doc_count":13},{"key_as_string":"2017-02-11T13:16:00.000Z","key":1486818960000,"doc_count":15},{"key_as_string":"2017-02-11T13:17:00.000Z","key":1486819020000,"doc_count":15},{"key_as_string":"2017-02-11T13:18:00.000Z","key":1486819080000,"doc_count":11},{"key_as_string":"2017-02-11T13:19:00.000Z","key":1486819140000,"doc_count":17},{"key_as_string":"2017-02-11T13:20:00.000Z","key":1486819200000,"doc_count":11},{"key_as_string":"2017-02-11T13:21:00.000Z","key":1486819260000,"doc_count":14},{"key_as_string":"2017-02-11T13:22:00.000Z","key":1486819320000,"doc_count":10},{"key_as_string":"2017-02-11T13:23:00.000Z","key":1486819380000,"doc_count":15},{"key_as_string":"2017-02-11T13:24:00.000Z","key":1486819440000,"doc_count":14},{"key_as_string":"2017-02-11T13:25:00.000Z","key":1486819500000,"doc_count":11},{"key_as_string":"2017-02-11T13:26:00.000Z","key":1486819560000,"doc_count":12},{"key_as_string":"2017-02-11T13:27:00.000Z","key":1486819620000,"doc_count":13},{"key_as_string":"2017-02-11T13:28:00.000Z","key":1486819680000,"doc_count":16},{"key_as_string":"2017-02-11T13:29:00.000Z","key":1486819740000,"doc_count":8},{"key_as_string":"2017-02-11T13:30:00.000Z","key":1486819800000,"doc_count":12},{"key_as_string":"2017-02-11T13:31:00.000Z","key":1486819860000,"doc_count":10},{"key_as_string":"2017-02-11T13:32:00.000Z","key":1486819920000,"doc_count":15},{"key_as_string":"2017-02-11T13:33:00.000Z","key":1486819980000,"doc_count":13},{"key_as_string":"2017-02-11T13:34:00.000Z","key":1486820040000,"doc_count":15},{"key_as_string":"2017-02-11T13:35:00.000Z","key":1486820100000,"doc_count":13},{"key_as_string":"2017-02-11T13:36:00.000Z","key":1486820160000,"doc_count":15},{"key_as_string":"2017-02-11T13:37:00.000Z","key":1486820220000,"doc_count":19},{"key_as_string":"2017-02-11T13:38:00.000Z","key":1486820280000,"doc_count":13},{"key_as_string":"2017-02-11T13:39:00.000Z","key":1486820340000,"doc_count":15},{"key_as_string":"2017-02-11T13:40:00.000Z","key":1486820400000,"doc_count":16},{"key_as_string":"2017-02-11T13:41:00.000Z","key":1486820460000,"doc_count":14},{"key_as_string":"2017-02-11T13:42:00.000Z","key":1486820520000,"doc_count":18},{"key_as_string":"2017-02-11T13:43:00.000Z","key":1486820580000,"doc_count":11},{"key_as_string":"2017-02-11T13:44:00.000Z","key":1486820640000,"doc_count":14},{"key_as_string":"2017-02-11T13:45:00.000Z","key":1486820700000,"doc_count":14},{"key_as_string":"2017-02-11T13:46:00.000Z","key":1486820760000,"doc_count":13},{"key_as_string":"2017-02-11T13:47:00.000Z","key":1486820820000,"doc_count":9},{"key_as_string":"2017-02-11T13:48:00.000Z","key":1486820880000,"doc_count":11},{"key_as_string":"2017-02-11T13:49:00.000Z","key":1486820940000,"doc_count":20},{"key_as_string":"2017-02-11T13:50:00.000Z","key":1486821000000,"doc_count":9},{"key_as_string":"2017-02-11T13:51:00.000Z","key":1486821060000,"doc_count":8},{"key_as_string":"2017-02-11T13:52:00.000Z","key":1486821120000,"doc_count":20},{"key_as_string":"2017-02-11T13:53:00.000Z","key":1486821180000,"doc_count":11},{"key_as_string":"2017-02-11T13:54:00.000Z","key":1486821240000,"doc_count":16},{"key_as_string":"2017-02-11T13:55:00.000Z","key":1486821300000,"doc_count":15},{"key_as_string":"2017-02-11T13:56:00.000Z","key":1486821360000,"doc_count":14},{"key_as_string":"2017-02-11T13:57:00.000Z","key":1486821420000,"doc_count":15},{"key_as_string":"2017-02-11T13:58:00.000Z","key":1486821480000,"doc_count":15},{"key_as_string":"2017-02-11T13:59:00.000Z","key":1486821540000,"doc_count":9},{"key_as_string":"2017-02-11T14:00:00.000Z","key":1486821600000,"doc_count":12},{"key_as_string":"2017-02-11T14:01:00.000Z","key":1486821660000,"doc_count":13},{"key_as_string":"2017-02-11T14:02:00.000Z","key":1486821720000,"doc_count":15},{"key_as_string":"2017-02-11T14:03:00.000Z","key":1486821780000,"doc_count":12},{"key_as_string":"2017-02-11T14:04:00.000Z","key":1486821840000,"doc_count":16},{"key_as_string":"2017-02-11T14:05:00.000Z","key":1486821900000,"doc_count":10},{"key_as_string":"2017-02-11T14:06:00.000Z","key":1486821960000,"doc_count":12},{"key_as_string":"2017-02-11T14:07:00.000Z","key":1486822020000,"doc_count":13},{"key_as_string":"2017-02-11T14:08:00.000Z","key":1486822080000,"doc_count":9},{"key_as_string":"2017-02-11T14:09:00.000Z","key":1486822140000,"doc_count":16},{"key_as_string":"2017-02-11T14:10:00.000Z","key":1486822200000,"doc_count":15},{"key_as_string":"2017-02-11T14:11:00.000Z","key":1486822260000,"doc_count":14},{"key_as_string":"2017-02-11T14:12:00.000Z","key":1486822320000,"doc_count":10},{"key_as_string":"2017-02-11T14:13:00.000Z","key":1486822380000,"doc_count":10},{"key_as_string":"2017-02-11T14:14:00.000Z","key":1486822440000,"doc_count":15},{"key_as_string":"2017-02-11T14:15:00.000Z","key":1486822500000,"doc_count":10},{"key_as_string":"2017-02-11T14:16:00.000Z","key":1486822560000,"doc_count":13},{"key_as_string":"2017-02-11T14:17:00.000Z","key":1486822620000,"doc_count":14},{"key_as_string":"2017-02-11T14:18:00.000Z","key":1486822680000,"doc_count":14},{"key_as_string":"2017-02-11T14:19:00.000Z","key":1486822740000,"doc_count":17},{"key_as_string":"2017-02-11T14:20:00.000Z","key":1486822800000,"doc_count":13},{"key_as_string":"2017-02-11T14:21:00.000Z","key":1486822860000,"doc_count":14},{"key_as_string":"2017-02-11T14:22:00.000Z","key":1486822920000,"doc_count":12},{"key_as_string":"2017-02-11T14:23:00.000Z","key":1486822980000,"doc_count":21},{"key_as_string":"2017-02-11T14:24:00.000Z","key":1486823040000,"doc_count":14},{"key_as_string":"2017-02-11T14:25:00.000Z","key":1486823100000,"doc_count":9},{"key_as_string":"2017-02-11T14:26:00.000Z","key":1486823160000,"doc_count":8},{"key_as_string":"2017-02-11T14:27:00.000Z","key":1486823220000,"doc_count":14},{"key_as_string":"2017-02-11T14:28:00.000Z","key":1486823280000,"doc_count":10},{"key_as_string":"2017-02-11T14:29:00.000Z","key":1486823340000,"doc_count":12},{"key_as_string":"2017-02-11T14:30:00.000Z","key":1486823400000,"doc_count":12},{"key_as_string":"2017-02-11T14:31:00.000Z","key":1486823460000,"doc_count":14},{"key_as_string":"2017-02-11T14:32:00.000Z","key":1486823520000,"doc_count":15},{"key_as_string":"2017-02-11T14:33:00.000Z","key":1486823580000,"doc_count":14},{"key_as_string":"2017-02-11T14:34:00.000Z","key":1486823640000,"doc_count":9},{"key_as_string":"2017-02-11T14:35:00.000Z","key":1486823700000,"doc_count":14},{"key_as_string":"2017-02-11T14:36:00.000Z","key":1486823760000,"doc_count":12},{"key_as_string":"2017-02-11T14:37:00.000Z","key":1486823820000,"doc_count":17},{"key_as_string":"2017-02-11T14:38:00.000Z","key":1486823880000,"doc_count":11},{"key_as_string":"2017-02-11T14:39:00.000Z","key":1486823940000,"doc_count":12},{"key_as_string":"2017-02-11T14:40:00.000Z","key":1486824000000,"doc_count":15},{"key_as_string":"2017-02-11T14:41:00.000Z","key":1486824060000,"doc_count":11},{"key_as_string":"2017-02-11T14:42:00.000Z","key":1486824120000,"doc_count":13},{"key_as_string":"2017-02-11T14:43:00.000Z","key":1486824180000,"doc_count":18},{"key_as_string":"2017-02-11T14:44:00.000Z","key":1486824240000,"doc_count":10},{"key_as_string":"2017-02-11T14:45:00.000Z","key":1486824300000,"doc_count":13},{"key_as_string":"2017-02-11T14:46:00.000Z","key":1486824360000,"doc_count":9},{"key_as_string":"2017-02-11T14:47:00.000Z","key":1486824420000,"doc_count":14},{"key_as_string":"2017-02-11T14:48:00.000Z","key":1486824480000,"doc_count":16},{"key_as_string":"2017-02-11T14:49:00.000Z","key":1486824540000,"doc_count":14},{"key_as_string":"2017-02-11T14:50:00.000Z","key":1486824600000,"doc_count":13},{"key_as_string":"2017-02-11T14:51:00.000Z","key":1486824660000,"doc_count":12},{"key_as_string":"2017-02-11T14:52:00.000Z","key":1486824720000,"doc_count":11},{"key_as_string":"2017-02-11T14:53:00.000Z","key":1486824780000,"doc_count":16},{"key_as_string":"2017-02-11T14:54:00.000Z","key":1486824840000,"doc_count":12},{"key_as_string":"2017-02-11T14:55:00.000Z","key":1486824900000,"doc_count":11},{"key_as_string":"2017-02-11T14:56:00.000Z","key":1486824960000,"doc_count":14},{"key_as_string":"2017-02-11T14:57:00.000Z","key":1486825020000,"doc_count":15},{"key_as_string":"2017-02-11T14:58:00.000Z","key":1486825080000,"doc_count":10},{"key_as_string":"2017-02-11T14:59:00.000Z","key":1486825140000,"doc_count":18},{"key_as_string":"2017-02-11T15:00:00.000Z","key":1486825200000,"doc_count":15},{"key_as_string":"2017-02-11T15:01:00.000Z","key":1486825260000,"doc_count":10},{"key_as_string":"2017-02-11T15:02:00.000Z","key":1486825320000,"doc_count":17},{"key_as_string":"2017-02-11T15:03:00.000Z","key":1486825380000,"doc_count":12},{"key_as_string":"2017-02-11T15:04:00.000Z","key":1486825440000,"doc_count":13},{"key_as_string":"2017-02-11T15:05:00.000Z","key":1486825500000,"doc_count":14},{"key_as_string":"2017-02-11T15:06:00.000Z","key":1486825560000,"doc_count":10},{"key_as_string":"2017-02-11T15:07:00.000Z","key":1486825620000,"doc_count":13},{"key_as_string":"2017-02-11T15:08:00.000Z","key":1486825680000,"doc_count":12},{"key_as_string":"2017-02-11T15:09:00.000Z","key":1486825740000,"doc_count":13},{"key_as_string":"2017-02-11T15:10:00.000Z","key":1486825800000,"doc_count":11},{"key_as_string":"2017-02-11T15:11:00.000Z","key":1486825860000,"doc_count":16},{"key_as_string":"2017-02-11T15:12:00.000Z","key":1486825920000,"doc_count":10},{"key_as_string":"2017-02-11T15:13:00.000Z","key":1486825980000,"doc_count":11},{"key_as_string":"2017-02-11T15:14:00.000Z","key":1486826040000,"doc_count":18},{"key_as_string":"2017-02-11T15:15:00.000Z","key":1486826100000,"doc_count":15},{"key_as_string":"2017-02-11T15:16:00.000Z","key":1486826160000,"doc_count":12},{"key_as_string":"2017-02-11T15:17:00.000Z","key":1486826220000,"doc_count":18},{"key_as_string":"2017-02-11T15:18:00.000Z","key":1486826280000,"doc_count":11},{"key_as_string":"2017-02-11T15:19:00.000Z","key":1486826340000,"doc_count":9},{"key_as_string":"2017-02-11T15:20:00.000Z","key":1486826400000,"doc_count":19},{"key_as_string":"2017-02-11T15:21:00.000Z","key":1486826460000,"doc_count":11},{"key_as_string":"2017-02-11T15:22:00.000Z","key":1486826520000,"doc_count":9},{"key_as_string":"2017-02-11T15:23:00.000Z","key":1486826580000,"doc_count":16},{"key_as_string":"2017-02-11T15:24:00.000Z","key":1486826640000,"doc_count":14},{"key_as_string":"2017-02-11T15:25:00.000Z","key":1486826700000,"doc_count":17},{"key_as_string":"2017-02-11T15:26:00.000Z","key":1486826760000,"doc_count":14},{"key_as_string":"2017-02-11T15:27:00.000Z","key":1486826820000,"doc_count":17},{"key_as_string":"2017-02-11T15:28:00.000Z","key":1486826880000,"doc_count":10},{"key_as_string":"2017-02-11T15:29:00.000Z","key":1486826940000,"doc_count":20},{"key_as_string":"2017-02-11T15:30:00.000Z","key":1486827000000,"doc_count":7},{"key_as_string":"2017-02-11T15:31:00.000Z","key":1486827060000,"doc_count":12},{"key_as_string":"2017-02-11T15:32:00.000Z","key":1486827120000,"doc_count":12},{"key_as_string":"2017-02-11T15:33:00.000Z","key":1486827180000,"doc_count":10},{"key_as_string":"2017-02-11T15:34:00.000Z","key":1486827240000,"doc_count":16},{"key_as_string":"2017-02-11T15:35:00.000Z","key":1486827300000,"doc_count":13},{"key_as_string":"2017-02-11T15:36:00.000Z","key":1486827360000,"doc_count":9},{"key_as_string":"2017-02-11T15:37:00.000Z","key":1486827420000,"doc_count":12},{"key_as_string":"2017-02-11T15:38:00.000Z","key":1486827480000,"doc_count":17},{"key_as_string":"2017-02-11T15:39:00.000Z","key":1486827540000,"doc_count":11},{"key_as_string":"2017-02-11T15:40:00.000Z","key":1486827600000,"doc_count":17},{"key_as_string":"2017-02-11T15:41:00.000Z","key":1486827660000,"doc_count":12},{"key_as_string":"2017-02-11T15:42:00.000Z","key":1486827720000,"doc_count":11},{"key_as_string":"2017-02-11T15:43:00.000Z","key":1486827780000,"doc_count":10},{"key_as_string":"2017-02-11T15:44:00.000Z","key":1486827840000,"doc_count":15},{"key_as_string":"2017-02-11T15:45:00.000Z","key":1486827900000,"doc_count":11},{"key_as_string":"2017-02-11T15:46:00.000Z","key":1486827960000,"doc_count":19},{"key_as_string":"2017-02-11T15:47:00.000Z","key":1486828020000,"doc_count":12},{"key_as_string":"2017-02-11T15:48:00.000Z","key":1486828080000,"doc_count":9},{"key_as_string":"2017-02-11T15:49:00.000Z","key":1486828140000,"doc_count":12},{"key_as_string":"2017-02-11T15:50:00.000Z","key":1486828200000,"doc_count":15},{"key_as_string":"2017-02-11T15:51:00.000Z","key":1486828260000,"doc_count":11},{"key_as_string":"2017-02-11T15:52:00.000Z","key":1486828320000,"doc_count":19},{"key_as_string":"2017-02-11T15:53:00.000Z","key":1486828380000,"doc_count":8},{"key_as_string":"2017-02-11T15:54:00.000Z","key":1486828440000,"doc_count":14},{"key_as_string":"2017-02-11T15:55:00.000Z","key":1486828500000,"doc_count":22},{"key_as_string":"2017-02-11T15:56:00.000Z","key":1486828560000,"doc_count":12},{"key_as_string":"2017-02-11T15:57:00.000Z","key":1486828620000,"doc_count":15},{"key_as_string":"2017-02-11T15:58:00.000Z","key":1486828680000,"doc_count":15},{"key_as_string":"2017-02-11T15:59:00.000Z","key":1486828740000,"doc_count":12},{"key_as_string":"2017-02-11T16:00:00.000Z","key":1486828800000,"doc_count":13},{"key_as_string":"2017-02-11T16:01:00.000Z","key":1486828860000,"doc_count":19},{"key_as_string":"2017-02-11T16:02:00.000Z","key":1486828920000,"doc_count":18},{"key_as_string":"2017-02-11T16:03:00.000Z","key":1486828980000,"doc_count":11},{"key_as_string":"2017-02-11T16:04:00.000Z","key":1486829040000,"doc_count":12},{"key_as_string":"2017-02-11T16:05:00.000Z","key":1486829100000,"doc_count":14},{"key_as_string":"2017-02-11T16:06:00.000Z","key":1486829160000,"doc_count":18},{"key_as_string":"2017-02-11T16:07:00.000Z","key":1486829220000,"doc_count":14},{"key_as_string":"2017-02-11T16:08:00.000Z","key":1486829280000,"doc_count":13},{"key_as_string":"2017-02-11T16:09:00.000Z","key":1486829340000,"doc_count":12},{"key_as_string":"2017-02-11T16:10:00.000Z","key":1486829400000,"doc_count":8},{"key_as_string":"2017-02-11T16:11:00.000Z","key":1486829460000,"doc_count":12},{"key_as_string":"2017-02-11T16:12:00.000Z","key":1486829520000,"doc_count":18},{"key_as_string":"2017-02-11T16:13:00.000Z","key":1486829580000,"doc_count":11},{"key_as_string":"2017-02-11T16:14:00.000Z","key":1486829640000,"doc_count":13},{"key_as_string":"2017-02-11T16:15:00.000Z","key":1486829700000,"doc_count":9},{"key_as_string":"2017-02-11T16:16:00.000Z","key":1486829760000,"doc_count":12},{"key_as_string":"2017-02-11T16:17:00.000Z","key":1486829820000,"doc_count":8},{"key_as_string":"2017-02-11T16:18:00.000Z","key":1486829880000,"doc_count":15},{"key_as_string":"2017-02-11T16:19:00.000Z","key":1486829940000,"doc_count":12},{"key_as_string":"2017-02-11T16:20:00.000Z","key":1486830000000,"doc_count":18},{"key_as_string":"2017-02-11T16:21:00.000Z","key":1486830060000,"doc_count":12},{"key_as_string":"2017-02-11T16:22:00.000Z","key":1486830120000,"doc_count":15},{"key_as_string":"2017-02-11T16:23:00.000Z","key":1486830180000,"doc_count":9},{"key_as_string":"2017-02-11T16:24:00.000Z","key":1486830240000,"doc_count":19},{"key_as_string":"2017-02-11T16:25:00.000Z","key":1486830300000,"doc_count":10},{"key_as_string":"2017-02-11T16:26:00.000Z","key":1486830360000,"doc_count":13},{"key_as_string":"2017-02-11T16:27:00.000Z","key":1486830420000,"doc_count":11},{"key_as_string":"2017-02-11T16:28:00.000Z","key":1486830480000,"doc_count":8},{"key_as_string":"2017-02-11T16:29:00.000Z","key":1486830540000,"doc_count":16},{"key_as_string":"2017-02-11T16:30:00.000Z","key":1486830600000,"doc_count":14},{"key_as_string":"2017-02-11T16:31:00.000Z","key":1486830660000,"doc_count":12},{"key_as_string":"2017-02-11T16:32:00.000Z","key":1486830720000,"doc_count":6},{"key_as_string":"2017-02-11T16:33:00.000Z","key":1486830780000,"doc_count":13},{"key_as_string":"2017-02-11T16:34:00.000Z","key":1486830840000,"doc_count":12},{"key_as_string":"2017-02-11T16:35:00.000Z","key":1486830900000,"doc_count":15},{"key_as_string":"2017-02-11T16:36:00.000Z","key":1486830960000,"doc_count":8},{"key_as_string":"2017-02-11T16:37:00.000Z","key":1486831020000,"doc_count":11},{"key_as_string":"2017-02-11T16:38:00.000Z","key":1486831080000,"doc_count":17},{"key_as_string":"2017-02-11T16:39:00.000Z","key":1486831140000,"doc_count":11},{"key_as_string":"2017-02-11T16:40:00.000Z","key":1486831200000,"doc_count":15},{"key_as_string":"2017-02-11T16:41:00.000Z","key":1486831260000,"doc_count":15},{"key_as_string":"2017-02-11T16:42:00.000Z","key":1486831320000,"doc_count":14},{"key_as_string":"2017-02-11T16:43:00.000Z","key":1486831380000,"doc_count":11},{"key_as_string":"2017-02-11T16:44:00.000Z","key":1486831440000,"doc_count":13},{"key_as_string":"2017-02-11T16:45:00.000Z","key":1486831500000,"doc_count":12},{"key_as_string":"2017-02-11T16:46:00.000Z","key":1486831560000,"doc_count":14},{"key_as_string":"2017-02-11T16:47:00.000Z","key":1486831620000,"doc_count":10},{"key_as_string":"2017-02-11T16:48:00.000Z","key":1486831680000,"doc_count":11},{"key_as_string":"2017-02-11T16:49:00.000Z","key":1486831740000,"doc_count":14},{"key_as_string":"2017-02-11T16:50:00.000Z","key":1486831800000,"doc_count":16},{"key_as_string":"2017-02-11T16:51:00.000Z","key":1486831860000,"doc_count":11},{"key_as_string":"2017-02-11T16:52:00.000Z","key":1486831920000,"doc_count":9},{"key_as_string":"2017-02-11T16:53:00.000Z","key":1486831980000,"doc_count":13},{"key_as_string":"2017-02-11T16:54:00.000Z","key":1486832040000,"doc_count":13},{"key_as_string":"2017-02-11T16:55:00.000Z","key":1486832100000,"doc_count":16},{"key_as_string":"2017-02-11T16:56:00.000Z","key":1486832160000,"doc_count":10},{"key_as_string":"2017-02-11T16:57:00.000Z","key":1486832220000,"doc_count":9},{"key_as_string":"2017-02-11T16:58:00.000Z","key":1486832280000,"doc_count":22},{"key_as_string":"2017-02-11T16:59:00.000Z","key":1486832340000,"doc_count":12},{"key_as_string":"2017-02-11T17:00:00.000Z","key":1486832400000,"doc_count":8},{"key_as_string":"2017-02-11T17:01:00.000Z","key":1486832460000,"doc_count":13},{"key_as_string":"2017-02-11T17:02:00.000Z","key":1486832520000,"doc_count":13},{"key_as_string":"2017-02-11T17:03:00.000Z","key":1486832580000,"doc_count":9},{"key_as_string":"2017-02-11T17:04:00.000Z","key":1486832640000,"doc_count":11},{"key_as_string":"2017-02-11T17:05:00.000Z","key":1486832700000,"doc_count":10},{"key_as_string":"2017-02-11T17:06:00.000Z","key":1486832760000,"doc_count":15},{"key_as_string":"2017-02-11T17:07:00.000Z","key":1486832820000,"doc_count":12},{"key_as_string":"2017-02-11T17:08:00.000Z","key":1486832880000,"doc_count":10},{"key_as_string":"2017-02-11T17:09:00.000Z","key":1486832940000,"doc_count":16},{"key_as_string":"2017-02-11T17:10:00.000Z","key":1486833000000,"doc_count":14},{"key_as_string":"2017-02-11T17:11:00.000Z","key":1486833060000,"doc_count":13},{"key_as_string":"2017-02-11T17:12:00.000Z","key":1486833120000,"doc_count":16},{"key_as_string":"2017-02-11T17:13:00.000Z","key":1486833180000,"doc_count":9},{"key_as_string":"2017-02-11T17:14:00.000Z","key":1486833240000,"doc_count":5},{"key_as_string":"2017-02-11T17:15:00.000Z","key":1486833300000,"doc_count":15},{"key_as_string":"2017-02-11T17:16:00.000Z","key":1486833360000,"doc_count":14},{"key_as_string":"2017-02-11T17:17:00.000Z","key":1486833420000,"doc_count":8},{"key_as_string":"2017-02-11T17:18:00.000Z","key":1486833480000,"doc_count":12},{"key_as_string":"2017-02-11T17:19:00.000Z","key":1486833540000,"doc_count":13},{"key_as_string":"2017-02-11T17:20:00.000Z","key":1486833600000,"doc_count":13},{"key_as_string":"2017-02-11T17:21:00.000Z","key":1486833660000,"doc_count":13},{"key_as_string":"2017-02-11T17:22:00.000Z","key":1486833720000,"doc_count":11},{"key_as_string":"2017-02-11T17:23:00.000Z","key":1486833780000,"doc_count":11},{"key_as_string":"2017-02-11T17:24:00.000Z","key":1486833840000,"doc_count":14},{"key_as_string":"2017-02-11T17:25:00.000Z","key":1486833900000,"doc_count":7},{"key_as_string":"2017-02-11T17:26:00.000Z","key":1486833960000,"doc_count":15},{"key_as_string":"2017-02-11T17:27:00.000Z","key":1486834020000,"doc_count":11},{"key_as_string":"2017-02-11T17:28:00.000Z","key":1486834080000,"doc_count":9},{"key_as_string":"2017-02-11T17:29:00.000Z","key":1486834140000,"doc_count":13},{"key_as_string":"2017-02-11T17:30:00.000Z","key":1486834200000,"doc_count":11},{"key_as_string":"2017-02-11T17:31:00.000Z","key":1486834260000,"doc_count":7},{"key_as_string":"2017-02-11T17:32:00.000Z","key":1486834320000,"doc_count":14},{"key_as_string":"2017-02-11T17:33:00.000Z","key":1486834380000,"doc_count":13},{"key_as_string":"2017-02-11T17:34:00.000Z","key":1486834440000,"doc_count":10},{"key_as_string":"2017-02-11T17:35:00.000Z","key":1486834500000,"doc_count":13},{"key_as_string":"2017-02-11T17:36:00.000Z","key":1486834560000,"doc_count":12},{"key_as_string":"2017-02-11T17:37:00.000Z","key":1486834620000,"doc_count":13},{"key_as_string":"2017-02-11T17:38:00.000Z","key":1486834680000,"doc_count":12},{"key_as_string":"2017-02-11T17:39:00.000Z","key":1486834740000,"doc_count":11},{"key_as_string":"2017-02-11T17:40:00.000Z","key":1486834800000,"doc_count":15},{"key_as_string":"2017-02-11T17:41:00.000Z","key":1486834860000,"doc_count":15},{"key_as_string":"2017-02-11T17:42:00.000Z","key":1486834920000,"doc_count":14},{"key_as_string":"2017-02-11T17:43:00.000Z","key":1486834980000,"doc_count":10},{"key_as_string":"2017-02-11T17:44:00.000Z","key":1486835040000,"doc_count":8},{"key_as_string":"2017-02-11T17:45:00.000Z","key":1486835100000,"doc_count":14},{"key_as_string":"2017-02-11T17:46:00.000Z","key":1486835160000,"doc_count":8},{"key_as_string":"2017-02-11T17:47:00.000Z","key":1486835220000,"doc_count":11},{"key_as_string":"2017-02-11T17:48:00.000Z","key":1486835280000,"doc_count":13},{"key_as_string":"2017-02-11T17:49:00.000Z","key":1486835340000,"doc_count":13},{"key_as_string":"2017-02-11T17:50:00.000Z","key":1486835400000,"doc_count":10},{"key_as_string":"2017-02-11T17:51:00.000Z","key":1486835460000,"doc_count":11},{"key_as_string":"2017-02-11T17:52:00.000Z","key":1486835520000,"doc_count":22},{"key_as_string":"2017-02-11T17:53:00.000Z","key":1486835580000,"doc_count":14},{"key_as_string":"2017-02-11T17:54:00.000Z","key":1486835640000,"doc_count":11},{"key_as_string":"2017-02-11T17:55:00.000Z","key":1486835700000,"doc_count":11},{"key_as_string":"2017-02-11T17:56:00.000Z","key":1486835760000,"doc_count":10},{"key_as_string":"2017-02-11T17:57:00.000Z","key":1486835820000,"doc_count":13},{"key_as_string":"2017-02-11T17:58:00.000Z","key":1486835880000,"doc_count":6},{"key_as_string":"2017-02-11T17:59:00.000Z","key":1486835940000,"doc_count":16},{"key_as_string":"2017-02-11T18:00:00.000Z","key":1486836000000,"doc_count":11},{"key_as_string":"2017-02-11T18:01:00.000Z","key":1486836060000,"doc_count":15},{"key_as_string":"2017-02-11T18:02:00.000Z","key":1486836120000,"doc_count":8},{"key_as_string":"2017-02-11T18:03:00.000Z","key":1486836180000,"doc_count":12},{"key_as_string":"2017-02-11T18:04:00.000Z","key":1486836240000,"doc_count":14},{"key_as_string":"2017-02-11T18:05:00.000Z","key":1486836300000,"doc_count":11},{"key_as_string":"2017-02-11T18:06:00.000Z","key":1486836360000,"doc_count":15},{"key_as_string":"2017-02-11T18:07:00.000Z","key":1486836420000,"doc_count":9},{"key_as_string":"2017-02-11T18:08:00.000Z","key":1486836480000,"doc_count":11},{"key_as_string":"2017-02-11T18:09:00.000Z","key":1486836540000,"doc_count":9},{"key_as_string":"2017-02-11T18:10:00.000Z","key":1486836600000,"doc_count":10},{"key_as_string":"2017-02-11T18:11:00.000Z","key":1486836660000,"doc_count":12},{"key_as_string":"2017-02-11T18:12:00.000Z","key":1486836720000,"doc_count":10},{"key_as_string":"2017-02-11T18:13:00.000Z","key":1486836780000,"doc_count":11},{"key_as_string":"2017-02-11T18:14:00.000Z","key":1486836840000,"doc_count":12},{"key_as_string":"2017-02-11T18:15:00.000Z","key":1486836900000,"doc_count":11},{"key_as_string":"2017-02-11T18:16:00.000Z","key":1486836960000,"doc_count":9},{"key_as_string":"2017-02-11T18:17:00.000Z","key":1486837020000,"doc_count":7},{"key_as_string":"2017-02-11T18:18:00.000Z","key":1486837080000,"doc_count":16},{"key_as_string":"2017-02-11T18:19:00.000Z","key":1486837140000,"doc_count":10},{"key_as_string":"2017-02-11T18:20:00.000Z","key":1486837200000,"doc_count":14},{"key_as_string":"2017-02-11T18:21:00.000Z","key":1486837260000,"doc_count":5},{"key_as_string":"2017-02-11T18:22:00.000Z","key":1486837320000,"doc_count":12},{"key_as_string":"2017-02-11T18:23:00.000Z","key":1486837380000,"doc_count":13},{"key_as_string":"2017-02-11T18:24:00.000Z","key":1486837440000,"doc_count":12},{"key_as_string":"2017-02-11T18:25:00.000Z","key":1486837500000,"doc_count":9},{"key_as_string":"2017-02-11T18:26:00.000Z","key":1486837560000,"doc_count":12},{"key_as_string":"2017-02-11T18:27:00.000Z","key":1486837620000,"doc_count":11},{"key_as_string":"2017-02-11T18:28:00.000Z","key":1486837680000,"doc_count":12},{"key_as_string":"2017-02-11T18:29:00.000Z","key":1486837740000,"doc_count":10},{"key_as_string":"2017-02-11T18:30:00.000Z","key":1486837800000,"doc_count":11},{"key_as_string":"2017-02-11T18:31:00.000Z","key":1486837860000,"doc_count":9},{"key_as_string":"2017-02-11T18:32:00.000Z","key":1486837920000,"doc_count":13},{"key_as_string":"2017-02-11T18:33:00.000Z","key":1486837980000,"doc_count":11},{"key_as_string":"2017-02-11T18:34:00.000Z","key":1486838040000,"doc_count":13},{"key_as_string":"2017-02-11T18:35:00.000Z","key":1486838100000,"doc_count":12},{"key_as_string":"2017-02-11T18:36:00.000Z","key":1486838160000,"doc_count":8},{"key_as_string":"2017-02-11T18:37:00.000Z","key":1486838220000,"doc_count":10},{"key_as_string":"2017-02-11T18:38:00.000Z","key":1486838280000,"doc_count":10},{"key_as_string":"2017-02-11T18:39:00.000Z","key":1486838340000,"doc_count":10},{"key_as_string":"2017-02-11T18:40:00.000Z","key":1486838400000,"doc_count":14},{"key_as_string":"2017-02-11T18:41:00.000Z","key":1486838460000,"doc_count":13},{"key_as_string":"2017-02-11T18:42:00.000Z","key":1486838520000,"doc_count":15},{"key_as_string":"2017-02-11T18:43:00.000Z","key":1486838580000,"doc_count":9},{"key_as_string":"2017-02-11T18:44:00.000Z","key":1486838640000,"doc_count":10},{"key_as_string":"2017-02-11T18:45:00.000Z","key":1486838700000,"doc_count":14},{"key_as_string":"2017-02-11T18:46:00.000Z","key":1486838760000,"doc_count":9},{"key_as_string":"2017-02-11T18:47:00.000Z","key":1486838820000,"doc_count":14},{"key_as_string":"2017-02-11T18:48:00.000Z","key":1486838880000,"doc_count":8},{"key_as_string":"2017-02-11T18:49:00.000Z","key":1486838940000,"doc_count":10},{"key_as_string":"2017-02-11T18:50:00.000Z","key":1486839000000,"doc_count":15},{"key_as_string":"2017-02-11T18:51:00.000Z","key":1486839060000,"doc_count":9},{"key_as_string":"2017-02-11T18:52:00.000Z","key":1486839120000,"doc_count":13},{"key_as_string":"2017-02-11T18:53:00.000Z","key":1486839180000,"doc_count":14},{"key_as_string":"2017-02-11T18:54:00.000Z","key":1486839240000,"doc_count":9},{"key_as_string":"2017-02-11T18:55:00.000Z","key":1486839300000,"doc_count":9},{"key_as_string":"2017-02-11T18:56:00.000Z","key":1486839360000,"doc_count":9},{"key_as_string":"2017-02-11T18:57:00.000Z","key":1486839420000,"doc_count":12},{"key_as_string":"2017-02-11T18:58:00.000Z","key":1486839480000,"doc_count":14},{"key_as_string":"2017-02-11T18:59:00.000Z","key":1486839540000,"doc_count":9},{"key_as_string":"2017-02-11T19:00:00.000Z","key":1486839600000,"doc_count":14},{"key_as_string":"2017-02-11T19:01:00.000Z","key":1486839660000,"doc_count":13},{"key_as_string":"2017-02-11T19:02:00.000Z","key":1486839720000,"doc_count":13},{"key_as_string":"2017-02-11T19:03:00.000Z","key":1486839780000,"doc_count":15},{"key_as_string":"2017-02-11T19:04:00.000Z","key":1486839840000,"doc_count":11},{"key_as_string":"2017-02-11T19:05:00.000Z","key":1486839900000,"doc_count":11},{"key_as_string":"2017-02-11T19:06:00.000Z","key":1486839960000,"doc_count":10},{"key_as_string":"2017-02-11T19:07:00.000Z","key":1486840020000,"doc_count":11},{"key_as_string":"2017-02-11T19:08:00.000Z","key":1486840080000,"doc_count":15},{"key_as_string":"2017-02-11T19:09:00.000Z","key":1486840140000,"doc_count":13},{"key_as_string":"2017-02-11T19:10:00.000Z","key":1486840200000,"doc_count":17},{"key_as_string":"2017-02-11T19:11:00.000Z","key":1486840260000,"doc_count":9},{"key_as_string":"2017-02-11T19:12:00.000Z","key":1486840320000,"doc_count":10},{"key_as_string":"2017-02-11T19:13:00.000Z","key":1486840380000,"doc_count":8},{"key_as_string":"2017-02-11T19:14:00.000Z","key":1486840440000,"doc_count":17},{"key_as_string":"2017-02-11T19:15:00.000Z","key":1486840500000,"doc_count":8},{"key_as_string":"2017-02-11T19:16:00.000Z","key":1486840560000,"doc_count":13},{"key_as_string":"2017-02-11T19:17:00.000Z","key":1486840620000,"doc_count":10},{"key_as_string":"2017-02-11T19:18:00.000Z","key":1486840680000,"doc_count":9},{"key_as_string":"2017-02-11T19:19:00.000Z","key":1486840740000,"doc_count":13},{"key_as_string":"2017-02-11T19:20:00.000Z","key":1486840800000,"doc_count":12},{"key_as_string":"2017-02-11T19:21:00.000Z","key":1486840860000,"doc_count":18},{"key_as_string":"2017-02-11T19:22:00.000Z","key":1486840920000,"doc_count":14},{"key_as_string":"2017-02-11T19:23:00.000Z","key":1486840980000,"doc_count":10},{"key_as_string":"2017-02-11T19:24:00.000Z","key":1486841040000,"doc_count":11},{"key_as_string":"2017-02-11T19:25:00.000Z","key":1486841100000,"doc_count":12},{"key_as_string":"2017-02-11T19:26:00.000Z","key":1486841160000,"doc_count":16},{"key_as_string":"2017-02-11T19:27:00.000Z","key":1486841220000,"doc_count":10},{"key_as_string":"2017-02-11T19:28:00.000Z","key":1486841280000,"doc_count":16},{"key_as_string":"2017-02-11T19:29:00.000Z","key":1486841340000,"doc_count":6},{"key_as_string":"2017-02-11T19:30:00.000Z","key":1486841400000,"doc_count":12},{"key_as_string":"2017-02-11T19:31:00.000Z","key":1486841460000,"doc_count":10},{"key_as_string":"2017-02-11T19:32:00.000Z","key":1486841520000,"doc_count":15},{"key_as_string":"2017-02-11T19:33:00.000Z","key":1486841580000,"doc_count":16},{"key_as_string":"2017-02-11T19:34:00.000Z","key":1486841640000,"doc_count":14},{"key_as_string":"2017-02-11T19:35:00.000Z","key":1486841700000,"doc_count":5},{"key_as_string":"2017-02-11T19:36:00.000Z","key":1486841760000,"doc_count":15},{"key_as_string":"2017-02-11T19:37:00.000Z","key":1486841820000,"doc_count":9},{"key_as_string":"2017-02-11T19:38:00.000Z","key":1486841880000,"doc_count":11},{"key_as_string":"2017-02-11T19:39:00.000Z","key":1486841940000,"doc_count":9},{"key_as_string":"2017-02-11T19:40:00.000Z","key":1486842000000,"doc_count":14},{"key_as_string":"2017-02-11T19:41:00.000Z","key":1486842060000,"doc_count":8},{"key_as_string":"2017-02-11T19:42:00.000Z","key":1486842120000,"doc_count":7},{"key_as_string":"2017-02-11T19:43:00.000Z","key":1486842180000,"doc_count":8},{"key_as_string":"2017-02-11T19:44:00.000Z","key":1486842240000,"doc_count":12},{"key_as_string":"2017-02-11T19:45:00.000Z","key":1486842300000,"doc_count":12},{"key_as_string":"2017-02-11T19:46:00.000Z","key":1486842360000,"doc_count":11},{"key_as_string":"2017-02-11T19:47:00.000Z","key":1486842420000,"doc_count":10},{"key_as_string":"2017-02-11T19:48:00.000Z","key":1486842480000,"doc_count":8},{"key_as_string":"2017-02-11T19:49:00.000Z","key":1486842540000,"doc_count":11},{"key_as_string":"2017-02-11T19:50:00.000Z","key":1486842600000,"doc_count":12},{"key_as_string":"2017-02-11T19:51:00.000Z","key":1486842660000,"doc_count":10},{"key_as_string":"2017-02-11T19:52:00.000Z","key":1486842720000,"doc_count":11},{"key_as_string":"2017-02-11T19:53:00.000Z","key":1486842780000,"doc_count":17},{"key_as_string":"2017-02-11T19:54:00.000Z","key":1486842840000,"doc_count":11},{"key_as_string":"2017-02-11T19:55:00.000Z","key":1486842900000,"doc_count":11},{"key_as_string":"2017-02-11T19:56:00.000Z","key":1486842960000,"doc_count":14},{"key_as_string":"2017-02-11T19:57:00.000Z","key":1486843020000,"doc_count":5},{"key_as_string":"2017-02-11T19:58:00.000Z","key":1486843080000,"doc_count":17},{"key_as_string":"2017-02-11T19:59:00.000Z","key":1486843140000,"doc_count":7},{"key_as_string":"2017-02-11T20:00:00.000Z","key":1486843200000,"doc_count":12},{"key_as_string":"2017-02-11T20:01:00.000Z","key":1486843260000,"doc_count":12},{"key_as_string":"2017-02-11T20:02:00.000Z","key":1486843320000,"doc_count":15},{"key_as_string":"2017-02-11T20:03:00.000Z","key":1486843380000,"doc_count":8},{"key_as_string":"2017-02-11T20:04:00.000Z","key":1486843440000,"doc_count":10},{"key_as_string":"2017-02-11T20:05:00.000Z","key":1486843500000,"doc_count":10},{"key_as_string":"2017-02-11T20:06:00.000Z","key":1486843560000,"doc_count":10},{"key_as_string":"2017-02-11T20:07:00.000Z","key":1486843620000,"doc_count":14},{"key_as_string":"2017-02-11T20:08:00.000Z","key":1486843680000,"doc_count":11},{"key_as_string":"2017-02-11T20:09:00.000Z","key":1486843740000,"doc_count":9},{"key_as_string":"2017-02-11T20:10:00.000Z","key":1486843800000,"doc_count":7},{"key_as_string":"2017-02-11T20:11:00.000Z","key":1486843860000,"doc_count":17},{"key_as_string":"2017-02-11T20:12:00.000Z","key":1486843920000,"doc_count":11},{"key_as_string":"2017-02-11T20:13:00.000Z","key":1486843980000,"doc_count":15},{"key_as_string":"2017-02-11T20:14:00.000Z","key":1486844040000,"doc_count":12},{"key_as_string":"2017-02-11T20:15:00.000Z","key":1486844100000,"doc_count":9},{"key_as_string":"2017-02-11T20:16:00.000Z","key":1486844160000,"doc_count":7},{"key_as_string":"2017-02-11T20:17:00.000Z","key":1486844220000,"doc_count":9},{"key_as_string":"2017-02-11T20:18:00.000Z","key":1486844280000,"doc_count":14},{"key_as_string":"2017-02-11T20:19:00.000Z","key":1486844340000,"doc_count":9},{"key_as_string":"2017-02-11T20:20:00.000Z","key":1486844400000,"doc_count":13},{"key_as_string":"2017-02-11T20:21:00.000Z","key":1486844460000,"doc_count":11},{"key_as_string":"2017-02-11T20:22:00.000Z","key":1486844520000,"doc_count":15},{"key_as_string":"2017-02-11T20:23:00.000Z","key":1486844580000,"doc_count":13},{"key_as_string":"2017-02-11T20:24:00.000Z","key":1486844640000,"doc_count":11},{"key_as_string":"2017-02-11T20:25:00.000Z","key":1486844700000,"doc_count":7},{"key_as_string":"2017-02-11T20:26:00.000Z","key":1486844760000,"doc_count":7},{"key_as_string":"2017-02-11T20:27:00.000Z","key":1486844820000,"doc_count":13},{"key_as_string":"2017-02-11T20:28:00.000Z","key":1486844880000,"doc_count":8},{"key_as_string":"2017-02-11T20:29:00.000Z","key":1486844940000,"doc_count":12},{"key_as_string":"2017-02-11T20:30:00.000Z","key":1486845000000,"doc_count":12},{"key_as_string":"2017-02-11T20:31:00.000Z","key":1486845060000,"doc_count":11},{"key_as_string":"2017-02-11T20:32:00.000Z","key":1486845120000,"doc_count":11},{"key_as_string":"2017-02-11T20:33:00.000Z","key":1486845180000,"doc_count":10},{"key_as_string":"2017-02-11T20:34:00.000Z","key":1486845240000,"doc_count":15},{"key_as_string":"2017-02-11T20:35:00.000Z","key":1486845300000,"doc_count":5},{"key_as_string":"2017-02-11T20:36:00.000Z","key":1486845360000,"doc_count":9},{"key_as_string":"2017-02-11T20:37:00.000Z","key":1486845420000,"doc_count":11},{"key_as_string":"2017-02-11T20:38:00.000Z","key":1486845480000,"doc_count":9},{"key_as_string":"2017-02-11T20:39:00.000Z","key":1486845540000,"doc_count":12},{"key_as_string":"2017-02-11T20:40:00.000Z","key":1486845600000,"doc_count":16},{"key_as_string":"2017-02-11T20:41:00.000Z","key":1486845660000,"doc_count":6},{"key_as_string":"2017-02-11T20:42:00.000Z","key":1486845720000,"doc_count":11},{"key_as_string":"2017-02-11T20:43:00.000Z","key":1486845780000,"doc_count":13},{"key_as_string":"2017-02-11T20:44:00.000Z","key":1486845840000,"doc_count":7},{"key_as_string":"2017-02-11T20:45:00.000Z","key":1486845900000,"doc_count":11},{"key_as_string":"2017-02-11T20:46:00.000Z","key":1486845960000,"doc_count":7},{"key_as_string":"2017-02-11T20:47:00.000Z","key":1486846020000,"doc_count":9},{"key_as_string":"2017-02-11T20:48:00.000Z","key":1486846080000,"doc_count":14},{"key_as_string":"2017-02-11T20:49:00.000Z","key":1486846140000,"doc_count":9},{"key_as_string":"2017-02-11T20:50:00.000Z","key":1486846200000,"doc_count":12},{"key_as_string":"2017-02-11T20:51:00.000Z","key":1486846260000,"doc_count":17},{"key_as_string":"2017-02-11T20:52:00.000Z","key":1486846320000,"doc_count":13},{"key_as_string":"2017-02-11T20:53:00.000Z","key":1486846380000,"doc_count":7},{"key_as_string":"2017-02-11T20:54:00.000Z","key":1486846440000,"doc_count":15},{"key_as_string":"2017-02-11T20:55:00.000Z","key":1486846500000,"doc_count":13},{"key_as_string":"2017-02-11T20:56:00.000Z","key":1486846560000,"doc_count":16},{"key_as_string":"2017-02-11T20:57:00.000Z","key":1486846620000,"doc_count":12},{"key_as_string":"2017-02-11T20:58:00.000Z","key":1486846680000,"doc_count":8},{"key_as_string":"2017-02-11T20:59:00.000Z","key":1486846740000,"doc_count":14},{"key_as_string":"2017-02-11T21:00:00.000Z","key":1486846800000,"doc_count":11},{"key_as_string":"2017-02-11T21:01:00.000Z","key":1486846860000,"doc_count":8},{"key_as_string":"2017-02-11T21:02:00.000Z","key":1486846920000,"doc_count":13},{"key_as_string":"2017-02-11T21:03:00.000Z","key":1486846980000,"doc_count":8},{"key_as_string":"2017-02-11T21:04:00.000Z","key":1486847040000,"doc_count":9},{"key_as_string":"2017-02-11T21:05:00.000Z","key":1486847100000,"doc_count":12},{"key_as_string":"2017-02-11T21:06:00.000Z","key":1486847160000,"doc_count":11},{"key_as_string":"2017-02-11T21:07:00.000Z","key":1486847220000,"doc_count":15},{"key_as_string":"2017-02-11T21:08:00.000Z","key":1486847280000,"doc_count":6},{"key_as_string":"2017-02-11T21:09:00.000Z","key":1486847340000,"doc_count":14},{"key_as_string":"2017-02-11T21:10:00.000Z","key":1486847400000,"doc_count":5},{"key_as_string":"2017-02-11T21:11:00.000Z","key":1486847460000,"doc_count":11},{"key_as_string":"2017-02-11T21:12:00.000Z","key":1486847520000,"doc_count":12},{"key_as_string":"2017-02-11T21:13:00.000Z","key":1486847580000,"doc_count":10},{"key_as_string":"2017-02-11T21:14:00.000Z","key":1486847640000,"doc_count":7},{"key_as_string":"2017-02-11T21:15:00.000Z","key":1486847700000,"doc_count":9},{"key_as_string":"2017-02-11T21:16:00.000Z","key":1486847760000,"doc_count":12},{"key_as_string":"2017-02-11T21:17:00.000Z","key":1486847820000,"doc_count":11},{"key_as_string":"2017-02-11T21:18:00.000Z","key":1486847880000,"doc_count":12},{"key_as_string":"2017-02-11T21:19:00.000Z","key":1486847940000,"doc_count":14},{"key_as_string":"2017-02-11T21:20:00.000Z","key":1486848000000,"doc_count":10},{"key_as_string":"2017-02-11T21:21:00.000Z","key":1486848060000,"doc_count":9},{"key_as_string":"2017-02-11T21:22:00.000Z","key":1486848120000,"doc_count":16},{"key_as_string":"2017-02-11T21:23:00.000Z","key":1486848180000,"doc_count":10},{"key_as_string":"2017-02-11T21:24:00.000Z","key":1486848240000,"doc_count":10},{"key_as_string":"2017-02-11T21:25:00.000Z","key":1486848300000,"doc_count":11},{"key_as_string":"2017-02-11T21:26:00.000Z","key":1486848360000,"doc_count":12},{"key_as_string":"2017-02-11T21:27:00.000Z","key":1486848420000,"doc_count":6},{"key_as_string":"2017-02-11T21:28:00.000Z","key":1486848480000,"doc_count":14},{"key_as_string":"2017-02-11T21:29:00.000Z","key":1486848540000,"doc_count":10},{"key_as_string":"2017-02-11T21:30:00.000Z","key":1486848600000,"doc_count":12},{"key_as_string":"2017-02-11T21:31:00.000Z","key":1486848660000,"doc_count":7},{"key_as_string":"2017-02-11T21:32:00.000Z","key":1486848720000,"doc_count":11},{"key_as_string":"2017-02-11T21:33:00.000Z","key":1486848780000,"doc_count":11},{"key_as_string":"2017-02-11T21:34:00.000Z","key":1486848840000,"doc_count":11},{"key_as_string":"2017-02-11T21:35:00.000Z","key":1486848900000,"doc_count":5},{"key_as_string":"2017-02-11T21:36:00.000Z","key":1486848960000,"doc_count":15},{"key_as_string":"2017-02-11T21:37:00.000Z","key":1486849020000,"doc_count":8},{"key_as_string":"2017-02-11T21:38:00.000Z","key":1486849080000,"doc_count":11},{"key_as_string":"2017-02-11T21:39:00.000Z","key":1486849140000,"doc_count":9},{"key_as_string":"2017-02-11T21:40:00.000Z","key":1486849200000,"doc_count":9},{"key_as_string":"2017-02-11T21:41:00.000Z","key":1486849260000,"doc_count":10},{"key_as_string":"2017-02-11T21:42:00.000Z","key":1486849320000,"doc_count":7},{"key_as_string":"2017-02-11T21:43:00.000Z","key":1486849380000,"doc_count":13},{"key_as_string":"2017-02-11T21:44:00.000Z","key":1486849440000,"doc_count":10},{"key_as_string":"2017-02-11T21:45:00.000Z","key":1486849500000,"doc_count":10},{"key_as_string":"2017-02-11T21:46:00.000Z","key":1486849560000,"doc_count":11},{"key_as_string":"2017-02-11T21:47:00.000Z","key":1486849620000,"doc_count":10},{"key_as_string":"2017-02-11T21:48:00.000Z","key":1486849680000,"doc_count":7},{"key_as_string":"2017-02-11T21:49:00.000Z","key":1486849740000,"doc_count":8},{"key_as_string":"2017-02-11T21:50:00.000Z","key":1486849800000,"doc_count":12},{"key_as_string":"2017-02-11T21:51:00.000Z","key":1486849860000,"doc_count":8},{"key_as_string":"2017-02-11T21:52:00.000Z","key":1486849920000,"doc_count":7},{"key_as_string":"2017-02-11T21:53:00.000Z","key":1486849980000,"doc_count":15},{"key_as_string":"2017-02-11T21:54:00.000Z","key":1486850040000,"doc_count":14},{"key_as_string":"2017-02-11T21:55:00.000Z","key":1486850100000,"doc_count":9},{"key_as_string":"2017-02-11T21:56:00.000Z","key":1486850160000,"doc_count":9},{"key_as_string":"2017-02-11T21:57:00.000Z","key":1486850220000,"doc_count":9},{"key_as_string":"2017-02-11T21:58:00.000Z","key":1486850280000,"doc_count":13},{"key_as_string":"2017-02-11T21:59:00.000Z","key":1486850340000,"doc_count":13},{"key_as_string":"2017-02-11T22:00:00.000Z","key":1486850400000,"doc_count":11},{"key_as_string":"2017-02-11T22:01:00.000Z","key":1486850460000,"doc_count":12},{"key_as_string":"2017-02-11T22:02:00.000Z","key":1486850520000,"doc_count":12},{"key_as_string":"2017-02-11T22:03:00.000Z","key":1486850580000,"doc_count":11},{"key_as_string":"2017-02-11T22:04:00.000Z","key":1486850640000,"doc_count":7},{"key_as_string":"2017-02-11T22:05:00.000Z","key":1486850700000,"doc_count":9},{"key_as_string":"2017-02-11T22:06:00.000Z","key":1486850760000,"doc_count":12},{"key_as_string":"2017-02-11T22:07:00.000Z","key":1486850820000,"doc_count":13},{"key_as_string":"2017-02-11T22:08:00.000Z","key":1486850880000,"doc_count":11},{"key_as_string":"2017-02-11T22:09:00.000Z","key":1486850940000,"doc_count":8},{"key_as_string":"2017-02-11T22:10:00.000Z","key":1486851000000,"doc_count":6},{"key_as_string":"2017-02-11T22:11:00.000Z","key":1486851060000,"doc_count":9},{"key_as_string":"2017-02-11T22:12:00.000Z","key":1486851120000,"doc_count":15},{"key_as_string":"2017-02-11T22:13:00.000Z","key":1486851180000,"doc_count":12},{"key_as_string":"2017-02-11T22:14:00.000Z","key":1486851240000,"doc_count":12},{"key_as_string":"2017-02-11T22:15:00.000Z","key":1486851300000,"doc_count":9},{"key_as_string":"2017-02-11T22:16:00.000Z","key":1486851360000,"doc_count":11},{"key_as_string":"2017-02-11T22:17:00.000Z","key":1486851420000,"doc_count":9},{"key_as_string":"2017-02-11T22:18:00.000Z","key":1486851480000,"doc_count":13},{"key_as_string":"2017-02-11T22:19:00.000Z","key":1486851540000,"doc_count":10},{"key_as_string":"2017-02-11T22:20:00.000Z","key":1486851600000,"doc_count":16},{"key_as_string":"2017-02-11T22:21:00.000Z","key":1486851660000,"doc_count":10},{"key_as_string":"2017-02-11T22:22:00.000Z","key":1486851720000,"doc_count":7},{"key_as_string":"2017-02-11T22:23:00.000Z","key":1486851780000,"doc_count":8},{"key_as_string":"2017-02-11T22:24:00.000Z","key":1486851840000,"doc_count":13},{"key_as_string":"2017-02-11T22:25:00.000Z","key":1486851900000,"doc_count":11},{"key_as_string":"2017-02-11T22:26:00.000Z","key":1486851960000,"doc_count":11},{"key_as_string":"2017-02-11T22:27:00.000Z","key":1486852020000,"doc_count":15},{"key_as_string":"2017-02-11T22:28:00.000Z","key":1486852080000,"doc_count":10},{"key_as_string":"2017-02-11T22:29:00.000Z","key":1486852140000,"doc_count":13},{"key_as_string":"2017-02-11T22:30:00.000Z","key":1486852200000,"doc_count":3},{"key_as_string":"2017-02-11T22:31:00.000Z","key":1486852260000,"doc_count":16},{"key_as_string":"2017-02-11T22:32:00.000Z","key":1486852320000,"doc_count":8},{"key_as_string":"2017-02-11T22:33:00.000Z","key":1486852380000,"doc_count":13},{"key_as_string":"2017-02-11T22:34:00.000Z","key":1486852440000,"doc_count":10},{"key_as_string":"2017-02-11T22:35:00.000Z","key":1486852500000,"doc_count":10},{"key_as_string":"2017-02-11T22:36:00.000Z","key":1486852560000,"doc_count":15},{"key_as_string":"2017-02-11T22:37:00.000Z","key":1486852620000,"doc_count":10},{"key_as_string":"2017-02-11T22:38:00.000Z","key":1486852680000,"doc_count":9},{"key_as_string":"2017-02-11T22:39:00.000Z","key":1486852740000,"doc_count":11},{"key_as_string":"2017-02-11T22:40:00.000Z","key":1486852800000,"doc_count":11},{"key_as_string":"2017-02-11T22:41:00.000Z","key":1486852860000,"doc_count":8},{"key_as_string":"2017-02-11T22:42:00.000Z","key":1486852920000,"doc_count":10},{"key_as_string":"2017-02-11T22:43:00.000Z","key":1486852980000,"doc_count":12},{"key_as_string":"2017-02-11T22:44:00.000Z","key":1486853040000,"doc_count":8},{"key_as_string":"2017-02-11T22:45:00.000Z","key":1486853100000,"doc_count":14},{"key_as_string":"2017-02-11T22:46:00.000Z","key":1486853160000,"doc_count":9},{"key_as_string":"2017-02-11T22:47:00.000Z","key":1486853220000,"doc_count":10},{"key_as_string":"2017-02-11T22:48:00.000Z","key":1486853280000,"doc_count":12},{"key_as_string":"2017-02-11T22:49:00.000Z","key":1486853340000,"doc_count":13},{"key_as_string":"2017-02-11T22:50:00.000Z","key":1486853400000,"doc_count":13},{"key_as_string":"2017-02-11T22:51:00.000Z","key":1486853460000,"doc_count":5},{"key_as_string":"2017-02-11T22:52:00.000Z","key":1486853520000,"doc_count":10},{"key_as_string":"2017-02-11T22:53:00.000Z","key":1486853580000,"doc_count":8},{"key_as_string":"2017-02-11T22:54:00.000Z","key":1486853640000,"doc_count":12},{"key_as_string":"2017-02-11T22:55:00.000Z","key":1486853700000,"doc_count":6},{"key_as_string":"2017-02-11T22:56:00.000Z","key":1486853760000,"doc_count":15},{"key_as_string":"2017-02-11T22:57:00.000Z","key":1486853820000,"doc_count":8},{"key_as_string":"2017-02-11T22:58:00.000Z","key":1486853880000,"doc_count":11},{"key_as_string":"2017-02-11T22:59:00.000Z","key":1486853940000,"doc_count":10},{"key_as_string":"2017-02-11T23:00:00.000Z","key":1486854000000,"doc_count":8},{"key_as_string":"2017-02-11T23:01:00.000Z","key":1486854060000,"doc_count":9},{"key_as_string":"2017-02-11T23:02:00.000Z","key":1486854120000,"doc_count":11},{"key_as_string":"2017-02-11T23:03:00.000Z","key":1486854180000,"doc_count":15},{"key_as_string":"2017-02-11T23:04:00.000Z","key":1486854240000,"doc_count":9},{"key_as_string":"2017-02-11T23:05:00.000Z","key":1486854300000,"doc_count":14},{"key_as_string":"2017-02-11T23:06:00.000Z","key":1486854360000,"doc_count":8},{"key_as_string":"2017-02-11T23:07:00.000Z","key":1486854420000,"doc_count":10},{"key_as_string":"2017-02-11T23:08:00.000Z","key":1486854480000,"doc_count":8},{"key_as_string":"2017-02-11T23:09:00.000Z","key":1486854540000,"doc_count":13},{"key_as_string":"2017-02-11T23:10:00.000Z","key":1486854600000,"doc_count":11},{"key_as_string":"2017-02-11T23:11:00.000Z","key":1486854660000,"doc_count":12},{"key_as_string":"2017-02-11T23:12:00.000Z","key":1486854720000,"doc_count":11},{"key_as_string":"2017-02-11T23:13:00.000Z","key":1486854780000,"doc_count":6},{"key_as_string":"2017-02-11T23:14:00.000Z","key":1486854840000,"doc_count":14},{"key_as_string":"2017-02-11T23:15:00.000Z","key":1486854900000,"doc_count":14},{"key_as_string":"2017-02-11T23:16:00.000Z","key":1486854960000,"doc_count":14},{"key_as_string":"2017-02-11T23:17:00.000Z","key":1486855020000,"doc_count":11},{"key_as_string":"2017-02-11T23:18:00.000Z","key":1486855080000,"doc_count":9},{"key_as_string":"2017-02-11T23:19:00.000Z","key":1486855140000,"doc_count":10},{"key_as_string":"2017-02-11T23:20:00.000Z","key":1486855200000,"doc_count":8},{"key_as_string":"2017-02-11T23:21:00.000Z","key":1486855260000,"doc_count":13},{"key_as_string":"2017-02-11T23:22:00.000Z","key":1486855320000,"doc_count":12},{"key_as_string":"2017-02-11T23:23:00.000Z","key":1486855380000,"doc_count":10},{"key_as_string":"2017-02-11T23:24:00.000Z","key":1486855440000,"doc_count":13},{"key_as_string":"2017-02-11T23:25:00.000Z","key":1486855500000,"doc_count":11},{"key_as_string":"2017-02-11T23:26:00.000Z","key":1486855560000,"doc_count":11},{"key_as_string":"2017-02-11T23:27:00.000Z","key":1486855620000,"doc_count":9},{"key_as_string":"2017-02-11T23:28:00.000Z","key":1486855680000,"doc_count":13},{"key_as_string":"2017-02-11T23:29:00.000Z","key":1486855740000,"doc_count":3},{"key_as_string":"2017-02-11T23:30:00.000Z","key":1486855800000,"doc_count":10},{"key_as_string":"2017-02-11T23:31:00.000Z","key":1486855860000,"doc_count":9},{"key_as_string":"2017-02-11T23:32:00.000Z","key":1486855920000,"doc_count":7},{"key_as_string":"2017-02-11T23:33:00.000Z","key":1486855980000,"doc_count":15},{"key_as_string":"2017-02-11T23:34:00.000Z","key":1486856040000,"doc_count":12},{"key_as_string":"2017-02-11T23:35:00.000Z","key":1486856100000,"doc_count":17},{"key_as_string":"2017-02-11T23:36:00.000Z","key":1486856160000,"doc_count":9},{"key_as_string":"2017-02-11T23:37:00.000Z","key":1486856220000,"doc_count":8},{"key_as_string":"2017-02-11T23:38:00.000Z","key":1486856280000,"doc_count":9},{"key_as_string":"2017-02-11T23:39:00.000Z","key":1486856340000,"doc_count":14},{"key_as_string":"2017-02-11T23:40:00.000Z","key":1486856400000,"doc_count":9},{"key_as_string":"2017-02-11T23:41:00.000Z","key":1486856460000,"doc_count":7},{"key_as_string":"2017-02-11T23:42:00.000Z","key":1486856520000,"doc_count":10},{"key_as_string":"2017-02-11T23:43:00.000Z","key":1486856580000,"doc_count":7},{"key_as_string":"2017-02-11T23:44:00.000Z","key":1486856640000,"doc_count":12},{"key_as_string":"2017-02-11T23:45:00.000Z","key":1486856700000,"doc_count":10},{"key_as_string":"2017-02-11T23:46:00.000Z","key":1486856760000,"doc_count":10},{"key_as_string":"2017-02-11T23:47:00.000Z","key":1486856820000,"doc_count":9},{"key_as_string":"2017-02-11T23:48:00.000Z","key":1486856880000,"doc_count":7},{"key_as_string":"2017-02-11T23:49:00.000Z","key":1486856940000,"doc_count":9},{"key_as_string":"2017-02-11T23:50:00.000Z","key":1486857000000,"doc_count":11},{"key_as_string":"2017-02-11T23:51:00.000Z","key":1486857060000,"doc_count":10},{"key_as_string":"2017-02-11T23:52:00.000Z","key":1486857120000,"doc_count":11},{"key_as_string":"2017-02-11T23:53:00.000Z","key":1486857180000,"doc_count":10},{"key_as_string":"2017-02-11T23:54:00.000Z","key":1486857240000,"doc_count":15},{"key_as_string":"2017-02-11T23:55:00.000Z","key":1486857300000,"doc_count":5},{"key_as_string":"2017-02-11T23:56:00.000Z","key":1486857360000,"doc_count":7},{"key_as_string":"2017-02-11T23:57:00.000Z","key":1486857420000,"doc_count":14},{"key_as_string":"2017-02-11T23:58:00.000Z","key":1486857480000,"doc_count":10},{"key_as_string":"2017-02-11T23:59:00.000Z","key":1486857540000,"doc_count":9}]}}} diff --git a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_it_search_response.json b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_it_search_response.json index b771fab4dd352..3f3f016cc7ff5 100644 --- a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_it_search_response.json +++ b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_it_search_response.json @@ -1 +1 @@ -{"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":325,"max_score":0,"hits":[]},"aggregations":{"non_empty_buckets":{"buckets":[{"key_as_string":"1485984480000","key":1485984480000,"doc_count":1},{"key_as_string":"1485993300000","key":1485993300000,"doc_count":1},{"key_as_string":"1486020960000","key":1486020960000,"doc_count":3},{"key_as_string":"1486022820000","key":1486022820000,"doc_count":1},{"key_as_string":"1486032720000","key":1486032720000,"doc_count":1},{"key_as_string":"1486036440000","key":1486036440000,"doc_count":1},{"key_as_string":"1486046040000","key":1486046040000,"doc_count":1},{"key_as_string":"1486059780000","key":1486059780000,"doc_count":1},{"key_as_string":"1486063560000","key":1486063560000,"doc_count":1},{"key_as_string":"1486063860000","key":1486063860000,"doc_count":1},{"key_as_string":"1486125420000","key":1486125420000,"doc_count":1},{"key_as_string":"1486129800000","key":1486129800000,"doc_count":1},{"key_as_string":"1486131000000","key":1486131000000,"doc_count":1},{"key_as_string":"1486138740000","key":1486138740000,"doc_count":4},{"key_as_string":"1486141560000","key":1486141560000,"doc_count":2},{"key_as_string":"1486147080000","key":1486147080000,"doc_count":4},{"key_as_string":"1486155720000","key":1486155720000,"doc_count":1},{"key_as_string":"1486165740000","key":1486165740000,"doc_count":3},{"key_as_string":"1486165800000","key":1486165800000,"doc_count":1},{"key_as_string":"1486165920000","key":1486165920000,"doc_count":4},{"key_as_string":"1486179960000","key":1486179960000,"doc_count":3},{"key_as_string":"1486181460000","key":1486181460000,"doc_count":1},{"key_as_string":"1486204860000","key":1486204860000,"doc_count":1},{"key_as_string":"1486225440000","key":1486225440000,"doc_count":1},{"key_as_string":"1486225680000","key":1486225680000,"doc_count":1},{"key_as_string":"1486250220000","key":1486250220000,"doc_count":1},{"key_as_string":"1486286340000","key":1486286340000,"doc_count":3},{"key_as_string":"1486288620000","key":1486288620000,"doc_count":1},{"key_as_string":"1486313760000","key":1486313760000,"doc_count":1},{"key_as_string":"1486316160000","key":1486316160000,"doc_count":1},{"key_as_string":"1486322520000","key":1486322520000,"doc_count":1},{"key_as_string":"1486377960000","key":1486377960000,"doc_count":1},{"key_as_string":"1486398720000","key":1486398720000,"doc_count":1},{"key_as_string":"1486551360000","key":1486551360000,"doc_count":1},{"key_as_string":"1486554120000","key":1486554120000,"doc_count":1},{"key_as_string":"1486564680000","key":1486564680000,"doc_count":1},{"key_as_string":"1486566900000","key":1486566900000,"doc_count":5},{"key_as_string":"1486566960000","key":1486566960000,"doc_count":8},{"key_as_string":"1486567020000","key":1486567020000,"doc_count":12},{"key_as_string":"1486567080000","key":1486567080000,"doc_count":16},{"key_as_string":"1486567140000","key":1486567140000,"doc_count":12},{"key_as_string":"1486567200000","key":1486567200000,"doc_count":16},{"key_as_string":"1486567260000","key":1486567260000,"doc_count":16},{"key_as_string":"1486567320000","key":1486567320000,"doc_count":12},{"key_as_string":"1486567380000","key":1486567380000,"doc_count":16},{"key_as_string":"1486567440000","key":1486567440000,"doc_count":16},{"key_as_string":"1486567500000","key":1486567500000,"doc_count":12},{"key_as_string":"1486567560000","key":1486567560000,"doc_count":16},{"key_as_string":"1486567620000","key":1486567620000,"doc_count":16},{"key_as_string":"1486567680000","key":1486567680000,"doc_count":12},{"key_as_string":"1486567740000","key":1486567740000,"doc_count":16},{"key_as_string":"1486567800000","key":1486567800000,"doc_count":16},{"key_as_string":"1486567860000","key":1486567860000,"doc_count":8},{"key_as_string":"1486567920000","key":1486567920000,"doc_count":3},{"key_as_string":"1486567980000","key":1486567980000,"doc_count":1},{"key_as_string":"1486589280000","key":1486589280000,"doc_count":1},{"key_as_string":"1486598100000","key":1486598100000,"doc_count":1},{"key_as_string":"1486625760000","key":1486625760000,"doc_count":3},{"key_as_string":"1486627620000","key":1486627620000,"doc_count":1},{"key_as_string":"1486637520000","key":1486637520000,"doc_count":1},{"key_as_string":"1486641240000","key":1486641240000,"doc_count":1},{"key_as_string":"1486650840000","key":1486650840000,"doc_count":1},{"key_as_string":"1486664580000","key":1486664580000,"doc_count":1},{"key_as_string":"1486668360000","key":1486668360000,"doc_count":1},{"key_as_string":"1486668660000","key":1486668660000,"doc_count":1},{"key_as_string":"1486730220000","key":1486730220000,"doc_count":1},{"key_as_string":"1486734600000","key":1486734600000,"doc_count":1},{"key_as_string":"1486735800000","key":1486735800000,"doc_count":1},{"key_as_string":"1486743540000","key":1486743540000,"doc_count":4},{"key_as_string":"1486746360000","key":1486746360000,"doc_count":2},{"key_as_string":"1486751880000","key":1486751880000,"doc_count":4},{"key_as_string":"1486760520000","key":1486760520000,"doc_count":1},{"key_as_string":"1486770540000","key":1486770540000,"doc_count":3},{"key_as_string":"1486770600000","key":1486770600000,"doc_count":1},{"key_as_string":"1486770720000","key":1486770720000,"doc_count":4},{"key_as_string":"1486784760000","key":1486784760000,"doc_count":3},{"key_as_string":"1486786260000","key":1486786260000,"doc_count":1},{"key_as_string":"1486809660000","key":1486809660000,"doc_count":1},{"key_as_string":"1486830240000","key":1486830240000,"doc_count":1},{"key_as_string":"1486830480000","key":1486830480000,"doc_count":1},{"key_as_string":"1486855020000","key":1486855020000,"doc_count":1}]}}} +{"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total": { "value": 325, "relation": "eq" },"max_score":0,"hits":[]},"aggregations":{"non_empty_buckets":{"buckets":[{"key_as_string":"1485984480000","key":1485984480000,"doc_count":1},{"key_as_string":"1485993300000","key":1485993300000,"doc_count":1},{"key_as_string":"1486020960000","key":1486020960000,"doc_count":3},{"key_as_string":"1486022820000","key":1486022820000,"doc_count":1},{"key_as_string":"1486032720000","key":1486032720000,"doc_count":1},{"key_as_string":"1486036440000","key":1486036440000,"doc_count":1},{"key_as_string":"1486046040000","key":1486046040000,"doc_count":1},{"key_as_string":"1486059780000","key":1486059780000,"doc_count":1},{"key_as_string":"1486063560000","key":1486063560000,"doc_count":1},{"key_as_string":"1486063860000","key":1486063860000,"doc_count":1},{"key_as_string":"1486125420000","key":1486125420000,"doc_count":1},{"key_as_string":"1486129800000","key":1486129800000,"doc_count":1},{"key_as_string":"1486131000000","key":1486131000000,"doc_count":1},{"key_as_string":"1486138740000","key":1486138740000,"doc_count":4},{"key_as_string":"1486141560000","key":1486141560000,"doc_count":2},{"key_as_string":"1486147080000","key":1486147080000,"doc_count":4},{"key_as_string":"1486155720000","key":1486155720000,"doc_count":1},{"key_as_string":"1486165740000","key":1486165740000,"doc_count":3},{"key_as_string":"1486165800000","key":1486165800000,"doc_count":1},{"key_as_string":"1486165920000","key":1486165920000,"doc_count":4},{"key_as_string":"1486179960000","key":1486179960000,"doc_count":3},{"key_as_string":"1486181460000","key":1486181460000,"doc_count":1},{"key_as_string":"1486204860000","key":1486204860000,"doc_count":1},{"key_as_string":"1486225440000","key":1486225440000,"doc_count":1},{"key_as_string":"1486225680000","key":1486225680000,"doc_count":1},{"key_as_string":"1486250220000","key":1486250220000,"doc_count":1},{"key_as_string":"1486286340000","key":1486286340000,"doc_count":3},{"key_as_string":"1486288620000","key":1486288620000,"doc_count":1},{"key_as_string":"1486313760000","key":1486313760000,"doc_count":1},{"key_as_string":"1486316160000","key":1486316160000,"doc_count":1},{"key_as_string":"1486322520000","key":1486322520000,"doc_count":1},{"key_as_string":"1486377960000","key":1486377960000,"doc_count":1},{"key_as_string":"1486398720000","key":1486398720000,"doc_count":1},{"key_as_string":"1486551360000","key":1486551360000,"doc_count":1},{"key_as_string":"1486554120000","key":1486554120000,"doc_count":1},{"key_as_string":"1486564680000","key":1486564680000,"doc_count":1},{"key_as_string":"1486566900000","key":1486566900000,"doc_count":5},{"key_as_string":"1486566960000","key":1486566960000,"doc_count":8},{"key_as_string":"1486567020000","key":1486567020000,"doc_count":12},{"key_as_string":"1486567080000","key":1486567080000,"doc_count":16},{"key_as_string":"1486567140000","key":1486567140000,"doc_count":12},{"key_as_string":"1486567200000","key":1486567200000,"doc_count":16},{"key_as_string":"1486567260000","key":1486567260000,"doc_count":16},{"key_as_string":"1486567320000","key":1486567320000,"doc_count":12},{"key_as_string":"1486567380000","key":1486567380000,"doc_count":16},{"key_as_string":"1486567440000","key":1486567440000,"doc_count":16},{"key_as_string":"1486567500000","key":1486567500000,"doc_count":12},{"key_as_string":"1486567560000","key":1486567560000,"doc_count":16},{"key_as_string":"1486567620000","key":1486567620000,"doc_count":16},{"key_as_string":"1486567680000","key":1486567680000,"doc_count":12},{"key_as_string":"1486567740000","key":1486567740000,"doc_count":16},{"key_as_string":"1486567800000","key":1486567800000,"doc_count":16},{"key_as_string":"1486567860000","key":1486567860000,"doc_count":8},{"key_as_string":"1486567920000","key":1486567920000,"doc_count":3},{"key_as_string":"1486567980000","key":1486567980000,"doc_count":1},{"key_as_string":"1486589280000","key":1486589280000,"doc_count":1},{"key_as_string":"1486598100000","key":1486598100000,"doc_count":1},{"key_as_string":"1486625760000","key":1486625760000,"doc_count":3},{"key_as_string":"1486627620000","key":1486627620000,"doc_count":1},{"key_as_string":"1486637520000","key":1486637520000,"doc_count":1},{"key_as_string":"1486641240000","key":1486641240000,"doc_count":1},{"key_as_string":"1486650840000","key":1486650840000,"doc_count":1},{"key_as_string":"1486664580000","key":1486664580000,"doc_count":1},{"key_as_string":"1486668360000","key":1486668360000,"doc_count":1},{"key_as_string":"1486668660000","key":1486668660000,"doc_count":1},{"key_as_string":"1486730220000","key":1486730220000,"doc_count":1},{"key_as_string":"1486734600000","key":1486734600000,"doc_count":1},{"key_as_string":"1486735800000","key":1486735800000,"doc_count":1},{"key_as_string":"1486743540000","key":1486743540000,"doc_count":4},{"key_as_string":"1486746360000","key":1486746360000,"doc_count":2},{"key_as_string":"1486751880000","key":1486751880000,"doc_count":4},{"key_as_string":"1486760520000","key":1486760520000,"doc_count":1},{"key_as_string":"1486770540000","key":1486770540000,"doc_count":3},{"key_as_string":"1486770600000","key":1486770600000,"doc_count":1},{"key_as_string":"1486770720000","key":1486770720000,"doc_count":4},{"key_as_string":"1486784760000","key":1486784760000,"doc_count":3},{"key_as_string":"1486786260000","key":1486786260000,"doc_count":1},{"key_as_string":"1486809660000","key":1486809660000,"doc_count":1},{"key_as_string":"1486830240000","key":1486830240000,"doc_count":1},{"key_as_string":"1486830480000","key":1486830480000,"doc_count":1},{"key_as_string":"1486855020000","key":1486855020000,"doc_count":1}]}}} diff --git a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_time_range.json b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_time_range.json index 6eb1808ea7d0c..1a22fc869f354 100644 --- a/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_time_range.json +++ b/x-pack/plugins/ml/server/models/job_validation/__mocks__/mock_time_range.json @@ -1 +1 @@ -{"took":0,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":32017,"max_score":0,"hits":[]},"aggregations":{"earliest":{"value":-252464400000,"value_as_string":"1961-12-31T23:00:00.000Z"},"latest":{"value":1419894000000,"value_as_string":"2014-12-29T23:00:00.000Z"}}} +{"took":0,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":{"value":32017,"relation":"eq"},"max_score":0,"hits":[]},"aggregations":{"earliest":{"value":-252464400000,"value_as_string":"1961-12-31T23:00:00.000Z"},"latest":{"value":1419894000000,"value_as_string":"2014-12-29T23:00:00.000Z"}}} diff --git a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts index 810d0ae9dcd87..691b039c0ad37 100644 --- a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts @@ -22,7 +22,7 @@ const callAs = { }, }), }, - search: () => Promise.resolve({ body: {} }), + search: () => Promise.resolve({ body: { hits: { total: { value: 0, relation: 'eq' } } } }), }; const mlClusterClient = ({ diff --git a/x-pack/plugins/ml/server/models/job_validation/validate_cardinality.test.ts b/x-pack/plugins/ml/server/models/job_validation/validate_cardinality.test.ts index 1be0751e15f22..2e2a9e21aa959 100644 --- a/x-pack/plugins/ml/server/models/job_validation/validate_cardinality.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/validate_cardinality.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; +import { cloneDeep } from 'lodash'; import { IScopedClusterClient } from 'kibana/server'; diff --git a/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts b/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts index 12458af0521a9..ddf73166e1858 100644 --- a/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/validate_time_range.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import cloneDeep from 'lodash/cloneDeep'; +import { cloneDeep } from 'lodash'; import { IScopedClusterClient } from 'kibana/server'; diff --git a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js index 588e0e10a8d63..65820b5281338 100644 --- a/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js +++ b/x-pack/plugins/ml/server/models/results_service/build_anomaly_table_items.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import sortBy from 'lodash/sortBy'; -import each from 'lodash/each'; +import { sortBy, each } from 'lodash'; import moment from 'moment-timezone'; import { diff --git a/x-pack/plugins/ml/server/models/results_service/results_service.ts b/x-pack/plugins/ml/server/models/results_service/results_service.ts index 190b5d99309d7..abb42a5a18689 100644 --- a/x-pack/plugins/ml/server/models/results_service/results_service.ts +++ b/x-pack/plugins/ml/server/models/results_service/results_service.ts @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import sortBy from 'lodash/sortBy'; -import slice from 'lodash/slice'; -import get from 'lodash/get'; +import { sortBy, slice, get } from 'lodash'; import moment from 'moment'; import { SearchResponse } from 'elasticsearch'; import { IScopedClusterClient } from 'kibana/server'; @@ -144,9 +142,8 @@ export function resultsServiceProvider(client: IScopedClusterClient) { }); } - const { body } = await asInternalUser.search>({ + const { body } = await asInternalUser.search({ index: ML_RESULTS_INDEX_PATTERN, - rest_total_hits_as_int: true, size: maxRecords, body: { query: { @@ -178,9 +175,9 @@ export function resultsServiceProvider(client: IScopedClusterClient) { anomalies: [], interval: 'second', }; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { let records: AnomalyRecordDoc[] = []; - body.hits.hits.forEach((hit) => { + body.hits.hits.forEach((hit: any) => { records.push(hit._source); }); @@ -382,7 +379,6 @@ export function resultsServiceProvider(client: IScopedClusterClient) { async function getCategoryExamples(jobId: string, categoryIds: any, maxExamples: number) { const { body } = await asInternalUser.search({ index: ML_RESULTS_INDEX_PATTERN, - rest_total_hits_as_int: true, size: ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, // Matches size of records in anomaly summary table. body: { query: { @@ -394,7 +390,7 @@ export function resultsServiceProvider(client: IScopedClusterClient) { }); const examplesByCategoryId: { [key: string]: any } = {}; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { body.hits.hits.forEach((hit: any) => { if (maxExamples) { examplesByCategoryId[hit._source.category_id] = slice( @@ -417,7 +413,6 @@ export function resultsServiceProvider(client: IScopedClusterClient) { async function getCategoryDefinition(jobId: string, categoryId: string) { const { body } = await asInternalUser.search({ index: ML_RESULTS_INDEX_PATTERN, - rest_total_hits_as_int: true, size: 1, body: { query: { @@ -429,7 +424,7 @@ export function resultsServiceProvider(client: IScopedClusterClient) { }); const definition = { categoryId, terms: null, regex: null, examples: [] }; - if (body.hits.total !== 0) { + if (body.hits.total.value > 0) { const source = body.hits.hits[0]._source; definition.categoryId = source.category_id; definition.regex = source.regex; diff --git a/x-pack/plugins/ml/server/shared.ts b/x-pack/plugins/ml/server/shared.ts index 100433b23f7d1..68ee489a3b5ce 100644 --- a/x-pack/plugins/ml/server/shared.ts +++ b/x-pack/plugins/ml/server/shared.ts @@ -7,5 +7,5 @@ export * from '../common/types/anomalies'; export * from '../common/types/anomaly_detection_jobs'; export * from './lib/capabilities/errors'; -export { ModuleSetupPayload } from './shared_services/providers/modules'; +export type { ModuleSetupPayload } from './shared_services/providers/modules'; export { getHistogramsForFields } from './models/data_visualizer/'; diff --git a/x-pack/plugins/ml/shared_imports.ts b/x-pack/plugins/ml/shared_imports.ts index a82ed5387818d..33669a082f7f0 100644 --- a/x-pack/plugins/ml/shared_imports.ts +++ b/x-pack/plugins/ml/shared_imports.ts @@ -3,9 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { XJson } from '../../../src/plugins/es_ui_shared/public'; +const { collapseLiteralStrings, expandLiteralStrings } = XJson; -export { - XJsonMode, - collapseLiteralStrings, - expandLiteralStrings, -} from '../../../src/plugins/es_ui_shared/public'; +export { XJsonMode } from '@kbn/ace'; +export { collapseLiteralStrings, expandLiteralStrings }; diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts index 2c714080969e4..8be0eb0b06823 100644 --- a/x-pack/plugins/monitoring/common/constants.ts +++ b/x-pack/plugins/monitoring/common/constants.ts @@ -273,3 +273,10 @@ export const ALERT_ACTION_TYPE_EMAIL = '.email'; export const ALERT_ACTION_TYPE_LOG = '.server-log'; export const ALERT_EMAIL_SERVICES = ['gmail', 'hotmail', 'icloud', 'outlook365', 'ses', 'yahoo']; + +/** + * The saved object type for various monitoring data + */ +export const SAVED_OBJECT_TELEMETRY = 'monitoring-telemetry'; + +export const TELEMETRY_METRIC_BUTTON_CLICK = 'btnclick__'; diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index 2b8756ea0cb46..8b0b0b7aae693 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -12,7 +12,8 @@ "triggers_actions_ui", "alerts", "actions", - "encryptedSavedObjects" + "encryptedSavedObjects", + "observability" ], "optionalPlugins": ["infra", "telemetryCollectionManager", "usageCollection", "home", "cloud"], "server": true, diff --git a/x-pack/plugins/monitoring/public/angular/app_modules.ts b/x-pack/plugins/monitoring/public/angular/app_modules.ts index 499610045d771..4ef905fd35fc4 100644 --- a/x-pack/plugins/monitoring/public/angular/app_modules.ts +++ b/x-pack/plugins/monitoring/public/angular/app_modules.ts @@ -41,10 +41,6 @@ import { licenseProvider } from '../services/license'; // @ts-ignore import { titleProvider } from '../services/title'; // @ts-ignore -import { monitoringBeatsBeatProvider } from '../directives/beats/beat'; -// @ts-ignore -import { monitoringBeatsOverviewProvider } from '../directives/beats/overview'; -// @ts-ignore import { monitoringMlListingProvider } from '../directives/elasticsearch/ml_job_listing'; // @ts-ignore import { monitoringMainProvider } from '../directives/main'; @@ -153,8 +149,6 @@ function createMonitoringAppServices() { function createMonitoringAppDirectives() { angular .module('monitoring/directives', []) - .directive('monitoringBeatsBeat', monitoringBeatsBeatProvider) - .directive('monitoringBeatsOverview', monitoringBeatsOverviewProvider) .directive('monitoringMlListing', monitoringMlListingProvider) .directive('monitoringMain', monitoringMainProvider); } diff --git a/x-pack/plugins/monitoring/public/angular/index.ts b/x-pack/plugins/monitoring/public/angular/index.ts index da57c028643a5..3c30d3c358a14 100644 --- a/x-pack/plugins/monitoring/public/angular/index.ts +++ b/x-pack/plugins/monitoring/public/angular/index.ts @@ -26,6 +26,7 @@ export class AngularApp { pluginInitializerContext, externalConfig, triggersActionsUi, + usageCollection, kibanaLegacy, } = deps; const app: IModule = localAppModule(deps); @@ -42,6 +43,7 @@ export class AngularApp { externalConfig, kibanaLegacy, triggersActionsUi, + usageCollection, }, this.injector ); diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js index 396d2258edd0c..eec24e741ac41 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js @@ -42,9 +42,7 @@ export function ApmServerInstance({ summary, metrics, ...props }) { const charts = seriesToShow.map((data, index) => ( - - - + )); @@ -55,15 +53,15 @@ export function ApmServerInstance({ summary, metrics, ...props }) {

+ + + + - - - - {charts} diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js index 6dcfa6dd043aa..e05ba1878caed 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js +++ b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js @@ -156,11 +156,11 @@ export function ApmServerInstances({ apms, setupMode }) { /> + + + + - - - - {setupModeCallout} ( - - - + )); @@ -51,15 +49,15 @@ export function ApmOverview({ stats, metrics, ...props }) {

+ + + + - - - - {charts} diff --git a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js index 3fe211c0f2edc..f489271659bfe 100644 --- a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js +++ b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js @@ -135,6 +135,9 @@ export function Beat({ summary, metrics, ...props }) { + + + diff --git a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js index be8595e8e6bbe..60a35e00a4c63 100644 --- a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js @@ -13,6 +13,7 @@ import { EuiSpacer, EuiLink, EuiScreenReaderOnly, + EuiPanel, } from '@elastic/eui'; import { Stats } from '../../beats'; import { formatMetric } from '../../../lib/format_number'; @@ -153,9 +154,11 @@ export class Listing extends PureComponent { /> - + - + + + {setupModeCallOut} - + + + + - - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- +
+ + @@ -212,18 +213,25 @@ exports[`Overview that overview page shows a message if there is no beats data 1 /> - + + + + - + + + diff --git a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js index 83f92ea1b481c..897f017f44f41 100644 --- a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js +++ b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js @@ -30,46 +30,40 @@ function renderLatestActive(latestActive, latestTypes, latestVersions) { return ( - - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
- - -

- -

-
- - -
+ +

+ +

+
+ +
); @@ -118,10 +112,13 @@ export function BeatsOverview({ /> - + - {renderLatestActive(latestActive, latestTypes, latestVersions)} - + + + {renderLatestActive(latestActive, latestTypes, latestVersions)} + + {charts}
diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index ccbf0b0ec711d..4bf07710393ea 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -55,7 +55,7 @@ export function ApmPanel(props) { {...props} url="apm" title={i18n.translate('xpack.monitoring.cluster.overview.apmPanel.apmTitle', { - defaultMessage: 'APM', + defaultMessage: 'APM server', })} > @@ -70,21 +70,21 @@ export function ApmPanel(props) { aria-label={i18n.translate( 'xpack.monitoring.cluster.overview.apmPanel.overviewLinkAriaLabel', { - defaultMessage: 'APM Overview', + defaultMessage: 'APM server overview', } )} data-test-subj="apmOverview" > - + {formatMetric(props.totalEvents, '0.[0]a')} - + {apmsTotal} }} /> @@ -144,7 +144,7 @@ export function ApmPanel(props) { - + - + {formatMetric(props.totalEvents, '0.[0]a')} - + {props.logs.types.map((log, index) => ( - + - + @@ -276,7 +277,7 @@ export function ElasticsearchPanel(props) { - + - + - + - + {showMlJobs()} - + - + - + - + - + {formatNumber(get(indices, 'docs.count'), 'int_commas')} - + - + - + - + diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js index 6fa533302db48..7df0a3ca7138e 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js @@ -111,7 +111,7 @@ export function KibanaPanel(props) { data-test-subj="kibana_overview" data-overview-status={props.status} > - + {props.requests_total} - + - + {formatNumber(props.concurrent_connections, 'int_commas')} - + - + {formatNumber(props.events_in_total, '0.[0]a')} - + - + {props.max_uptime ? formatNumber(props.max_uptime, 'time_since') : 0} - + - + {queueTypes[LOGSTASH.QUEUE_TYPES.MEMORY] || 0} - + } checked={showSystemIndices} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap index c7081dc439085..b0b5ceb46d16c 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap @@ -10,30 +10,46 @@ exports[`Node Listing Metric Cell should format N/A as the metric for an offline exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = `
+
- - 206.3 GB  - - -
- 206.5 GB max -
- 206.3 GB min +
+
+
+
+
+
+
+
+
+ 206.3 GB +
+
@@ -41,30 +57,46 @@ exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = ` exports[`Node Listing Metric Cell should format a percentage metric 1`] = `
+
- - 0%  - - -
- 2% max -
- 0% min +
+
+
+
+
+
+
+
+
+ 0% +
+
diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js index 0c4b4b2b3c3f4..f0b131b65433c 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js @@ -27,6 +27,7 @@ describe('Node Listing Metric Cell', () => { }, summary: { minVal: 0, maxVal: 2, lastVal: 0, slope: -1 }, }, + 'data-test-subj': 'testCell', }; expect(renderWithIntl()).toMatchSnapshot(); }); @@ -54,6 +55,7 @@ describe('Node Listing Metric Cell', () => { slope: -1, }, }, + 'data-test-subj': 'testCell2', }; expect(renderWithIntl()).toMatchSnapshot(); }); diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js index 4c3b642213d99..9956dd4da7d8a 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js @@ -4,19 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useState } from 'react'; import { get } from 'lodash'; import { formatMetric } from '../../../lib/format_number'; -import { EuiText, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiText, + EuiPopover, + EuiIcon, + EuiDescriptionList, + EuiSpacer, + EuiKeyboardAccessible, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +const TRENDING_DOWN = i18n.translate('xpack.monitoring.elasticsearch.node.cells.trendingDownText', { + defaultMessage: 'down', +}); +const TRENDING_UP = i18n.translate('xpack.monitoring.elasticsearch.node.cells.trendingUpText', { + defaultMessage: 'up', +}); + function OfflineCell() { return
N/A
; } -const getSlopeArrow = (slope) => { +const getDirection = (slope) => { + if (slope || slope === 0) { + return slope > 0 ? TRENDING_UP : TRENDING_DOWN; + } + return null; +}; + +const getIcon = (slope) => { if (slope || slope === 0) { - return slope > 0 ? 'up' : 'down'; + return slope > 0 ? 'arrowUp' : 'arrowDown'; } return null; }; @@ -28,40 +51,82 @@ const metricVal = (metric, format, isPercent, units) => { return formatMetric(metric, format, units); }; -const noWrapStyle = { overflowX: 'hidden', whiteSpace: 'nowrap' }; - function MetricCell({ isOnline, metric = {}, isPercent, ...props }) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onButtonClick = () => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen); + const closePopover = () => setIsPopoverOpen(false); + if (isOnline) { const { lastVal, maxVal, minVal, slope } = get(metric, 'summary', {}); const format = get(metric, 'metric.format'); const units = get(metric, 'metric.units'); + const tooltipItems = [ + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.trending', { + defaultMessage: 'Trending', + }), + description: getDirection(slope), + }, + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.max', { + defaultMessage: 'Max value', + }), + description: metricVal(maxVal, format, isPercent, units), + }, + { + title: i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.min', { + defaultMessage: 'Min value', + }), + description: metricVal(minVal, format, isPercent, units), + }, + ]; + + const button = ( + + + + ); + return ( - + + - - - {metricVal(lastVal, format, isPercent)} -   - - - - - {i18n.translate('xpack.monitoring.elasticsearch.nodes.cells.maxText', { - defaultMessage: '{metric} max', - values: { - metric: metricVal(maxVal, format, isPercent, units), - }, - })} - - - {i18n.translate('xpack.monitoring.elasticsearch.nodes.cells.minText', { - defaultMessage: '{metric} min', - values: { - metric: metricVal(minVal, format, isPercent, units), - }, - })} - + + + +
+ + + + {i18n.translate('xpack.monitoring.elasticsearch.node.cells.tooltip.preface', { + defaultMessage: 'Applies to current time period', + })} + +
+
+
+ + {metricVal(lastVal, format, isPercent)} + +
); diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index 43512f8e528f6..f088f7c0d348a 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -73,7 +73,6 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.nameColumnTitle', { defaultMessage: 'Name', }), - width: '20%', field: 'name', sortable: true, render: (value, node) => { @@ -131,7 +130,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler defaultMessage: 'Alerts', }), field: 'alerts', - width: '175px', + // width: '175px', sortable: true, render: (_field, node) => { return ( @@ -148,6 +147,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.statusColumnTitle', { defaultMessage: 'Status', }), + dataType: 'boolean', field: 'isOnline', sortable: true, render: (value) => { @@ -181,22 +181,18 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.shardsColumnTitle', { defaultMessage: 'Shards', }), + dataType: 'number', field: 'shardCount', sortable: true, render: (value, node) => { - return node.isOnline ? ( -
- {value} -
- ) : ( - - ); + return node.isOnline ? {value} : ; }, }); if (showCgroupMetricsElasticsearch) { cols.push({ name: cpuUsageColumnTitle, + dataType: 'number', field: 'node_cgroup_quota', sortable: getSortHandler('node_cgroup_quota'), render: (value, node) => ( @@ -213,6 +209,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.cpuThrottlingColumnTitle', { defaultMessage: 'CPU Throttling', }), + dataType: 'number', field: 'node_cgroup_throttled', sortable: getSortHandler('node_cgroup_throttled'), render: (value, node) => ( @@ -227,6 +224,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler } else { cols.push({ name: cpuUsageColumnTitle, + dataType: 'number', field: 'node_cpu_utilization', sortable: getSortHandler('node_cpu_utilization'), render: (value, node) => { @@ -245,6 +243,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.loadAverageColumnTitle', { defaultMessage: 'Load Average', }), + dataType: 'number', field: 'node_load_average', sortable: getSortHandler('node_load_average'), render: (value, node) => ( @@ -265,6 +264,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler javaVirtualMachine: 'JVM', }, }), + dataType: 'number', field: 'node_jvm_mem_percent', sortable: getSortHandler('node_jvm_mem_percent'), render: (value, node) => ( @@ -281,6 +281,7 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler name: i18n.translate('xpack.monitoring.elasticsearch.nodes.diskFreeSpaceColumnTitle', { defaultMessage: 'Disk Free Space', }), + dataType: 'number', field: 'node_free_space', sortable: getSortHandler('node_free_space'), render: (value, node) => ( diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js index fd5f28ea02039..3c875667fe04c 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js @@ -5,6 +5,7 @@ */ import React from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -37,7 +38,12 @@ class IndexLabel extends React.Component { @@ -45,3 +46,18 @@ export function PageLoading() { ); } + +function PageLoadingTracking({ pageViewTitle }) { + const path = pageViewTitle.toLowerCase().replace(/-/g, '').replace(/\s+/g, '_'); + useTrackPageview({ app: 'stack_monitoring', path }); + useTrackPageview({ app: 'stack_monitoring', path, delay: 15000 }); + return ; +} + +export function PageLoading({ pageViewTitle }) { + if (pageViewTitle) { + return ; + } + + return ; +} diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss index a5ab07618f267..e19b4fa760641 100644 --- a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss +++ b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.scss @@ -1,6 +1,3 @@ .monSetupModeEnterButton__buttonWrapper { - position: absolute; - top: $euiSize; - left: $euiSizeM; - z-index: 1; + padding: $euiSizeM; } diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx index e06113255c1ef..b47b51e664f5f 100644 --- a/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx +++ b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx @@ -8,6 +8,8 @@ import React from 'react'; import { EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import './enter_button.scss'; +import { METRIC_TYPE, useUiTracker } from '../../../../observability/public'; +import { TELEMETRY_METRIC_BUTTON_CLICK } from '../../../common/constants'; export interface SetupModeEnterButtonProps { enabled: boolean; @@ -18,6 +20,7 @@ export const SetupModeEnterButton: React.FC = ( props: SetupModeEnterButtonProps ) => { const [isLoading, setIsLoading] = React.useState(false); + const trackStat = useUiTracker({ app: 'stack_monitoring' }); if (!props.enabled) { return null; @@ -26,6 +29,10 @@ export const SetupModeEnterButton: React.FC = ( async function enterSetupMode() { setIsLoading(true); await props.toggleSetupMode(true); + trackStat({ + metric: `${TELEMETRY_METRIC_BUTTON_CLICK}setupmode_enter`, + metricType: METRIC_TYPE.CLICK, + }); setIsLoading(false); } diff --git a/x-pack/plugins/monitoring/public/directives/beats/beat/index.js b/x-pack/plugins/monitoring/public/directives/beats/beat/index.js deleted file mode 100644 index 103cac98ba564..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/beats/beat/index.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { Beat } from '../../../components/beats/beat'; - -//monitoringBeatsBeat -export function monitoringBeatsBeatProvider() { - return { - restrict: 'E', - scope: { - data: '=', - onBrush: '<', - zoomInfo: '<', - }, - link(scope, $el) { - scope.$on('$destroy', () => $el && $el[0] && unmountComponentAtNode($el[0])); - - scope.$watch('data', (data = {}) => { - render( - , - $el[0] - ); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/beats/overview/index.js b/x-pack/plugins/monitoring/public/directives/beats/overview/index.js deleted file mode 100644 index 4faf69e13d02c..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/beats/overview/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { BeatsOverview } from '../../../components/beats/overview'; - -export function monitoringBeatsOverviewProvider() { - return { - restrict: 'E', - scope: { - data: '=', - onBrush: '<', - zoomInfo: '<', - }, - link(scope, $el) { - scope.$on('$destroy', () => $el && $el[0] && unmountComponentAtNode($el[0])); - - scope.$watch('data', (data = {}) => { - render( - , - $el[0] - ); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index fabd207d72b1f..fb24d9e678d56 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -1,19 +1,32 @@
-
- - +
+
+
+
+
+
+

{{pageTitle || monitoringMain.instance}}

+
+
+
+
+
+ + +
+
diff --git a/x-pack/plugins/reporting/public/components/report_listing.tsx b/x-pack/plugins/reporting/public/components/report_listing.tsx index f326d365351f2..a512b1305b8e0 100644 --- a/x-pack/plugins/reporting/public/components/report_listing.tsx +++ b/x-pack/plugins/reporting/public/components/report_listing.tsx @@ -41,17 +41,17 @@ export interface Job { type: string; object_type: string; object_title: string; - created_by?: string; + created_by?: string | false; created_at: string; started_at?: string; completed_at?: string; status: string; statusLabel: string; - max_size_reached: boolean; + max_size_reached?: boolean; attempts: number; max_attempts: number; csv_contains_formulas: boolean; - warnings: string[]; + warnings?: string[]; } export interface Props { @@ -154,7 +154,7 @@ class ReportListingUi extends Component {

diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index eddf151167be8..22b97f45db186 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -7,10 +7,11 @@ import { EuiButton, EuiCopy, EuiForm, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; -import url from 'url'; import { ToastsSetup } from 'src/core/public'; -import { ReportingAPIClient } from '../lib/reporting_api_client'; +import url from 'url'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; +import { BaseParams } from '../../common/types'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { apiClient: ReportingAPIClient; @@ -19,7 +20,7 @@ interface Props { layoutId: string | undefined; objectId?: string; objectType: string; - getJobParams: () => any; + getJobParams: () => BaseParams; options?: ReactElement; isDirty: boolean; onClose: () => void; diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx index 9fb74a70ff1ac..4a62ab2b76508 100644 --- a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiSpacer, EuiSwitch } from '@elastic/eui'; +import { EuiSpacer, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; import { ToastsSetup } from 'src/core/public'; -import { ReportingPanelContent } from './reporting_panel_content'; +import { BaseParams } from '../../common/types'; import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { ReportingPanelContent } from './reporting_panel_content'; interface Props { apiClient: ReportingAPIClient; @@ -17,7 +18,7 @@ interface Props { reportType: string; objectId?: string; objectType: string; - getJobParams: () => any; + getJobParams: () => BaseParams; isDirty: boolean; onClose: () => void; } @@ -83,7 +84,7 @@ export class ScreenCapturePanelContent extends Component { ); }; - private handlePrintLayoutChange = (evt: any) => { + private handlePrintLayoutChange = (evt: EuiSwitchEvent) => { this.setState({ usePrintLayout: evt.target.checked }); }; diff --git a/x-pack/plugins/reporting/public/index.ts b/x-pack/plugins/reporting/public/index.ts index 185367a85bdc0..251fd14ee4d57 100644 --- a/x-pack/plugins/reporting/public/index.ts +++ b/x-pack/plugins/reporting/public/index.ts @@ -7,6 +7,7 @@ import { PluginInitializerContext } from 'src/core/public'; import { ReportingPublicPlugin } from './plugin'; import * as jobCompletionNotifications from './lib/job_completion_notifications'; +import { JobId, JobStatus } from '../common/types'; export function plugin(initializerContext: PluginInitializerContext) { return new ReportingPublicPlugin(initializerContext); @@ -14,3 +15,23 @@ export function plugin(initializerContext: PluginInitializerContext) { export { ReportingPublicPlugin as Plugin }; export { jobCompletionNotifications }; + +export interface JobSummary { + id: JobId; + status: JobStatus; + title: string; + jobtype: string; + maxSizeReached?: boolean; + csvContainsFormulas?: boolean; +} + +export interface JobSummarySet { + completed: JobSummary[]; + failed: JobSummary[]; +} + +type DownloadLink = string; +export type DownloadReportFn = (jobId: JobId) => DownloadLink; + +type ManagementLink = string; +export type ManagementLinkFn = () => ManagementLink; diff --git a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap index 6b95a00ea0009..f1d9d747a7236 100644 --- a/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap +++ b/x-pack/plugins/reporting/public/lib/__snapshots__/stream_handler.test.ts.snap @@ -6,20 +6,20 @@ Object { Object { "csvContainsFormulas": false, "id": "job-source-mock1", + "jobtype": undefined, "maxSizeReached": false, "status": "completed", "title": "specimen", - "type": "spectacular", }, ], "failed": Array [ Object { "csvContainsFormulas": false, "id": "job-source-mock2", + "jobtype": undefined, "maxSizeReached": false, "status": "failed", "title": "specimen", - "type": "spectacular", }, ], } @@ -49,9 +49,9 @@ Array [ Object { "csvContainsFormulas": true, "id": "yas3", + "jobtype": "yas", "status": "completed", "title": "Yas", - "type": "yas", } } /> @@ -149,10 +149,10 @@ Array [ job={ Object { "id": "yas2", + "jobtype": "yas", "maxSizeReached": true, "status": "completed", "title": "Yas", - "type": "yas", } } /> @@ -191,9 +191,9 @@ Array [ job={ Object { "id": "yas1", + "jobtype": "yas", "status": "completed", "title": "Yas", - "type": "yas", } } /> diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts index 2f813bd811c6c..2853caaaaa1b5 100644 --- a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts @@ -7,10 +7,11 @@ import { stringify } from 'query-string'; import rison from 'rison-node'; import { HttpSetup } from 'src/core/public'; -import { JobId, SourceJob } from '../../common/types'; +import { DownloadReportFn, ManagementLinkFn } from '../'; +import { JobId, ReportApiJSON, ReportDocument, ReportSource } from '../../common/types'; import { - API_BASE_URL, API_BASE_GENERATE, + API_BASE_URL, API_LIST_URL, REPORTING_MANAGEMENT_HOME, } from '../../constants'; @@ -18,7 +19,7 @@ import { add } from './job_completion_notifications'; export interface JobQueueEntry { _id: string; - _source: any; + _source: ReportSource; } export interface JobContent { @@ -26,40 +27,6 @@ export interface JobContent { content_type: boolean; } -export interface JobInfo { - kibana_name: string; - kibana_id: string; - browser_type: string; - created_at: string; - priority: number; - jobtype: string; - created_by: string; - timeout: number; - output: { - content_type: string; - size: number; - warnings: string[]; - }; - process_expiration: string; - completed_at: string; - payload: { - layout: { id: string; dimensions: { width: number; height: number } }; - objects: Array<{ relativeUrl: string }>; - type: string; - title: string; - forceNow: string; - browserTimezone: string; - }; - meta: { - layout: string; - objectType: string; - }; - max_attempts: number; - started_at: string; - attempts: number; - status: string; -} - interface JobParams { [paramName: string]: any; } @@ -121,13 +88,13 @@ export class ReportingAPIClient { }); } - public getInfo(jobId: string): Promise { + public getInfo(jobId: string): Promise { return this.http.get(`${API_LIST_URL}/info/${jobId}`, { asSystemRequest: true, }); } - public findForJobIds = (jobIds: JobId[]): Promise => { + public findForJobIds = (jobIds: JobId[]): Promise => { return this.http.fetch(`${API_LIST_URL}/list`, { query: { page: 0, ids: jobIds.join(',') }, method: 'GET', @@ -159,9 +126,10 @@ export class ReportingAPIClient { return resp; }; - public getManagementLink = () => this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); + public getManagementLink: ManagementLinkFn = () => + this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); - public getDownloadLink = (jobId: JobId) => + public getDownloadLink: DownloadReportFn = (jobId: JobId) => this.http.basePath.prepend(`${API_LIST_URL}/download/${jobId}`); /* diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index 998f0711b1355..f91517e4397f9 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -6,7 +6,8 @@ import sinon, { stub } from 'sinon'; import { NotificationsStart } from 'src/core/public'; -import { JobSummary, SourceJob } from '../../common/types'; +import { JobSummary } from '../'; +import { ReportDocument } from '../../common/types'; import { ReportingAPIClient } from './reporting_api_client'; import { ReportingNotifierStreamHandler } from './stream_handler'; @@ -23,7 +24,7 @@ const mockJobsFound = [ _source: { status: 'completed', output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { type: 'spectacular', title: 'specimen' }, + payload: { title: 'specimen' }, }, }, { @@ -31,7 +32,7 @@ const mockJobsFound = [ _source: { status: 'failed', output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { type: 'spectacular', title: 'specimen' }, + payload: { title: 'specimen' }, }, }, { @@ -39,14 +40,14 @@ const mockJobsFound = [ _source: { status: 'pending', output: { max_size_reached: false, csv_contains_formulas: false }, - payload: { type: 'spectacular', title: 'specimen' }, + payload: { title: 'specimen' }, }, }, ]; const jobQueueClientMock: ReportingAPIClient = { findForJobIds: async (jobIds: string[]) => { - return mockJobsFound as SourceJob[]; + return mockJobsFound as ReportDocument[]; }, getContent: (): Promise => { return Promise.resolve({ content: 'this is the completed report data' }); @@ -109,7 +110,7 @@ describe('stream handler', () => { { id: 'yas1', title: 'Yas', - type: 'yas', + jobtype: 'yas', status: 'completed', } as JobSummary, ], @@ -130,7 +131,7 @@ describe('stream handler', () => { { id: 'yas2', title: 'Yas', - type: 'yas', + jobtype: 'yas', status: 'completed', maxSizeReached: true, } as JobSummary, @@ -152,7 +153,7 @@ describe('stream handler', () => { { id: 'yas3', title: 'Yas', - type: 'yas', + jobtype: 'yas', status: 'completed', csvContainsFormulas: true, } as JobSummary, @@ -175,7 +176,7 @@ describe('stream handler', () => { { id: 'yas7', title: 'Yas 7', - type: 'yas', + jobtype: 'yas', status: 'failed', } as JobSummary, ], @@ -195,20 +196,20 @@ describe('stream handler', () => { { id: 'yas8', title: 'Yas 8', - type: 'yas', + jobtype: 'yas', status: 'completed', } as JobSummary, { id: 'yas9', title: 'Yas 9', - type: 'yas', + jobtype: 'yas', status: 'completed', csvContainsFormulas: true, } as JobSummary, { id: 'yas10', title: 'Yas 10', - type: 'yas', + jobtype: 'yas', status: 'completed', maxSizeReached: true, } as JobSummary, @@ -217,7 +218,7 @@ describe('stream handler', () => { { id: 'yas13', title: 'Yas 13', - type: 'yas', + jobtype: 'yas', status: 'failed', } as JobSummary, ], diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index 80ba02e17d56d..d97c0a7a2d11e 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -8,7 +8,8 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { catchError, map } from 'rxjs/operators'; import { NotificationsSetup } from 'src/core/public'; -import { JobId, JobStatusBuckets, JobSummary, SourceJob } from '../../common/types'; +import { JobSummarySet, JobSummary } from '../'; +import { JobId, ReportDocument } from '../../common/types'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUS_COMPLETED, @@ -28,14 +29,14 @@ function updateStored(jobIds: JobId[]): void { sessionStorage.setItem(JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JSON.stringify(jobIds)); } -function summarizeJob(src: SourceJob): JobSummary { +function getReportStatus(src: ReportDocument): JobSummary { return { id: src._id, status: src._source.status, title: src._source.payload.title, - type: src._source.payload.type, - maxSizeReached: src._source.output.max_size_reached, - csvContainsFormulas: src._source.output.csv_contains_formulas, + jobtype: src._source.jobtype, + maxSizeReached: src._source.output?.max_size_reached, + csvContainsFormulas: src._source.output?.csv_contains_formulas, }; } @@ -48,7 +49,7 @@ export class ReportingNotifierStreamHandler { public showNotifications({ completed: completedJobs, failed: failedJobs, - }: JobStatusBuckets): Rx.Observable { + }: JobSummarySet): Rx.Observable { const showNotificationsAsync = async () => { // notifications with download link for (const job of completedJobs) { @@ -92,9 +93,9 @@ export class ReportingNotifierStreamHandler { * An observable that finds jobs that are known to be "processing" (stored in * session storage) but have non-processing job status on the server */ - public findChangedStatusJobs(storedJobs: JobId[]): Rx.Observable { + public findChangedStatusJobs(storedJobs: JobId[]): Rx.Observable { return Rx.from(this.apiClient.findForJobIds(storedJobs)).pipe( - map((jobs: SourceJob[]) => { + map((jobs: ReportDocument[]) => { const completedJobs: JobSummary[] = []; const failedJobs: JobSummary[] = []; const pending: JobId[] = []; @@ -107,9 +108,9 @@ export class ReportingNotifierStreamHandler { } = job; if (storedJobs.includes(jobId)) { if (jobStatus === JOB_STATUS_COMPLETED || jobStatus === JOB_STATUS_WARNINGS) { - completedJobs.push(summarizeJob(job)); + completedJobs.push(getReportStatus(job)); } else if (jobStatus === JOB_STATUS_FAILED) { - failedJobs.push(summarizeJob(job)); + failedJobs.push(getReportStatus(job)); } else { pending.push(jobId); } diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.tsx index a134377e194b8..cc5964f737988 100644 --- a/x-pack/plugins/reporting/public/plugin.tsx +++ b/x-pack/plugins/reporting/public/plugin.tsx @@ -27,8 +27,9 @@ import { ManagementSetup } from '../../../../src/plugins/management/public'; import { SharePluginSetup } from '../../../../src/plugins/share/public'; import { LicensingPluginSetup } from '../../licensing/public'; import { durationToNumber } from '../common/schema_utils'; -import { JobId, JobStatusBuckets, ReportingConfigType } from '../common/types'; +import { JobId, ReportingConfigType } from '../common/types'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../constants'; +import { JobSummarySet } from './'; import { getGeneralErrorToast } from './components'; import { ReportListing } from './components/report_listing'; import { ReportingAPIClient } from './lib/reporting_api_client'; @@ -46,10 +47,7 @@ function getStored(): JobId[] { return sessionValue ? JSON.parse(sessionValue) : []; } -function handleError( - notifications: NotificationsSetup, - err: Error -): Rx.Observable { +function handleError(notifications: NotificationsSetup, err: Error): Rx.Observable { notifications.toasts.addDanger( getGeneralErrorToast( i18n.translate('xpack.reporting.publicNotifier.pollingErrorMessage', { diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 4ad35fd768825..451d907199c4c 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { IUiSettingsClient, ToastsSetup } from 'src/core/public'; import { ShareContext } from '../../../../../src/plugins/share/public'; import { LicensingPluginSetup } from '../../../licensing/public'; -import { JobParamsDiscoverCsv, SearchRequest } from '../../server/export_types/csv/types'; +import { JobParamsCSV, SearchRequest } from '../../server/export_types/csv/types'; import { ReportingPanelContent } from '../components/reporting_panel_content'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; @@ -59,7 +59,7 @@ export const csvReportingProvider = ({ return []; } - const jobParams: JobParamsDiscoverCsv = { + const jobParams: JobParamsCSV = { browserTimezone, objectType, title: sharingData.title as string, diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx index e10d04ea5fc6b..2dab66187bb25 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { IUiSettingsClient, ToastsSetup } from 'src/core/public'; import { ShareContext } from '../../../../../src/plugins/share/public'; import { LicensingPluginSetup } from '../../../licensing/public'; -import { LayoutInstance } from '../../common/types'; +import { LayoutParams } from '../../common/types'; import { JobParamsPNG } from '../../server/export_types/png/types'; import { JobParamsPDF } from '../../server/export_types/printable_pdf/types'; import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content'; @@ -80,7 +80,7 @@ export const reportingPDFPNGProvider = ({ objectType, browserTimezone, relativeUrls: [relativeUrl], // multi URL for PDF - layout: sharingData.layout as LayoutInstance, + layout: sharingData.layout as LayoutParams, title: sharingData.title as string, }; }; @@ -96,7 +96,7 @@ export const reportingPDFPNGProvider = ({ objectType, browserTimezone, relativeUrl, // single URL for PNG - layout: sharingData.layout as LayoutInstance, + layout: sharingData.layout as LayoutParams, title: sharingData.title as string, }; }; diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts index 0a76c7fcfd3b2..04ab572a53dbc 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts @@ -10,10 +10,10 @@ import open from 'opn'; import { ElementHandle, EvaluateFn, Page, Response, SerializableOrJSHandle } from 'puppeteer'; import { parse as parseUrl } from 'url'; import { getDisallowedOutgoingUrlError } from '../'; +import { ConditionalHeaders, ConditionalHeadersConditions } from '../../../export_types/common'; import { LevelLogger } from '../../../lib'; import { ViewZoomWidthHeight } from '../../../lib/layouts/layout'; import { ElementPosition } from '../../../lib/screenshots'; -import { ConditionalHeaders } from '../../../types'; import { allowRequest, NetworkPolicy } from '../../network_policy'; export interface ChromiumDriverOptions { @@ -34,8 +34,6 @@ interface EvaluateMetaOpts { context: string; } -type ConditionalHeadersConditions = ConditionalHeaders['conditions']; - interface InterceptedRequest { requestId: string; request: { diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 6897f07c45e2b..efef323612322 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -64,7 +64,7 @@ export class HeadlessChromiumDriverFactory { * Return an observable to objects which will drive screenshot capture for a page */ createPage( - { viewport, browserTimezone }: { viewport: ViewportConfig; browserTimezone: string }, + { viewport, browserTimezone }: { viewport: ViewportConfig; browserTimezone?: string }, pLogger: LevelLogger ): Rx.Observable<{ driver: HeadlessChromiumDriver; exit$: Rx.Observable }> { return Rx.Observable.create(async (observer: InnerSubscriber) => { diff --git a/x-pack/plugins/reporting/server/browsers/download/clean.ts b/x-pack/plugins/reporting/server/browsers/download/clean.ts index 1a362be8568cd..3d840f445b76e 100644 --- a/x-pack/plugins/reporting/server/browsers/download/clean.ts +++ b/x-pack/plugins/reporting/server/browsers/download/clean.ts @@ -7,13 +7,13 @@ import del from 'del'; import { readdirSync } from 'fs'; import { resolve as resolvePath } from 'path'; -import { LevelLogger } from '../../lib'; +import { GenericLevelLogger } from '../../lib/level_logger'; import { asyncMap } from './util'; /** * Delete any file in the `dir` that is not in the expectedPaths */ -export async function clean(dir: string, expectedPaths: string[], logger: LevelLogger) { +export async function clean(dir: string, expectedPaths: string[], logger: GenericLevelLogger) { let filenames: string[]; try { filenames = await readdirSync(dir); diff --git a/x-pack/plugins/reporting/server/browsers/download/download.ts b/x-pack/plugins/reporting/server/browsers/download/download.ts index 30b50c32a7402..b4b303416facd 100644 --- a/x-pack/plugins/reporting/server/browsers/download/download.ts +++ b/x-pack/plugins/reporting/server/browsers/download/download.ts @@ -8,7 +8,7 @@ import Axios from 'axios'; import { createHash } from 'crypto'; import { closeSync, mkdirSync, openSync, writeSync } from 'fs'; import { dirname } from 'path'; -import { LevelLogger } from '../../lib'; +import { GenericLevelLogger } from '../../lib/level_logger'; /** * Download a url and calculate it's checksum @@ -16,7 +16,7 @@ import { LevelLogger } from '../../lib'; * @param {String} path * @return {Promise} checksum of the downloaded file */ -export async function download(url: string, path: string, logger: LevelLogger) { +export async function download(url: string, path: string, logger: GenericLevelLogger) { logger.info(`Downloading ${url} to ${path}`); const hash = createHash('md5'); diff --git a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts index f56af15f5d76b..7c3cb7b1d76bb 100644 --- a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts +++ b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts @@ -7,7 +7,7 @@ import { existsSync } from 'fs'; import { resolve as resolvePath } from 'path'; import { BrowserDownload, chromium } from '../'; -import { LevelLogger } from '../../lib'; +import { GenericLevelLogger } from '../../lib/level_logger'; import { md5 } from './checksum'; import { clean } from './clean'; import { download } from './download'; @@ -18,7 +18,7 @@ import { asyncMap } from './util'; * download them if they are missing or their checksum is invalid * @return {Promise} */ -export async function ensureBrowserDownloaded(logger: LevelLogger) { +export async function ensureBrowserDownloaded(logger: GenericLevelLogger) { await ensureDownloaded([chromium], logger); } @@ -29,7 +29,7 @@ export async function ensureBrowserDownloaded(logger: LevelLogger) { * @param {BrowserSpec} browsers * @return {Promise} */ -async function ensureDownloaded(browsers: BrowserDownload[], logger: LevelLogger) { +async function ensureDownloaded(browsers: BrowserDownload[], logger: GenericLevelLogger) { await asyncMap(browsers, async (browser) => { const { archivesPath } = browser.paths; diff --git a/x-pack/plugins/reporting/server/browsers/install.ts b/x-pack/plugins/reporting/server/browsers/install.ts index 35cc5b6d8b7c2..350c988309a1f 100644 --- a/x-pack/plugins/reporting/server/browsers/install.ts +++ b/x-pack/plugins/reporting/server/browsers/install.ts @@ -8,7 +8,7 @@ import del from 'del'; import os from 'os'; import path from 'path'; import * as Rx from 'rxjs'; -import { LevelLogger } from '../lib'; +import { GenericLevelLogger } from '../lib/level_logger'; import { paths } from './chromium/paths'; import { ensureBrowserDownloaded } from './download'; // @ts-ignore @@ -46,7 +46,7 @@ export const getBinaryPath = ( * archive. If there is an error extracting the archive an `ExtractError` is thrown */ export function installBrowser( - logger: LevelLogger, + logger: GenericLevelLogger, chromiumPath: string = path.resolve(__dirname, '../../chromium'), platform: string = process.platform, architecture: string = os.arch() diff --git a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts index 908817a2ccf81..db1e622df4e21 100644 --- a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.test.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { cryptoFactory, LevelLogger } from '../../lib'; +import { cryptoFactory } from '../../lib'; +import { createMockLevelLogger } from '../../test_helpers'; import { decryptJobHeaders } from './'; +const logger = createMockLevelLogger(); + const encryptHeaders = async (encryptionKey: string, headers: Record) => { const crypto = cryptoFactory(encryptionKey); return await crypto.encrypt(headers); @@ -15,15 +18,11 @@ const encryptHeaders = async (encryptionKey: string, headers: Record { test(`fails if it can't decrypt headers`, async () => { const getDecryptedHeaders = () => - decryptJobHeaders({ - encryptionKey: 'abcsecretsauce', - job: { - headers: 'Q53+9A+zf+Xe+ceR/uB/aR/Sw/8e+M+qR+WiG+8z+EY+mo+HiU/zQL+Xn', - }, - logger: ({ - error: jest.fn(), - } as unknown) as LevelLogger, - }); + decryptJobHeaders( + 'abcsecretsauce', + 'Q53+9A+zf+Xe+ceR/uB/aR/Sw/8e+M+qR+WiG+8z+EY+mo+HiU/zQL+Xn', + logger + ); await expect(getDecryptedHeaders()).rejects.toMatchInlineSnapshot( `[Error: Failed to decrypt report job data. Please ensure that xpack.reporting.encryptionKey is set and re-generate this report. Error: Invalid IV length]` ); @@ -36,15 +35,7 @@ describe('headers', () => { }; const encryptedHeaders = await encryptHeaders('abcsecretsauce', headers); - const decryptedHeaders = await decryptJobHeaders({ - encryptionKey: 'abcsecretsauce', - job: { - title: 'cool-job-bro', - type: 'csv', - headers: encryptedHeaders, - }, - logger: {} as LevelLogger, - }); + const decryptedHeaders = await decryptJobHeaders('abcsecretsauce', encryptedHeaders, logger); expect(decryptedHeaders).toEqual(headers); }); }); diff --git a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts index 4f0088467dd68..131a7936e3463 100644 --- a/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/decrypt_job_headers.ts @@ -7,24 +7,13 @@ import { i18n } from '@kbn/i18n'; import { cryptoFactory, LevelLogger } from '../../lib'; -interface HasEncryptedHeaders { - headers?: string; -} - -export const decryptJobHeaders = async < - JobParamsType, - TaskPayloadType extends HasEncryptedHeaders ->({ - encryptionKey, - job, - logger, -}: { - encryptionKey?: string; - job: TaskPayloadType; - logger: LevelLogger; -}): Promise> => { +export const decryptJobHeaders = async ( + encryptionKey: string | undefined, + headers: string, + logger: LevelLogger +): Promise> => { try { - if (typeof job.headers !== 'string') { + if (typeof headers !== 'string') { throw new Error( i18n.translate('xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage', { defaultMessage: 'Job headers are missing', @@ -32,7 +21,7 @@ export const decryptJobHeaders = async < ); } const crypto = cryptoFactory(encryptionKey); - const decryptedHeaders = (await crypto.decrypt(job.headers)) as Record; + const decryptedHeaders = (await crypto.decrypt(headers)) as Record; return decryptedHeaders; } catch (err) { logger.error(err); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts index 794ea9febb5c0..b1d6f6fdf79c1 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.test.ts @@ -6,7 +6,6 @@ import { ReportingConfig } from '../../'; import { createMockConfig, createMockConfigSchema } from '../../test_helpers'; -import { BasePayload } from '../../types'; import { getConditionalHeaders } from './'; let mockConfig: ReportingConfig; @@ -24,11 +23,7 @@ describe('conditions', () => { baz: 'quix', }; - const conditionalHeaders = getConditionalHeaders({ - job: {} as BasePayload, - filteredHeaders: permittedHeaders, - config: mockConfig, - }); + const conditionalHeaders = getConditionalHeaders(mockConfig, permittedHeaders); expect(conditionalHeaders.conditions.hostname).toEqual( mockConfig.get('kibanaServer', 'hostname') @@ -49,19 +44,7 @@ describe('config formatting', () => { const mockSchema = createMockConfigSchema(reportingConfig); mockConfig = createMockConfig(mockSchema); - const conditionalHeaders = getConditionalHeaders({ - job: { - title: 'cool-job-bro', - type: 'csv', - jobParams: { - savedObjectId: 'abc-123', - isImmediate: false, - savedObjectType: 'search', - }, - }, - filteredHeaders: {}, - config: mockConfig, - }); + const conditionalHeaders = getConditionalHeaders(mockConfig, {}); expect(conditionalHeaders.conditions.hostname).toEqual('great-hostname'); }); }); diff --git a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.ts b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.ts index ce83323914eb8..d167ac21635b1 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_conditional_headers.ts @@ -5,17 +5,12 @@ */ import { ReportingConfig } from '../../'; -import { ConditionalHeaders } from '../../types'; +import { ConditionalHeaders } from './'; -export const getConditionalHeaders = ({ - config, - job, - filteredHeaders, -}: { - config: ReportingConfig; - job: TaskPayloadType; - filteredHeaders: Record; -}) => { +export const getConditionalHeaders = ( + config: ReportingConfig, + filteredHeaders: Record +) => { const { kbnConfig } = config; const [hostname, port, basePath, protocol] = [ config.get('kibanaServer', 'hostname'), diff --git a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.test.ts b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.test.ts index fae66b26a83e0..6a4e21b08996e 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.test.ts @@ -10,11 +10,6 @@ import { TaskPayloadPNG } from '../png/types'; import { TaskPayloadPDF } from '../printable_pdf/types'; import { getFullUrls } from './get_full_urls'; -interface FullUrlsOpts { - job: TaskPayloadPNG & TaskPayloadPDF; - config: ReportingConfig; -} - let mockConfig: ReportingConfig; beforeEach(() => { @@ -30,7 +25,7 @@ beforeEach(() => { const getMockJob = (base: object) => base as TaskPayloadPNG & TaskPayloadPDF; test(`fails if no URL is passed`, async () => { - const fn = () => getFullUrls({ job: getMockJob({}), config: mockConfig } as FullUrlsOpts); + const fn = () => getFullUrls(mockConfig, getMockJob({})); expect(fn).toThrowErrorMatchingInlineSnapshot( `"No valid URL fields found in Job Params! Expected \`job.relativeUrl: string\` or \`job.relativeUrls: string[]\`"` ); @@ -39,11 +34,7 @@ test(`fails if no URL is passed`, async () => { test(`fails if URLs are file-protocols for PNGs`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'file://etc/passwd/#/something'; - const fn = () => - getFullUrls({ - job: getMockJob({ relativeUrl, forceNow }), - config: mockConfig, - } as FullUrlsOpts); + const fn = () => getFullUrls(mockConfig, getMockJob({ relativeUrl, forceNow })); expect(fn).toThrowErrorMatchingInlineSnapshot( `"Found invalid URL(s), all URLs must be relative: file://etc/passwd/#/something"` ); @@ -53,11 +44,7 @@ test(`fails if URLs are absolute for PNGs`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something'; - const fn = () => - getFullUrls({ - job: getMockJob({ relativeUrl, forceNow }), - config: mockConfig, - } as FullUrlsOpts); + const fn = () => getFullUrls(mockConfig, getMockJob({ relativeUrl, forceNow })); expect(fn).toThrowErrorMatchingInlineSnapshot( `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something"` ); @@ -67,13 +54,13 @@ test(`fails if URLs are file-protocols for PDF`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; const relativeUrl = 'file://etc/passwd/#/something'; const fn = () => - getFullUrls({ - job: getMockJob({ + getFullUrls( + mockConfig, + getMockJob({ relativeUrls: [relativeUrl], forceNow, - }), - config: mockConfig, - } as FullUrlsOpts); + }) + ); expect(fn).toThrowErrorMatchingInlineSnapshot( `"Found invalid URL(s), all URLs must be relative: file://etc/passwd/#/something"` ); @@ -84,13 +71,13 @@ test(`fails if URLs are absolute for PDF`, async () => { const relativeUrl = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something'; const fn = () => - getFullUrls({ - job: getMockJob({ + getFullUrls( + mockConfig, + getMockJob({ relativeUrls: [relativeUrl], forceNow, - }), - config: mockConfig, - } as FullUrlsOpts); + }) + ); expect(fn).toThrowErrorMatchingInlineSnapshot( `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something"` ); @@ -104,22 +91,14 @@ test(`fails if any URLs are absolute or file's for PDF`, async () => { 'file://etc/passwd/#/something', ]; - const fn = () => - getFullUrls({ - job: getMockJob({ relativeUrls, forceNow }), - config: mockConfig, - } as FullUrlsOpts); + const fn = () => getFullUrls(mockConfig, getMockJob({ relativeUrls, forceNow })); expect(fn).toThrowErrorMatchingInlineSnapshot( `"Found invalid URL(s), all URLs must be relative: http://169.254.169.254/latest/meta-data/iam/security-credentials/profileName/#/something file://etc/passwd/#/something"` ); }); test(`fails if URL does not route to a visualization`, async () => { - const fn = () => - getFullUrls({ - job: getMockJob({ relativeUrl: '/app/phoney' }), - config: mockConfig, - } as FullUrlsOpts); + const fn = () => getFullUrls(mockConfig, getMockJob({ relativeUrl: '/app/phoney' })); expect(fn).toThrowErrorMatchingInlineSnapshot( `"No valid hash in the URL! A hash is expected for the application to route to the intended visualization."` ); @@ -127,10 +106,10 @@ test(`fails if URL does not route to a visualization`, async () => { test(`adds forceNow to hash's query, if it exists`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const urls = await getFullUrls({ - job: getMockJob({ relativeUrl: '/app/kibana#/something', forceNow }), - config: mockConfig, - } as FullUrlsOpts); + const urls = await getFullUrls( + mockConfig, + getMockJob({ relativeUrl: '/app/kibana#/something', forceNow }) + ); expect(urls[0]).toEqual( 'http://localhost:5601/sbp/app/kibana#/something?forceNow=2000-01-01T00%3A00%3A00.000Z' @@ -140,10 +119,10 @@ test(`adds forceNow to hash's query, if it exists`, async () => { test(`appends forceNow to hash's query, if it exists`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const urls = await getFullUrls({ - job: getMockJob({ relativeUrl: '/app/kibana#/something?_g=something', forceNow }), - config: mockConfig, - } as FullUrlsOpts); + const urls = await getFullUrls( + mockConfig, + getMockJob({ relativeUrl: '/app/kibana#/something?_g=something', forceNow }) + ); expect(urls[0]).toEqual( 'http://localhost:5601/sbp/app/kibana#/something?_g=something&forceNow=2000-01-01T00%3A00%3A00.000Z' @@ -151,18 +130,16 @@ test(`appends forceNow to hash's query, if it exists`, async () => { }); test(`doesn't append forceNow query to url, if it doesn't exists`, async () => { - const urls = await getFullUrls({ - job: getMockJob({ relativeUrl: '/app/kibana#/something' }), - config: mockConfig, - } as FullUrlsOpts); + const urls = await getFullUrls(mockConfig, getMockJob({ relativeUrl: '/app/kibana#/something' })); expect(urls[0]).toEqual('http://localhost:5601/sbp/app/kibana#/something'); }); test(`adds forceNow to each of multiple urls`, async () => { const forceNow = '2000-01-01T00:00:00.000Z'; - const urls = await getFullUrls({ - job: getMockJob({ + const urls = await getFullUrls( + mockConfig, + getMockJob({ relativeUrls: [ '/app/kibana#/something_aaa', '/app/kibana#/something_bbb', @@ -170,9 +147,8 @@ test(`adds forceNow to each of multiple urls`, async () => { '/app/kibana#/something_ddd', ], forceNow, - }), - config: mockConfig, - } as FullUrlsOpts); + }) + ); expect(urls).toEqual([ 'http://localhost:5601/sbp/app/kibana#/something_aaa?forceNow=2000-01-01T00%3A00%3A00.000Z', diff --git a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts index f4e3a7b723c08..7621a95083bc7 100644 --- a/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts +++ b/x-pack/plugins/reporting/server/export_types/common/get_full_urls.ts @@ -23,13 +23,7 @@ function isPdfJob(job: TaskPayloadPNG | TaskPayloadPDF): job is TaskPayloadPDF { return (job as TaskPayloadPDF).relativeUrls !== undefined; } -export function getFullUrls({ - config, - job, -}: { - config: ReportingConfig; - job: TaskPayloadPDF | TaskPayloadPNG; -}) { +export function getFullUrls(config: ReportingConfig, job: TaskPayloadPDF | TaskPayloadPNG) { const [basePath, protocol, hostname, port] = [ config.kbnConfig.get('server', 'basePath'), config.get('kibanaServer', 'protocol'), diff --git a/x-pack/plugins/reporting/server/export_types/common/index.ts b/x-pack/plugins/reporting/server/export_types/common/index.ts index 80eaa52d0951b..5fa313c8a2fb7 100644 --- a/x-pack/plugins/reporting/server/export_types/common/index.ts +++ b/x-pack/plugins/reporting/server/export_types/common/index.ts @@ -9,3 +9,21 @@ export { getConditionalHeaders } from './get_conditional_headers'; export { getFullUrls } from './get_full_urls'; export { omitBlockedHeaders } from './omit_blocked_headers'; export { validateUrls } from './validate_urls'; + +export interface TimeRangeParams { + timezone: string; + min?: Date | string | number | null; + max?: Date | string | number | null; +} + +export interface ConditionalHeadersConditions { + protocol: string; + hostname: string; + port: number; + basePath: string; +} + +export interface ConditionalHeaders { + headers: Record; + conditions: ConditionalHeadersConditions; +} diff --git a/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.test.ts index f40651603db8f..1833c2a7c62d7 100644 --- a/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.test.ts @@ -24,20 +24,9 @@ test(`omits blocked headers`, async () => { trailer: 's are for trucks', }; - const filteredHeaders = await omitBlockedHeaders({ - job: { - title: 'cool-job-bro', - type: 'csv', - jobParams: { - savedObjectId: 'abc-123', - isImmediate: false, - savedObjectType: 'search', - }, - }, - decryptedHeaders: { - ...permittedHeaders, - ...blockedHeaders, - }, + const filteredHeaders = omitBlockedHeaders({ + ...permittedHeaders, + ...blockedHeaders, }); expect(filteredHeaders).toEqual(permittedHeaders); diff --git a/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.ts b/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.ts index 946f033b4b481..09512ae703076 100644 --- a/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/omit_blocked_headers.ts @@ -9,13 +9,7 @@ import { KBN_SCREENSHOT_HEADER_BLOCK_LIST_STARTS_WITH_PATTERN, } from '../../../common/constants'; -export const omitBlockedHeaders = ({ - job, - decryptedHeaders, -}: { - job: TaskPayloadType; - decryptedHeaders: Record; -}) => { +export const omitBlockedHeaders = (decryptedHeaders: Record) => { const filteredHeaders: Record = omitBy( decryptedHeaders, (_value, header: string) => diff --git a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts index d768dc6f8e084..cb60b218818f0 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts @@ -6,10 +6,11 @@ import { cryptoFactory } from '../../lib'; import { CreateJobFn, CreateJobFnFactory } from '../../types'; -import { JobParamsDiscoverCsv } from './types'; +import { IndexPatternSavedObject, JobParamsCSV, TaskPayloadCSV } from './types'; export const createJobFnFactory: CreateJobFnFactory> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); @@ -18,10 +19,10 @@ export const createJobFnFactory: CreateJobFnFactory, - TaskPayloadCSV, + CreateJobFn, RunTaskFn > => ({ ...metadata, diff --git a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts index 214157db51cb7..78615a0e7b72c 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts @@ -8,16 +8,6 @@ import { BaseParams, BasePayload } from '../../types'; export type RawValue = string | object | null | undefined; -interface DocValueField { - field: string; - format: string; -} - -interface SortOptions { - order: string; - unmapped_type: string; -} - export interface IndexPatternSavedObject { title: string; timeFieldName: string; @@ -28,25 +18,23 @@ export interface IndexPatternSavedObject { }; } -export interface JobParamsDiscoverCsv extends BaseParams { - browserTimezone: string; - indexPatternId: string; - title: string; +interface BaseParamsCSV { searchRequest: SearchRequest; fields: string[]; metaFields: string[]; conflictedTypesFields: string[]; } -export interface TaskPayloadCSV extends BasePayload { - browserTimezone: string; - basePath: string; - searchRequest: any; - fields: any; - indexPatternSavedObject: any; - metaFields: any; - conflictedTypesFields: any; -} +export type JobParamsCSV = BaseParamsCSV & + BaseParams & { + indexPatternId: string; + }; + +// CSV create job method converts indexPatternID to indexPatternSavedObject +export type TaskPayloadCSV = BaseParamsCSV & + BasePayload & { + indexPatternSavedObject: IndexPatternSavedObject; + }; export interface SearchRequest { index: string; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts index 1746792981a21..c780247dd61b3 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts @@ -6,57 +6,40 @@ import { notFound, notImplemented } from 'boom'; import { get } from 'lodash'; -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { RequestHandlerContext } from 'src/core/server'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { cryptoFactory } from '../../lib'; -import { CreateJobFnFactory, TimeRangeParams } from '../../types'; +import { CsvFromSavedObjectRequest } from '../../routes/generate_from_savedobject_immediate'; +import { CreateJobFnFactory } from '../../types'; import { JobParamsPanelCsv, + JobPayloadPanelCsv, SavedObject, SavedObjectReference, SavedObjectServiceError, - SavedSearchObjectAttributesJSON, - SearchPanel, VisObjectAttributesJSON, } from './types'; export type ImmediateCreateJobFn = ( jobParams: JobParamsPanelCsv, - headers: KibanaRequest['headers'], context: RequestHandlerContext, - req: KibanaRequest -) => Promise<{ - type: string; - title: string; - jobParams: JobParamsPanelCsv; -}>; - -interface VisData { - title: string; - visType: string; - panel: SearchPanel; -} + req: CsvFromSavedObjectRequest +) => Promise; export const createJobFnFactory: CreateJobFnFactory = function createJobFactoryFn( reporting, parentLogger ) { - const config = reporting.getConfig(); - const crypto = cryptoFactory(config.get('encryptionKey')); const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'create-job']); - return async function createJob(jobParams, headers, context, req) { + return async function createJob(jobParams, context, req) { const { savedObjectType, savedObjectId } = jobParams; - const serializedEncryptedHeaders = await crypto.encrypt(headers); - const { panel, title, visType }: VisData = await Promise.resolve() + const panel = await Promise.resolve() .then(() => context.core.savedObjects.client.get(savedObjectType, savedObjectId)) .then(async (savedObject: SavedObject) => { const { attributes, references } = savedObject; - const { - kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON, - } = attributes as SavedSearchObjectAttributesJSON; - const { timerange } = req.body as { timerange: TimeRangeParams }; + const { kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON } = attributes; + const { timerange } = req.body; if (!kibanaSavedObjectMetaJSON) { throw new Error('Could not parse saved object data!'); @@ -85,7 +68,7 @@ export const createJobFnFactory: CreateJobFnFactory = func throw new Error('Could not find index pattern for the saved search!'); } - const sPanel = { + return { attributes: { ...attributes, kibanaSavedObjectMeta: { searchSource }, @@ -93,8 +76,6 @@ export const createJobFnFactory: CreateJobFnFactory = func indexPatternSavedObjectId: indexPatternMeta.id, timerange, }; - - return { panel: sPanel, title: attributes.title, visType: 'search' }; }) .catch((err: Error) => { const boomErr = (err as unknown) as { isBoom: boolean }; @@ -109,11 +90,6 @@ export const createJobFnFactory: CreateJobFnFactory = func throw new Error(`Unable to create a job from saved object data! Error: ${err}`); }); - return { - headers: serializedEncryptedHeaders, - jobParams: { ...jobParams, panel, visType }, - type: visType, - title, - }; + return { ...jobParams, panel }; }; }; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts index 0ca80581fcc83..19348c0a678d7 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts @@ -7,16 +7,11 @@ import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { CancellationToken } from '../../../common'; import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; -import { BasePayload, RunTaskFnFactory, TaskRunResult } from '../../types'; +import { TaskRunResult } from '../../lib/tasks'; +import { RunTaskFnFactory } from '../../types'; import { createGenerateCsv } from '../csv/generate_csv'; import { getGenerateCsvParams } from './lib/get_csv_job'; -import { JobParamsPanelCsv, SearchPanel } from './types'; - -/* - * The run function receives the full request which provides the un-encrypted - * headers, so encrypted headers are not part of these kind of job params - */ -type ImmediateJobParams = Omit, 'headers'>; +import { JobPayloadPanelCsv } from './types'; /* * ImmediateExecuteFn receives the job doc payload because the payload was @@ -24,7 +19,7 @@ type ImmediateJobParams = Omit, 'headers'>; */ export type ImmediateExecuteFn = ( jobId: null, - job: ImmediateJobParams, + job: JobPayloadPanelCsv, context: RequestHandlerContext, req: KibanaRequest ) => Promise; @@ -36,20 +31,16 @@ export const runTaskFnFactory: RunTaskFnFactory = function e const config = reporting.getConfig(); const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); - return async function runTask(jobId: string | null, jobPayload, context, req) { - // There will not be a jobID for "immediate" generation. - // jobID is only for "queued" jobs - // Use the jobID as a logging tag or "immediate" - const { jobParams } = jobPayload; + return async function runTask(jobId, jobPayload, context, req) { const jobLogger = logger.clone(['immediate']); const generateCsv = createGenerateCsv(jobLogger); - const { panel, visType } = jobParams as JobParamsPanelCsv & { panel: SearchPanel }; + const { panel, visType } = jobPayload; jobLogger.debug(`Execute job generating [${visType}] csv`); const savedObjectsClient = context.core.savedObjects.client; const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); - const job = await getGenerateCsvParams(jobParams, panel, savedObjectsClient, uiSettingsClient); + const job = await getGenerateCsvParams(jobPayload, panel, savedObjectsClient, uiSettingsClient); const elasticsearch = reporting.getElasticsearchService(); const { callAsCurrentUser } = elasticsearch.legacy.client.asScoped(req); diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts index 4b4cfb3f062bf..abe9fbf3e3950 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts @@ -17,7 +17,6 @@ import { ExportTypeDefinition } from '../../types'; import { createJobFnFactory, ImmediateCreateJobFn } from './create_job'; import { ImmediateExecuteFn, runTaskFnFactory } from './execute_job'; import { metadata } from './metadata'; -import { JobParamsPanelCsv } from './types'; /* * These functions are exported to share with the API route handler that @@ -27,9 +26,7 @@ export { createJobFnFactory } from './create_job'; export { runTaskFnFactory } from './execute_job'; export const getExportType = (): ExportTypeDefinition< - JobParamsPanelCsv, ImmediateCreateJobFn, - JobParamsPanelCsv, ImmediateExecuteFn > => ({ ...metadata, diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts index b387245406fbb..acf749584c6cd 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.test.ts @@ -13,7 +13,7 @@ describe('Get CSV Job', () => { let mockSavedObjectsClient: any; let mockUiSettingsClient: any; beforeEach(() => { - mockJobParams = { isImmediate: true, savedObjectType: 'search', savedObjectId: '234-ididid' }; + mockJobParams = { savedObjectType: 'search', savedObjectId: '234-ididid' }; mockSearchPanel = { indexPatternSavedObjectId: '123-indexId', attributes: { diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts index 26a4b17aaf71f..513f38122527b 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_csv_job.ts @@ -6,13 +6,8 @@ import { IUiSettingsClient, SavedObjectsClientContract } from 'kibana/server'; import { EsQueryConfig } from 'src/plugins/data/server'; -import { - esQuery, - Filter, - IIndexPattern, - Query, -} from '../../../../../../../src/plugins/data/server'; -import { TimeRangeParams } from '../../../types'; +import { esQuery, Filter, Query } from '../../../../../../../src/plugins/data/server'; +import { TimeRangeParams } from '../../common'; import { GenerateCsvParams } from '../../csv/generate_csv'; import { DocValueFields, @@ -50,11 +45,11 @@ export const getGenerateCsvParams = async ( savedObjectsClient: SavedObjectsClientContract, uiConfig: IUiSettingsClient ): Promise => { - let timerange: TimeRangeParams; + let timerange: TimeRangeParams | null; if (jobParams.post?.timerange) { timerange = jobParams.post?.timerange; } else { - timerange = panel.timerange; + timerange = panel.timerange || null; } const { indexPatternSavedObjectId } = panel; const savedSearchObjectAttr = panel.attributes as SavedSearchObjectAttributes; @@ -126,7 +121,9 @@ export const getGenerateCsvParams = async ( _source: { includes }, docvalue_fields: docValueFields, query: esQuery.buildEsQuery( - indexPatternSavedObject as IIndexPattern, + // compromise made while factoring out IIndexPattern type + // @ts-expect-error + indexPatternSavedObject, (searchSourceQuery as unknown) as Query, (combinedFilter as unknown) as Filter, esQueryConfig @@ -137,7 +134,7 @@ export const getGenerateCsvParams = async ( }; return { - browserTimezone: timerange.timezone, + browserTimezone: timerange?.timezone, indexPatternSavedObject, searchRequest, fields: includes, diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts index 429b2c518cf14..75e979aa2ec01 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeRangeParams } from '../../../types'; +import { TimeRangeParams } from '../../common'; import { QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types'; import { getFilters } from './get_filters'; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts index a1b04cca0419d..8827a30d370d4 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/lib/get_filters.ts @@ -6,7 +6,7 @@ import { badRequest } from 'boom'; import moment from 'moment-timezone'; -import { TimeRangeParams } from '../../../types'; +import { TimeRangeParams } from '../../common'; import { Filter, QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types'; export function getFilters( diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts index 9c45d23b13a37..cca79747110d5 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts @@ -4,20 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JobParamPostPayload, TimeRangeParams } from '../../types'; +import { TimeRangeParams } from '../common'; export interface FakeRequest { - headers: Record; + headers: Record; } -export interface JobParamsPostPayloadPanelCsv extends JobParamPostPayload { +export interface JobParamsPanelCsvPost { + timerange?: TimeRangeParams; state?: any; } export interface SearchPanel { indexPatternSavedObjectId: string; attributes: SavedSearchObjectAttributes; - timerange: TimeRangeParams; + timerange?: TimeRangeParams; } export interface JobPayloadPanelCsv extends JobParamsPanelCsv { @@ -27,8 +28,7 @@ export interface JobPayloadPanelCsv extends JobParamsPanelCsv { export interface JobParamsPanelCsv { savedObjectType: string; savedObjectId: string; - isImmediate: boolean; - post?: JobParamsPostPayloadPanelCsv; + post?: JobParamsPanelCsvPost; visType?: string; } diff --git a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts index 3727b2ec7b432..eaaa11d461156 100644 --- a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts @@ -7,10 +7,11 @@ import { cryptoFactory } from '../../../lib'; import { CreateJobFn, CreateJobFnFactory } from '../../../types'; import { validateUrls } from '../../common'; -import { JobParamsPNG } from '../types'; +import { JobParamsPNG, TaskPayloadPNG } from '../types'; export const createJobFnFactory: CreateJobFnFactory> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); diff --git a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts index 67fc51bbfc352..e6b36643900dd 100644 --- a/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/execute_job/index.ts @@ -8,7 +8,8 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; import { PNG_JOB_TYPE } from '../../../../common/constants'; -import { RunTaskFn, RunTaskFnFactory, TaskRunResult } from '../../..//types'; +import { TaskRunResult } from '../../../lib/tasks'; +import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { decryptJobHeaders, getConditionalHeaders, @@ -18,12 +19,9 @@ import { import { generatePngObservableFactory } from '../lib/generate_png'; import { TaskPayloadPNG } from '../types'; -type QueuedPngExecutorFactory = RunTaskFnFactory>; - -export const runTaskFnFactory: QueuedPngExecutorFactory = function executeJobFactoryFn( - reporting, - parentLogger -) { +export const runTaskFnFactory: RunTaskFnFactory> = function executeJobFactoryFn(reporting, parentLogger) { const config = reporting.getConfig(); const encryptionKey = config.get('encryptionKey'); const logger = parentLogger.clone([PNG_JOB_TYPE, 'execute']); @@ -36,11 +34,11 @@ export const runTaskFnFactory: QueuedPngExecutorFactory = function executeJobFac const generatePngObservable = await generatePngObservableFactory(reporting); const jobLogger = logger.clone([jobId]); const process$: Rx.Observable = Rx.of(1).pipe( - mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })), - map((decryptedHeaders) => omitBlockedHeaders({ job, decryptedHeaders })), - map((filteredHeaders) => getConditionalHeaders({ config, job, filteredHeaders })), + mergeMap(() => decryptJobHeaders(encryptionKey, job.headers, logger)), + map((decryptedHeaders) => omitBlockedHeaders(decryptedHeaders)), + map((filteredHeaders) => getConditionalHeaders(config, filteredHeaders)), mergeMap((conditionalHeaders) => { - const urls = getFullUrls({ config, job }); + const urls = getFullUrls(config, job); const hashUrl = urls[0]; if (apmGetAssets) apmGetAssets.end(); @@ -60,7 +58,6 @@ export const runTaskFnFactory: QueuedPngExecutorFactory = function executeJobFac content_type: 'image/png', content: base64, size: (base64 && base64.length) || 0, - warnings, }; }), diff --git a/x-pack/plugins/reporting/server/export_types/png/index.ts b/x-pack/plugins/reporting/server/export_types/png/index.ts index 1cc6836572b7b..50e09a9984b2c 100644 --- a/x-pack/plugins/reporting/server/export_types/png/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/index.ts @@ -19,9 +19,7 @@ import { metadata } from './metadata'; import { JobParamsPNG, TaskPayloadPNG } from './types'; export const getExportType = (): ExportTypeDefinition< - JobParamsPNG, CreateJobFn, - TaskPayloadPNG, RunTaskFn > => ({ ...metadata, diff --git a/x-pack/plugins/reporting/server/export_types/png/lib/generate_png.ts b/x-pack/plugins/reporting/server/export_types/png/lib/generate_png.ts index 096d0bd428214..786936d43424c 100644 --- a/x-pack/plugins/reporting/server/export_types/png/lib/generate_png.ts +++ b/x-pack/plugins/reporting/server/export_types/png/lib/generate_png.ts @@ -11,7 +11,7 @@ import { ReportingCore } from '../../../'; import { LevelLogger } from '../../../lib'; import { LayoutParams, PreserveLayout } from '../../../lib/layouts'; import { ScreenshotResults } from '../../../lib/screenshots'; -import { ConditionalHeaders } from '../../../types'; +import { ConditionalHeaders } from '../../common'; export async function generatePngObservableFactory(reporting: ReportingCore) { const getScreenshots = await reporting.getScreenshotsObservable(); @@ -19,7 +19,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) { return function generatePngObservable( logger: LevelLogger, url: string, - browserTimezone: string, + browserTimezone: string | undefined, conditionalHeaders: ConditionalHeaders, layoutParams: LayoutParams ): Rx.Observable<{ base64: string | null; warnings: string[] }> { diff --git a/x-pack/plugins/reporting/server/export_types/png/types.d.ts b/x-pack/plugins/reporting/server/export_types/png/types.d.ts index 373b709592ed2..1f99082c757c6 100644 --- a/x-pack/plugins/reporting/server/export_types/png/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/png/types.d.ts @@ -4,19 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { BaseParams, BasePayload } from '../../../server/types'; import { LayoutParams } from '../../lib/layouts'; +import { BaseParams, BasePayload } from '../../types'; -// Job params: structure of incoming user request data -export interface JobParamsPNG extends BaseParams { - title: string; +interface BaseParamsPNG { + layout: LayoutParams; + forceNow?: string; relativeUrl: string; } +// Job params: structure of incoming user request data +export type JobParamsPNG = BaseParamsPNG & BaseParams; + // Job payload: structure of stored job data provided by create_job -export interface TaskPayloadPNG extends BasePayload { - browserTimezone: string; - forceNow?: string; - layout: LayoutParams; - relativeUrl: string; -} +export type TaskPayloadPNG = BaseParamsPNG & BasePayload; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts index cae706a479b7f..07eed00401bac 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts @@ -7,10 +7,11 @@ import { cryptoFactory } from '../../../lib'; import { CreateJobFn, CreateJobFnFactory } from '../../../types'; import { validateUrls } from '../../common'; -import { JobParamsPDF } from '../types'; +import { JobParamsPDF, TaskPayloadPDF } from '../types'; export const createJobFnFactory: CreateJobFnFactory> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts index f3dc5bd656f73..ea0d60a9fad12 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts @@ -8,7 +8,8 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; import { PDF_JOB_TYPE } from '../../../../common/constants'; -import { RunTaskFn, RunTaskFnFactory, TaskRunResult } from '../../../types'; +import { TaskRunResult } from '../../../lib/tasks'; +import { RunTaskFn, RunTaskFnFactory } from '../../../types'; import { decryptJobHeaders, getConditionalHeaders, @@ -19,12 +20,9 @@ import { generatePdfObservableFactory } from '../lib/generate_pdf'; import { getCustomLogo } from '../lib/get_custom_logo'; import { TaskPayloadPDF } from '../types'; -type QueuedPdfExecutorFactory = RunTaskFnFactory>; - -export const runTaskFnFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn( - reporting, - parentLogger -) { +export const runTaskFnFactory: RunTaskFnFactory> = function executeJobFactoryFn(reporting, parentLogger) { const config = reporting.getConfig(); const encryptionKey = config.get('encryptionKey'); @@ -39,12 +37,12 @@ export const runTaskFnFactory: QueuedPdfExecutorFactory = function executeJobFac const jobLogger = logger.clone([jobId]); const process$: Rx.Observable = Rx.of(1).pipe( - mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })), - map((decryptedHeaders) => omitBlockedHeaders({ job, decryptedHeaders })), - map((filteredHeaders) => getConditionalHeaders({ config, job, filteredHeaders })), + mergeMap(() => decryptJobHeaders(encryptionKey, job.headers, logger)), + map((decryptedHeaders) => omitBlockedHeaders(decryptedHeaders)), + map((filteredHeaders) => getConditionalHeaders(config, filteredHeaders)), mergeMap((conditionalHeaders) => getCustomLogo(reporting, conditionalHeaders, job.spaceId)), mergeMap(({ logo, conditionalHeaders }) => { - const urls = getFullUrls({ config, job }); + const urls = getFullUrls(config, job); const { browserTimezone, layout, title } = job; if (apmGetAssets) apmGetAssets.end(); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts index cf3ec9cdc8c2d..26704693ee489 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts @@ -19,9 +19,7 @@ import { metadata } from './metadata'; import { JobParamsPDF, TaskPayloadPDF } from './types'; export const getExportType = (): ExportTypeDefinition< - JobParamsPDF, CreateJobFn, - TaskPayloadPDF, RunTaskFn > => ({ ...metadata, diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts index 17624c1bedb57..2cf5b69835d1f 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/generate_pdf.ts @@ -9,9 +9,9 @@ import * as Rx from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { ReportingCore } from '../../../'; import { LevelLogger } from '../../../lib'; -import { createLayout, LayoutInstance, LayoutParams } from '../../../lib/layouts'; +import { createLayout, LayoutParams } from '../../../lib/layouts'; import { ScreenshotResults } from '../../../lib/screenshots'; -import { ConditionalHeaders } from '../../../types'; +import { ConditionalHeaders } from '../../common'; // @ts-ignore untyped module import { pdf } from './pdf'; import { getTracker } from './tracker'; @@ -35,7 +35,7 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) { logger: LevelLogger, title: string, urls: string[], - browserTimezone: string, + browserTimezone: string | undefined, conditionalHeaders: ConditionalHeaders, layoutParams: LayoutParams, logo?: string @@ -43,7 +43,7 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) { const tracker = getTracker(); tracker.startLayout(); - const layout = createLayout(captureConfig, layoutParams) as LayoutInstance; + const layout = createLayout(captureConfig, layoutParams); tracker.endLayout(); tracker.startScreenshots(); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts index 8fa8fa5cbe3cb..426770d719069 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.test.ts @@ -11,7 +11,6 @@ import { createMockReportingCore, } from '../../../test_helpers'; import { getConditionalHeaders } from '../../common'; -import { TaskPayloadPDF } from '../types'; import { getCustomLogo } from './get_custom_logo'; let mockConfig: ReportingConfig; @@ -39,11 +38,7 @@ test(`gets logo from uiSettings`, async () => { get: mockGet, }); - const conditionalHeaders = getConditionalHeaders({ - job: {} as TaskPayloadPDF, - filteredHeaders: permittedHeaders, - config: mockConfig, - }); + const conditionalHeaders = getConditionalHeaders(mockConfig, permittedHeaders); const { logo } = await getCustomLogo(mockReportingPlugin, conditionalHeaders); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts index 35ab7001ecbe4..7bd1637db1379 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/get_custom_logo.ts @@ -6,7 +6,7 @@ import { ReportingCore } from '../../../'; import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../../common/constants'; -import { ConditionalHeaders } from '../../../types'; +import { ConditionalHeaders } from '../../common'; export const getCustomLogo = async ( reporting: ReportingCore, diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/pdf/index.js b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/pdf/index.js index 1042fd66abad7..8840fd524f3e4 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/pdf/index.js +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/lib/pdf/index.js @@ -104,7 +104,7 @@ class PdfMaker { table: { body: [[img]], }, - layout: 'simpleBorder', + layout: 'noBorder', }; contents.push(wrappedImg); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts index 7fd176e71f2d5..cef5c42856ff1 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts @@ -4,20 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { BaseParams, BasePayload } from '../../../server/types'; -import { LayoutInstance, LayoutParams } from '../../lib/layouts'; +import { LayoutParams } from '../../lib/layouts'; +import { BaseParams, BasePayload } from '../../types'; -// Job params: structure of incoming user request data, after being parsed from RISON -export interface JobParamsPDF extends BaseParams { - title: string; +interface BaseParamsPDF { + layout: LayoutParams; + forceNow?: string; relativeUrls: string[]; - layout: LayoutInstance; } +// Job params: structure of incoming user request data, after being parsed from RISON +export type JobParamsPDF = BaseParamsPDF & BaseParams; + // Job payload: structure of stored job data provided by create_job -export interface TaskPayloadPDF extends BasePayload { - browserTimezone: string; - forceNow?: string; - layout: LayoutParams; - relativeUrls: string[]; -} +export type TaskPayloadPDF = BaseParamsPDF & BasePayload; diff --git a/x-pack/plugins/reporting/server/lib/check_license.ts b/x-pack/plugins/reporting/server/lib/check_license.ts index a764aa1f1eec6..1f8f66fe9b5ee 100644 --- a/x-pack/plugins/reporting/server/lib/check_license.ts +++ b/x-pack/plugins/reporting/server/lib/check_license.ts @@ -24,9 +24,7 @@ const messages = { }, }; -const makeManagementFeature = ( - exportTypes: Array> -) => { +const makeManagementFeature = (exportTypes: ExportTypeDefinition[]) => { return { id: 'management', checkLicense: (license?: ILicense) => { @@ -59,9 +57,7 @@ const makeManagementFeature = ( }; }; -const makeExportTypeFeature = ( - exportType: ExportTypeDefinition -) => { +const makeExportTypeFeature = (exportType: ExportTypeDefinition) => { return { id: exportType.id, checkLicense: (license?: ILicense) => { diff --git a/x-pack/plugins/reporting/server/lib/create_queue.ts b/x-pack/plugins/reporting/server/lib/create_queue.ts index 2da3d8bd47ccb..ded21d105f2f4 100644 --- a/x-pack/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/plugins/reporting/server/lib/create_queue.ts @@ -5,13 +5,13 @@ */ import { ReportingCore } from '../core'; -import { JobSource, TaskRunResult } from '../types'; import { createWorkerFactory } from './create_worker'; // @ts-ignore import { Esqueue } from './esqueue'; import { createTaggedLogger } from './esqueue/create_tagged_logger'; import { LevelLogger } from './level_logger'; -import { ReportingStore } from './store'; +import { ReportDocument, ReportingStore } from './store'; +import { TaskRunResult } from './tasks'; interface ESQueueWorker { on: (event: string, handler: any) => void; @@ -32,7 +32,7 @@ export interface ESQueueInstance { // GenericWorkerFn is a generic for ImmediateExecuteFn | ESQueueWorkerExecuteFn, type GenericWorkerFn = ( - jobSource: JobSource, + jobSource: ReportDocument, ...workerRestArgs: any[] ) => void | Promise; diff --git a/x-pack/plugins/reporting/server/lib/create_worker.ts b/x-pack/plugins/reporting/server/lib/create_worker.ts index c1c88dd8a54ba..7f03cefdb620e 100644 --- a/x-pack/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/plugins/reporting/server/lib/create_worker.ts @@ -9,10 +9,12 @@ import { PLUGIN_ID } from '../../common/constants'; import { durationToNumber } from '../../common/schema_utils'; import { ReportingCore } from '../../server'; import { LevelLogger } from '../../server/lib'; -import { ExportTypeDefinition, JobSource, RunTaskFn } from '../../server/types'; +import { RunTaskFn } from '../../server/types'; import { ESQueueInstance } from './create_queue'; // @ts-ignore untyped dependency import { events as esqueueEvents } from './esqueue'; +import { ReportDocument } from './store'; +import { ReportTaskParams } from './tasks'; export function createWorkerFactory(reporting: ReportingCore, logger: LevelLogger) { const config = reporting.getConfig(); @@ -23,18 +25,16 @@ export function createWorkerFactory(reporting: ReportingCore, log // Once more document types are added, this will need to be passed in return async function createWorker(queue: ESQueueInstance) { // export type / execute job map - const jobExecutors: Map> = new Map(); + const jobExecutors: Map = new Map(); - for (const exportType of reporting.getExportTypesRegistry().getAll() as Array< - ExportTypeDefinition> - >) { + for (const exportType of reporting.getExportTypesRegistry().getAll()) { const jobExecutor = exportType.runTaskFnFactory(reporting, logger); jobExecutors.set(exportType.jobType, jobExecutor); } - const workerFn = ( - jobSource: JobSource, - jobParams: TaskPayloadType, + const workerFn = ( + jobSource: ReportDocument, + payload: ReportTaskParams['payload'], cancellationToken: CancellationToken ) => { const { @@ -52,7 +52,7 @@ export function createWorkerFactory(reporting: ReportingCore, log } // pass the work to the jobExecutor - return jobTypeExecutor(jobId, jobParams, cancellationToken); + return jobTypeExecutor(jobId, payload, cancellationToken); }; const workerOptions = { diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 5acc6e38dddf9..305247e6f8637 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -6,7 +6,8 @@ import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { ReportingCore } from '../'; -import { BaseParams, CreateJobFn, ReportingUser } from '../types'; +import { durationToNumber } from '../../common/schema_utils'; +import { BaseParams, ReportingUser } from '../types'; import { LevelLogger } from './'; import { Report } from './store'; @@ -23,6 +24,13 @@ export function enqueueJobFactory( parentLogger: LevelLogger ): EnqueueJobFn { const logger = parentLogger.clone(['queue-job']); + const config = reporting.getConfig(); + const jobSettings = { + timeout: durationToNumber(config.get('queue', 'timeout')), + browser_type: config.get('capture', 'browser', 'type'), + max_attempts: config.get('capture', 'maxAttempts'), + priority: 10, // unused + }; return async function enqueueJob( exportTypeId: string, @@ -31,8 +39,6 @@ export function enqueueJobFactory( context: RequestHandlerContext, request: KibanaRequest ) { - type CreateJobFnType = CreateJobFn; - const exportType = reporting.getExportTypesRegistry().getById(exportTypeId); if (exportType == null) { @@ -40,15 +46,24 @@ export function enqueueJobFactory( } const [createJob, { store }] = await Promise.all([ - exportType.createJobFnFactory(reporting, logger) as CreateJobFnType, + exportType.createJobFnFactory(reporting, logger), reporting.getPluginStartDeps(), ]); - // add encrytped headers - const payload = await createJob(jobParams, context, request); + const job = await createJob(jobParams, context, request); + const pendingReport = new Report({ + jobtype: exportType.jobType, + created_by: user ? user.username : false, + payload: job, + meta: { + objectType: jobParams.objectType, + layout: jobParams.layout?.id, + }, + ...jobSettings, + }); // store the pending report, puts it in the Reporting Management UI table - const report = await store.addReport(exportType.jobType, user, payload); + const report = await store.addReport(pendingReport); logger.info(`Scheduled ${exportType.name} report: ${report._id}`); diff --git a/x-pack/plugins/reporting/server/lib/export_types_registry.ts b/x-pack/plugins/reporting/server/lib/export_types_registry.ts index 1159221a9224e..e93cdba48a26a 100644 --- a/x-pack/plugins/reporting/server/lib/export_types_registry.ts +++ b/x-pack/plugins/reporting/server/lib/export_types_registry.ts @@ -9,21 +9,16 @@ import { getExportType as getTypeCsv } from '../export_types/csv'; import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_from_savedobject'; import { getExportType as getTypePng } from '../export_types/png'; import { getExportType as getTypePrintablePdf } from '../export_types/printable_pdf'; -import { ExportTypeDefinition } from '../types'; +import { CreateJobFn, ExportTypeDefinition } from '../types'; -type GetCallbackFn = ( - item: ExportTypeDefinition -) => boolean; -// => ExportTypeDefinition +type GetCallbackFn = (item: ExportTypeDefinition) => boolean; export class ExportTypesRegistry { - private _map: Map> = new Map(); + private _map: Map = new Map(); constructor() {} - register( - item: ExportTypeDefinition - ): void { + register(item: ExportTypeDefinition): void { if (!isString(item.id)) { throw new Error(`'item' must have a String 'id' property `); } @@ -43,35 +38,21 @@ export class ExportTypesRegistry { return this._map.size; } - getById( - id: string - ): ExportTypeDefinition { + getById(id: string): ExportTypeDefinition { if (!this._map.has(id)) { throw new Error(`Unknown id ${id}`); } - return this._map.get(id) as ExportTypeDefinition< - JobParamsType, - CreateJobFnType, - JobPayloadType, - RunTaskFnType - >; + return this._map.get(id) as ExportTypeDefinition; } - get( - findType: GetCallbackFn - ): ExportTypeDefinition { + get(findType: GetCallbackFn): ExportTypeDefinition { let result; for (const value of this._map.values()) { if (!findType(value)) { continue; // try next value } - const foundResult: ExportTypeDefinition< - JobParamsType, - CreateJobFnType, - JobPayloadType, - RunTaskFnType - > = value; + const foundResult: ExportTypeDefinition = value; if (result) { throw new Error('Found multiple items matching predicate.'); @@ -88,13 +69,19 @@ export class ExportTypesRegistry { } } +// TODO: Define a 2nd ExportTypeRegistry instance for "immediate execute" report job types only. +// It should not require a `CreateJobFn` for its ExportTypeDefinitions, which only makes sense for async. +// Once that is done, the `any` types below can be removed. + +/* + * @return ExportTypeRegistry: the ExportTypeRegistry instance that should be + * used to register async export type definitions + */ export function getExportTypesRegistry(): ExportTypesRegistry { const registry = new ExportTypesRegistry(); - - /* this replaces the previously async method of registering export types, - * where this would run a directory scan and types would be registered via - * discovery */ - const getTypeFns: Array<() => ExportTypeDefinition> = [ + type CreateFnType = CreateJobFn; // can not specify params types because different type of params are not assignable to each other + type RunFnType = any; // can not specify because ImmediateExecuteFn is not assignable to RunTaskFn + const getTypeFns: Array<() => ExportTypeDefinition> = [ getTypeCsv, getTypeCsvFromSavedObject, getTypePng, diff --git a/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts index 921d302387edf..585175aac82c5 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts @@ -6,12 +6,11 @@ import { CaptureConfig } from '../../types'; import { LayoutParams, LayoutTypes } from './'; -import { Layout } from './layout'; import { PreserveLayout } from './preserve_layout'; import { PrintLayout } from './print_layout'; -export function createLayout(captureConfig: CaptureConfig, layoutParams?: LayoutParams): Layout { - if (layoutParams && layoutParams.id === LayoutTypes.PRESERVE_LAYOUT) { +export function createLayout(captureConfig: CaptureConfig, layoutParams?: LayoutParams) { + if (layoutParams && layoutParams.dimensions && layoutParams.id === LayoutTypes.PRESERVE_LAYOUT) { return new PreserveLayout(layoutParams.dimensions); } diff --git a/x-pack/plugins/reporting/server/lib/layouts/index.ts b/x-pack/plugins/reporting/server/lib/layouts/index.ts index 507b7614072ea..c091339a60582 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/index.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/index.ts @@ -53,7 +53,7 @@ export interface Size { export interface LayoutParams { id: string; - dimensions: Size; + dimensions?: Size; selectors?: LayoutSelectorDictionary; } @@ -64,4 +64,4 @@ interface LayoutSelectors { positionElements?: (browser: HeadlessChromiumDriver, logger: LevelLogger) => Promise; } -export type LayoutInstance = Layout & LayoutSelectors & Size; +export type LayoutInstance = Layout & LayoutSelectors & Partial; diff --git a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts index e8d182dac0b1d..cecd761fbcf32 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts @@ -12,12 +12,13 @@ import { LayoutTypes, PageSizeParams, Size, + LayoutInstance, } from './'; // We use a zoom of two to bump up the resolution of the screenshot a bit. const ZOOM: number = 2; -export class PreserveLayout extends Layout { +export class PreserveLayout extends Layout implements LayoutInstance { public readonly selectors: LayoutSelectorDictionary = getDefaultLayoutSelectors(); public readonly groupCount = 1; public readonly height: number; diff --git a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts index b055fae8a780d..33f16bc7865d5 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts @@ -9,10 +9,16 @@ import { EvaluateFn, SerializableOrJSHandle } from 'puppeteer'; import { LevelLogger } from '../'; import { HeadlessChromiumDriver } from '../../browsers'; import { CaptureConfig } from '../../types'; -import { getDefaultLayoutSelectors, LayoutSelectorDictionary, LayoutTypes, Size } from './'; +import { + getDefaultLayoutSelectors, + LayoutInstance, + LayoutSelectorDictionary, + LayoutTypes, + Size, +} from './'; import { Layout } from './layout'; -export class PrintLayout extends Layout { +export class PrintLayout extends Layout implements LayoutInstance { public readonly selectors: LayoutSelectorDictionary = { ...getDefaultLayoutSelectors(), screenshot: '[data-shared-item]', diff --git a/x-pack/plugins/reporting/server/lib/level_logger.ts b/x-pack/plugins/reporting/server/lib/level_logger.ts index d015d500363c1..9db5274a93db8 100644 --- a/x-pack/plugins/reporting/server/lib/level_logger.ts +++ b/x-pack/plugins/reporting/server/lib/level_logger.ts @@ -10,7 +10,14 @@ const trimStr = (toTrim: string) => { return typeof toTrim === 'string' ? toTrim.trim() : toTrim; }; -export class LevelLogger { +export interface GenericLevelLogger { + debug: (msg: string) => void; + info: (msg: string) => void; + warning: (msg: string) => void; + error: (msg: Error) => void; +} + +export class LevelLogger implements GenericLevelLogger { private _logger: LoggerFactory; private _tags: string[]; public warning: (msg: string, tags?: string[]) => void; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/get_time_range.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_time_range.ts index afd6364454835..5f7919df4e9fd 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_time_range.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_time_range.ts @@ -5,7 +5,7 @@ */ import { LevelLogger, startTrace } from '../'; -import { LayoutInstance } from '../../../common/types'; +import { LayoutInstance } from '../layouts'; import { HeadlessChromiumDriver } from '../../browsers'; import { CONTEXT_GETTIMERANGE } from './constants'; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/index.ts b/x-pack/plugins/reporting/server/lib/screenshots/index.ts index c1d33cb519384..1b9722fb49458 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/index.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/index.ts @@ -6,7 +6,7 @@ import * as Rx from 'rxjs'; import { LevelLogger } from '../'; -import { ConditionalHeaders } from '../../types'; +import { ConditionalHeaders } from '../../export_types/common'; import { LayoutInstance } from '../layouts'; export { screenshotsObservableFactory } from './observable'; @@ -16,7 +16,7 @@ export interface ScreenshotObservableOpts { urls: string[]; conditionalHeaders: ConditionalHeaders; layout: LayoutInstance; - browserTimezone: string; + browserTimezone?: string; } export interface AttributesMap { diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts index 5b671e9f5b47e..798f926cd0a31 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts @@ -18,6 +18,7 @@ jest.mock('../../browsers/chromium/puppeteer', () => ({ import moment from 'moment'; import * as Rx from 'rxjs'; import { HeadlessChromiumDriver } from '../../browsers'; +import { ConditionalHeaders } from '../../export_types/common'; import { createMockBrowserDriverFactory, createMockConfig, @@ -25,7 +26,6 @@ import { createMockLayoutInstance, createMockLevelLogger, } from '../../test_helpers'; -import { ConditionalHeaders } from '../../types'; import { ElementsPositionAndAttribute } from './'; import * as contexts from './constants'; import { screenshotsObservableFactory } from './observable'; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts index e28f50851f4d9..e8b7f91764efd 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts @@ -5,10 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { durationToNumber } from '../../../common/schema_utils'; import { LevelLogger, startTrace } from '../'; +import { durationToNumber } from '../../../common/schema_utils'; import { HeadlessChromiumDriver } from '../../browsers'; -import { CaptureConfig, ConditionalHeaders } from '../../types'; +import { ConditionalHeaders } from '../../export_types/common'; +import { CaptureConfig } from '../../types'; export const openUrl = async ( captureConfig: CaptureConfig, diff --git a/x-pack/plugins/reporting/server/lib/store/index.ts b/x-pack/plugins/reporting/server/lib/store/index.ts index a88d36d3fdf9a..a48f266120323 100644 --- a/x-pack/plugins/reporting/server/lib/store/index.ts +++ b/x-pack/plugins/reporting/server/lib/store/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { Report } from './report'; +export { Report, ReportDocument } from './report'; export { ReportingStore } from './store'; diff --git a/x-pack/plugins/reporting/server/lib/store/report.test.ts b/x-pack/plugins/reporting/server/lib/store/report.test.ts index 9ac5d1f87c387..1e4a833c7cabe 100644 --- a/x-pack/plugins/reporting/server/lib/store/report.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/report.test.ts @@ -14,7 +14,8 @@ describe('Class Report', () => { created_by: 'created_by_test_string', browser_type: 'browser_type_test_string', max_attempts: 50, - payload: { headers: 'payload_test_field', objectType: 'testOt' }, + payload: { headers: 'payload_test_field', objectType: 'testOt', title: 'cool report' }, + meta: { objectType: 'test' }, timeout: 30000, priority: 1, }); @@ -25,11 +26,10 @@ describe('Class Report', () => { attempts: 0, browser_type: 'browser_type_test_string', completed_at: undefined, - created_at: undefined, created_by: 'created_by_test_string', jobtype: 'test-report', max_attempts: 50, - meta: undefined, + meta: { objectType: 'test' }, payload: { headers: 'payload_test_field', objectType: 'testOt' }, priority: 1, started_at: undefined, @@ -38,12 +38,16 @@ describe('Class Report', () => { }, }); expect(report.toApiJSON()).toMatchObject({ + attempts: 0, browser_type: 'browser_type_test_string', created_by: 'created_by_test_string', + index: '.reporting-test-index-12345', jobtype: 'test-report', max_attempts: 50, payload: { headers: 'payload_test_field', objectType: 'testOt' }, + meta: { objectType: 'test' }, priority: 1, + status: 'pending', timeout: 30000, }); @@ -57,7 +61,8 @@ describe('Class Report', () => { created_by: 'created_by_test_string', browser_type: 'browser_type_test_string', max_attempts: 50, - payload: { headers: 'payload_test_field', objectType: 'testOt' }, + payload: { headers: 'payload_test_field', objectType: 'testOt', title: 'hot report' }, + meta: { objectType: 'stange' }, timeout: 30000, priority: 1, }); @@ -70,51 +75,46 @@ describe('Class Report', () => { }; report.updateWithEsDoc(metadata); - expect(report.toEsDocsJSON()).toMatchInlineSnapshot(` - Object { - "_id": "12342p9o387549o2345", - "_index": ".reporting-test-update", - "_source": Object { - "attempts": 0, - "browser_type": "browser_type_test_string", - "completed_at": undefined, - "created_at": undefined, - "created_by": "created_by_test_string", - "jobtype": "test-report", - "max_attempts": 50, - "meta": undefined, - "payload": Object { - "headers": "payload_test_field", - "objectType": "testOt", - }, - "priority": 1, - "started_at": undefined, - "status": "pending", - "timeout": 30000, - }, - } - `); - expect(report.toApiJSON()).toMatchInlineSnapshot(` - Object { - "attempts": 0, - "browser_type": "browser_type_test_string", - "completed_at": undefined, - "created_at": undefined, - "created_by": "created_by_test_string", - "id": "12342p9o387549o2345", - "index": ".reporting-test-update", - "jobtype": "test-report", - "max_attempts": 50, - "meta": undefined, - "payload": Object { - "headers": "payload_test_field", - "objectType": "testOt", - }, - "priority": 1, - "started_at": undefined, - "status": "pending", - "timeout": 30000, - } - `); + expect(report.toEsDocsJSON()).toMatchObject({ + _id: '12342p9o387549o2345', + _index: '.reporting-test-update', + _source: { + attempts: 0, + browser_type: 'browser_type_test_string', + completed_at: undefined, + created_by: 'created_by_test_string', + jobtype: 'test-report', + max_attempts: 50, + meta: { objectType: 'stange' }, + payload: { objectType: 'testOt' }, + priority: 1, + started_at: undefined, + status: 'pending', + timeout: 30000, + }, + }); + expect(report.toApiJSON()).toMatchObject({ + attempts: 0, + browser_type: 'browser_type_test_string', + completed_at: undefined, + created_by: 'created_by_test_string', + id: '12342p9o387549o2345', + index: '.reporting-test-update', + jobtype: 'test-report', + max_attempts: 50, + meta: { objectType: 'stange' }, + payload: { headers: 'payload_test_field', objectType: 'testOt' }, + priority: 1, + started_at: undefined, + status: 'pending', + timeout: 30000, + }); + }); + + it('throws error if converted to task JSON before being synced with ES storage', () => { + const report = new Report({} as any); + expect(() => report.updateWithEsDoc(report)).toThrowErrorMatchingInlineSnapshot( + `"Report object from ES has missing fields!"` + ); }); }); diff --git a/x-pack/plugins/reporting/server/lib/store/report.ts b/x-pack/plugins/reporting/server/lib/store/report.ts index 5c9b9ced7cce7..d82b90f4025ed 100644 --- a/x-pack/plugins/reporting/server/lib/store/report.ts +++ b/x-pack/plugins/reporting/server/lib/store/report.ts @@ -4,84 +4,96 @@ * you may not use this file except in compliance with the Elastic License. */ +import moment from 'moment'; // @ts-ignore no module definition import Puid from 'puid'; +import { JobStatus, ReportApiJSON } from '../../../common/types'; import { JobStatuses } from '../../../constants'; -import { LayoutInstance } from '../layouts'; +import { LayoutParams } from '../layouts'; +import { TaskRunResult } from '../tasks'; -/* - * The document created by Reporting to store in the .reporting index - */ -interface ReportingDocument { +interface ReportDocumentHead { _id: string; _index: string; _seq_no: unknown; _primary_term: unknown; +} + +/* + * The document created by Reporting to store in the .reporting index + */ +export interface ReportDocument extends ReportDocumentHead { + _source: ReportSource; +} + +export interface ReportSource { jobtype: string; + kibana_name: string; + kibana_id: string; created_by: string | false; payload: { headers: string; // encrypted headers + browserTimezone?: string; // may use timezone from advanced settings objectType: string; - layout?: LayoutInstance; + title: string; + layout?: LayoutParams; }; - meta: unknown; + meta: { objectType: string; layout?: string }; browser_type: string; max_attempts: number; timeout: number; - status: string; + status: JobStatus; attempts: number; - output?: unknown; + output: TaskRunResult | null; started_at?: string; completed_at?: string; - created_at?: string; + created_at: string; priority?: number; process_expiration?: string; } -/* - * The document created by Reporting to store as task parameters for Task - * Manager to reference the report in .reporting - */ const puid = new Puid(); -export class Report implements Partial { +export class Report implements Partial { public _index?: string; public _id: string; public _primary_term?: unknown; // set by ES public _seq_no: unknown; // set by ES - public readonly jobtype: string; - public readonly created_at?: string; - public readonly created_by?: string | false; - public readonly payload: { - headers: string; // encrypted headers - objectType: string; - layout?: LayoutInstance; - }; - public readonly meta: unknown; - public readonly max_attempts: number; - public readonly browser_type?: string; - - public readonly status: string; - public readonly attempts: number; - public readonly output?: unknown; - public readonly started_at?: string; - public readonly completed_at?: string; - public readonly process_expiration?: string; - public readonly priority?: number; - public readonly timeout?: number; + public readonly kibana_name: ReportSource['kibana_name']; + public readonly kibana_id: ReportSource['kibana_id']; + public readonly jobtype: ReportSource['jobtype']; + public readonly created_at: ReportSource['created_at']; + public readonly created_by: ReportSource['created_by']; + public readonly payload: ReportSource['payload']; + + public readonly meta: ReportSource['meta']; + public readonly max_attempts: ReportSource['max_attempts']; + public readonly browser_type?: ReportSource['browser_type']; + + public readonly status: ReportSource['status']; + public readonly attempts: ReportSource['attempts']; + public readonly output?: ReportSource['output']; + public readonly started_at?: ReportSource['started_at']; + public readonly completed_at?: ReportSource['completed_at']; + public readonly process_expiration?: ReportSource['process_expiration']; + public readonly priority?: ReportSource['priority']; + public readonly timeout?: ReportSource['timeout']; /* * Create an unsaved report + * Index string is required */ - constructor(opts: Partial) { + constructor(opts: Partial & Partial) { this._id = opts._id != null ? opts._id : puid.generate(); this._index = opts._index; this._primary_term = opts._primary_term; this._seq_no = opts._seq_no; this.payload = opts.payload!; + this.kibana_name = opts.kibana_name!; + this.kibana_id = opts.kibana_id!; this.jobtype = opts.jobtype!; this.max_attempts = opts.max_attempts!; this.attempts = opts.attempts || 0; @@ -89,9 +101,9 @@ export class Report implements Partial { this.process_expiration = opts.process_expiration; this.timeout = opts.timeout; - this.created_at = opts.created_at; - this.created_by = opts.created_by; - this.meta = opts.meta; + this.created_at = opts.created_at || moment.utc().toISOString(); + this.created_by = opts.created_by || false; + this.meta = opts.meta || { objectType: 'unknown' }; this.browser_type = opts.browser_type; this.priority = opts.priority; @@ -141,10 +153,12 @@ export class Report implements Partial { /* * Data structure for API responses */ - toApiJSON() { + toApiJSON(): ReportApiJSON { return { id: this._id, - index: this._index, + index: this._index!, + kibana_name: this.kibana_name, + kibana_id: this.kibana_id, jobtype: this.jobtype, created_at: this.created_at, created_by: this.created_by, diff --git a/x-pack/plugins/reporting/server/lib/store/store.test.ts b/x-pack/plugins/reporting/server/lib/store/store.test.ts index 8dc4edd200052..931eae8b246c4 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.test.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.test.ts @@ -48,28 +48,25 @@ describe('ReportingStore', () => { describe('addReport', () => { it('returns Report object', async () => { const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_headers_1', - objectType: 'testOt', - }; - await expect( - store.addReport(reportType, { username: 'username1' }, reportPayload) - ).resolves.toMatchObject({ + const mockReport = new Report({ + _index: '.reporting-mock', + attempts: 0, + created_by: 'username1', + jobtype: 'unknowntype', + status: 'pending', + payload: {}, + meta: {}, + } as any); + await expect(store.addReport(mockReport)).resolves.toMatchObject({ _primary_term: undefined, _seq_no: undefined, attempts: 0, - browser_type: undefined, completed_at: undefined, created_by: 'username1', jobtype: 'unknowntype', - max_attempts: undefined, payload: {}, - priority: 10, - started_at: undefined, + meta: {}, status: 'pending', - timeout: 120000, }); }); @@ -83,15 +80,15 @@ describe('ReportingStore', () => { mockCore = await createMockReportingCore(mockConfig); const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_headers_2', - objectType: 'testOt', - }; - expect( - store.addReport(reportType, { username: 'user1' }, reportPayload) - ).rejects.toMatchInlineSnapshot(`[Error: Invalid index interval: centurially]`); + const mockReport = new Report({ + _index: '.reporting-errortest', + jobtype: 'unknowntype', + payload: {}, + meta: {}, + } as any); + expect(store.addReport(mockReport)).rejects.toMatchInlineSnapshot( + `[TypeError: this.client.callAsInternalUser is not a function]` + ); }); it('handles error creating the index', async () => { @@ -100,15 +97,15 @@ describe('ReportingStore', () => { callClusterStub.withArgs('indices.create').rejects(new Error('horrible error')); const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_headers_3', - objectType: 'testOt', - }; - await expect( - store.addReport(reportType, { username: 'user1' }, reportPayload) - ).rejects.toMatchInlineSnapshot(`[Error: horrible error]`); + const mockReport = new Report({ + _index: '.reporting-errortest', + jobtype: 'unknowntype', + payload: {}, + meta: {}, + } as any); + await expect(store.addReport(mockReport)).rejects.toMatchInlineSnapshot( + `[Error: horrible error]` + ); }); /* Creating the index will fail, if there were multiple jobs staged in @@ -123,15 +120,15 @@ describe('ReportingStore', () => { callClusterStub.withArgs('indices.create').rejects(new Error('devastating error')); const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_headers_4', - objectType: 'testOt', - }; - await expect( - store.addReport(reportType, { username: 'user1' }, reportPayload) - ).rejects.toMatchInlineSnapshot(`[Error: devastating error]`); + const mockReport = new Report({ + _index: '.reporting-mock', + jobtype: 'unknowntype', + payload: {}, + meta: {}, + } as any); + await expect(store.addReport(mockReport)).rejects.toMatchInlineSnapshot( + `[Error: devastating error]` + ); }); it('skips creating the index if already exists', async () => { @@ -142,28 +139,20 @@ describe('ReportingStore', () => { .rejects(new Error('resource_already_exists_exception')); // will be triggered but ignored const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_headers_5', - objectType: 'testOt', - }; - await expect( - store.addReport(reportType, { username: 'user1' }, reportPayload) - ).resolves.toMatchObject({ + const mockReport = new Report({ + created_by: 'user1', + jobtype: 'unknowntype', + payload: {}, + meta: {}, + } as any); + await expect(store.addReport(mockReport)).resolves.toMatchObject({ _primary_term: undefined, _seq_no: undefined, attempts: 0, - browser_type: undefined, - completed_at: undefined, created_by: 'user1', jobtype: 'unknowntype', - max_attempts: undefined, payload: {}, - priority: 10, - started_at: undefined, status: 'pending', - timeout: 120000, }); }); @@ -175,26 +164,24 @@ describe('ReportingStore', () => { .rejects(new Error('resource_already_exists_exception')); // will be triggered but ignored const store = new ReportingStore(mockCore, mockLogger); - const reportType = 'unknowntype'; - const reportPayload = { - browserTimezone: 'UTC', - headers: 'rp_test_headers', - objectType: 'testOt', - }; - await expect(store.addReport(reportType, false, reportPayload)).resolves.toMatchObject({ + const mockReport = new Report({ + _index: '.reporting-unsecured', + attempts: 0, + created_by: false, + jobtype: 'unknowntype', + payload: {}, + meta: {}, + status: 'pending', + } as any); + await expect(store.addReport(mockReport)).resolves.toMatchObject({ _primary_term: undefined, _seq_no: undefined, attempts: 0, - browser_type: undefined, - completed_at: undefined, created_by: false, jobtype: 'unknowntype', - max_attempts: undefined, + meta: {}, payload: {}, - priority: 10, - started_at: undefined, status: 'pending', - timeout: 120000, }); }); }); @@ -209,8 +196,10 @@ describe('ReportingStore', () => { browser_type: 'browser_type_test_string', max_attempts: 50, payload: { + title: 'test report', headers: 'rp_test_headers', objectType: 'testOt', + browserTimezone: 'ABC', }, timeout: 30000, priority: 1, @@ -248,8 +237,10 @@ describe('ReportingStore', () => { browser_type: 'browser_type_test_string', max_attempts: 50, payload: { + title: 'test report', headers: 'rp_test_headers', objectType: 'testOt', + browserTimezone: 'BCD', }, timeout: 30000, priority: 1, @@ -287,8 +278,10 @@ describe('ReportingStore', () => { browser_type: 'browser_type_test_string', max_attempts: 50, payload: { + title: 'test report', headers: 'rp_test_headers', objectType: 'testOt', + browserTimezone: 'CDE', }, timeout: 30000, priority: 1, @@ -326,8 +319,10 @@ describe('ReportingStore', () => { browser_type: 'browser_type_test_string', max_attempts: 50, payload: { + title: 'test report', headers: 'rp_test_headers', objectType: 'testOt', + browserTimezone: 'utc', }, timeout: 30000, priority: 1, diff --git a/x-pack/plugins/reporting/server/lib/store/store.ts b/x-pack/plugins/reporting/server/lib/store/store.ts index 03d88ca60e2c0..c20a9e991b4bc 100644 --- a/x-pack/plugins/reporting/server/lib/store/store.ts +++ b/x-pack/plugins/reporting/server/lib/store/store.ts @@ -5,21 +5,12 @@ */ import { ElasticsearchServiceSetup } from 'src/core/server'; -import { durationToNumber } from '../../../common/schema_utils'; import { LevelLogger, statuses } from '../'; import { ReportingCore } from '../../'; -import { BaseParams, BaseParamsEncryptedFields, ReportingUser } from '../../types'; import { indexTimestamp } from './index_timestamp'; import { mapping } from './mapping'; import { Report } from './report'; -interface JobSettings { - timeout: number; - browser_type: string; - max_attempts: number; - priority: number; -} - const checkReportIsEditable = (report: Report) => { if (!report._id || !report._index) { throw new Error(`Report object is not synced with ES!`); @@ -35,7 +26,6 @@ const checkReportIsEditable = (report: Report) => { export class ReportingStore { private readonly indexPrefix: string; private readonly indexInterval: string; - private readonly jobSettings: JobSettings; private client: ElasticsearchServiceSetup['legacy']['client']; private logger: LevelLogger; @@ -46,13 +36,6 @@ export class ReportingStore { this.client = elasticsearch.legacy.client; this.indexPrefix = config.get('index'); this.indexInterval = config.get('queue', 'indexInterval'); - this.jobSettings = { - timeout: durationToNumber(config.get('queue', 'timeout')), - browser_type: config.get('capture', 'browser', 'type'), - max_attempts: config.get('capture', 'maxAttempts'), - priority: 10, // unused - }; - this.logger = logger; } @@ -101,36 +84,17 @@ export class ReportingStore { * Called from addReport, which handles any errors */ private async indexReport(report: Report) { - const params = report.payload; - - // Queing is handled by TM. These queueing-based fields for reference in Report Info panel - const infoFields = { - timeout: report.timeout, - process_expiration: new Date(0), // use epoch so the job query works - created_at: new Date(), - attempts: 0, - max_attempts: report.max_attempts, - status: statuses.JOB_STATUS_PENDING, - browser_type: report.browser_type, - }; - - const indexParams = { + const doc = { index: report._index, id: report._id, body: { - ...infoFields, - jobtype: report.jobtype, - meta: { - // We are copying these values out of payload because these fields are indexed and can be aggregated on - // for tracking stats, while payload contents are not. - objectType: params.objectType, - layout: params.layout ? params.layout.id : 'none', - }, - payload: report.payload, - created_by: report.created_by, + ...report.toEsDocsJSON()._source, + process_expiration: new Date(0), // use epoch so the job query works + attempts: 0, + status: statuses.JOB_STATUS_PENDING, }, }; - return await this.client.callAsInternalUser('index', indexParams); + return await this.client.callAsInternalUser('index', doc); } /* @@ -140,23 +104,15 @@ export class ReportingStore { return await this.client.callAsInternalUser('indices.refresh', { index }); } - public async addReport( - type: string, - user: ReportingUser, - payload: BaseParams & BaseParamsEncryptedFields - ): Promise { - const timestamp = indexTimestamp(this.indexInterval); - const index = `${this.indexPrefix}-${timestamp}`; + public async addReport(report: Report): Promise { + let index = report._index; + if (!index) { + const timestamp = indexTimestamp(this.indexInterval); + index = `${this.indexPrefix}-${timestamp}`; + report._index = index; + } await this.createIndex(index); - const report = new Report({ - _index: index, - payload, - jobtype: type, - created_by: user ? user.username : false, - ...this.jobSettings, - }); - try { const doc = await this.indexReport(report); report.updateWithEsDoc(doc); @@ -166,7 +122,7 @@ export class ReportingStore { return report; } catch (err) { - this.logger.error(`Error in addReport!`); + this.logger.error(`Error in adding a report!`); this.logger.error(err); throw err; } @@ -220,7 +176,7 @@ export class ReportingStore { public async setReportCompleted(report: Report, stats: Partial): Promise { try { - const { output } = stats as { output: any }; + const { output } = stats; const status = output && output.warnings && output.warnings.length > 0 ? statuses.JOB_STATUS_WARNINGS diff --git a/x-pack/plugins/reporting/server/lib/tasks/index.ts b/x-pack/plugins/reporting/server/lib/tasks/index.ts new file mode 100644 index 0000000000000..0dd9945985bfb --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/tasks/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { BasePayload } from '../../types'; +import { ReportSource } from '../store/report'; + +/* + * The document created by Reporting to store as task parameters for Task + * Manager to reference the report in .reporting + */ +export interface ReportTaskParams { + id: string; + index?: string; // For ad-hoc, which as an existing "pending" record + payload: JobPayloadType; + created_at: ReportSource['created_at']; + created_by: ReportSource['created_by']; + jobtype: ReportSource['jobtype']; + attempts: ReportSource['attempts']; + meta: ReportSource['meta']; +} + +export interface TaskRunResult { + content_type: string | null; + content: string | null; + csv_contains_formulas?: boolean; + size: number; + max_size_reached?: boolean; + warnings?: string[]; +} diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts index 33620bc9a0038..dc4b30ffcfa7c 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.ts @@ -9,8 +9,8 @@ import { ReportingCore } from '../..'; import { API_DIAGNOSE_URL } from '../../../common/constants'; import { browserStartLogs } from '../../browsers/chromium/driver_factory/start_logs'; import { LevelLogger as Logger } from '../../lib'; -import { DiagnosticResponse } from '../../types'; import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing'; +import { DiagnosticResponse } from './'; const logsToHelpMap = { 'error while loading shared libraries': i18n.translate( diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/config.ts b/x-pack/plugins/reporting/server/routes/diagnostic/config.ts index 95c3a05bbf680..70428779366b3 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/config.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/config.ts @@ -10,8 +10,8 @@ import { defaults, get } from 'lodash'; import { ReportingCore } from '../..'; import { API_DIAGNOSE_URL } from '../../../common/constants'; import { LevelLogger as Logger } from '../../lib'; -import { DiagnosticResponse } from '../../types'; import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing'; +import { DiagnosticResponse } from './'; const KIBANA_MAX_SIZE_BYTES_PATH = 'csv.maxSizeBytes'; const ES_MAX_SIZE_BYTES_PATH = 'http.max_content_length'; diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts index 895dee32614f1..84df91ea31b62 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/index.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/index.ts @@ -15,3 +15,9 @@ export const registerDiagnosticRoutes = (reporting: ReportingCore, logger: Logge registerDiagnoseConfig(reporting, logger); registerDiagnoseScreenshot(reporting, logger); }; + +export interface DiagnosticResponse { + help: string[]; + success: boolean; + logs: string; +} diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts index 0acf384869ded..6ea6e22c5d7f9 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.ts @@ -11,8 +11,8 @@ import { omitBlockedHeaders } from '../../export_types/common'; import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url'; import { generatePngObservableFactory } from '../../export_types/png/lib/generate_png'; import { LevelLogger as Logger } from '../../lib'; -import { DiagnosticResponse } from '../../types'; import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing'; +import { DiagnosticResponse } from './'; export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Logger) => { const setupDeps = reporting.getPluginSetupDeps(); @@ -54,10 +54,7 @@ export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Log }; const headers = { - headers: omitBlockedHeaders({ - job: null, - decryptedHeaders, - }), + headers: omitBlockedHeaders(decryptedHeaders), conditions: { hostname, port: +port, diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index 517f1dadc0ac1..400fbb16f54dc 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -10,17 +10,20 @@ import { ReportingCore } from '../'; import { API_BASE_GENERATE_V1 } from '../../common/constants'; import { createJobFnFactory } from '../export_types/csv_from_savedobject/create_job'; import { runTaskFnFactory } from '../export_types/csv_from_savedobject/execute_job'; -import { JobParamsPostPayloadPanelCsv } from '../export_types/csv_from_savedobject/types'; +import { + JobParamsPanelCsv, + JobParamsPanelCsvPost, +} from '../export_types/csv_from_savedobject/types'; import { LevelLogger as Logger } from '../lib'; -import { TaskRunResult } from '../types'; +import { TaskRunResult } from '../lib/tasks'; import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing'; import { getJobParamsFromRequest } from './lib/get_job_params_from_request'; import { HandlerErrorFunction } from './types'; export type CsvFromSavedObjectRequest = KibanaRequest< - { savedObjectType: string; savedObjectId: string }, + JobParamsPanelCsv, unknown, - JobParamsPostPayloadPanelCsv + JobParamsPanelCsvPost >; /* @@ -66,27 +69,22 @@ export function registerGenerateCsvFromSavedObjectImmediate( }, userHandler(async (user, context, req: CsvFromSavedObjectRequest, res) => { const logger = parentLogger.clone(['savedobject-csv']); - const jobParams = getJobParamsFromRequest(req, { isImmediate: true }); + const jobParams = getJobParamsFromRequest(req); const createJob = createJobFnFactory(reporting, logger); const runTaskFn = runTaskFnFactory(reporting, logger); try { // FIXME: no create job for immediate download - const jobDocPayload = await createJob(jobParams, req.headers, context, req); + const payload = await createJob(jobParams, context, req); const { content_type: jobOutputContentType, content: jobOutputContent, size: jobOutputSize, - }: TaskRunResult = await runTaskFn(null, jobDocPayload, context, req); + }: TaskRunResult = await runTaskFn(null, payload, context, req); logger.info(`Job output size: ${jobOutputSize} bytes`); - /* - * ESQueue worker function defaults `content` to null, even if the - * runTask returned undefined. - * - * This converts null to undefined so the value can be sent to h.response() - */ + // convert null to undefined so the value can be sent to h.response() if (jobOutputContent === null) { logger.warn('CSV Job Execution created empty content result'); } diff --git a/x-pack/plugins/reporting/server/routes/generation.test.ts b/x-pack/plugins/reporting/server/routes/generation.test.ts index dd905223a81d5..867af75c8de27 100644 --- a/x-pack/plugins/reporting/server/routes/generation.test.ts +++ b/x-pack/plugins/reporting/server/routes/generation.test.ts @@ -74,8 +74,8 @@ describe('POST /api/reporting/generate', () => { jobContentEncoding: 'base64', jobContentExtension: 'pdf', validLicenses: ['basic', 'gold'], - createJobFnFactory: () => () => ({ jobParamsTest: { test1: 'yes' } }), - runTaskFnFactory: () => () => ({ runParamsTest: { test2: 'yes' } }), + createJobFnFactory: () => async () => ({ createJobTest: { test1: 'yes' } } as any), + runTaskFnFactory: () => async () => ({ runParamsTest: { test2: 'yes' } } as any), }); core.getExportTypesRegistry = () => mockExportTypesRegistry; }); @@ -163,9 +163,21 @@ describe('POST /api/reporting/generate', () => { .then(({ body }) => { expect(body).toMatchObject({ job: { - id: expect.any(String), + attempts: 0, + created_by: 'Tom Riddle', + id: 'foo', + index: 'foo-index', + jobtype: 'printable_pdf', + payload: { + createJobTest: { + test1: 'yes', + }, + }, + priority: 10, + status: 'pending', + timeout: 10000, }, - path: expect.any(String), + path: 'undefined/api/reporting/jobs/download/foo', }); }); }); diff --git a/x-pack/plugins/reporting/server/routes/index.ts b/x-pack/plugins/reporting/server/routes/index.ts index 11ad4cc9d4eb8..22edd4002dbcf 100644 --- a/x-pack/plugins/reporting/server/routes/index.ts +++ b/x-pack/plugins/reporting/server/routes/index.ts @@ -15,3 +15,10 @@ export function registerRoutes(reporting: ReportingCore, logger: Logger) { registerJobInfoRoutes(reporting); registerDiagnosticRoutes(reporting, logger); } + +export interface ReportingRequestPre { + management: { + jobTypes: string[]; + }; + user: string; +} diff --git a/x-pack/plugins/reporting/server/routes/jobs.test.ts b/x-pack/plugins/reporting/server/routes/jobs.test.ts index 187c69f4a72ef..fc1cfd00493c3 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.test.ts @@ -25,7 +25,6 @@ describe('GET /api/reporting/jobs/download', () => { let core: ReportingCore; const config = createMockConfig(createMockConfigSchema()); - const getHits = (...sources: any) => { return { hits: { @@ -69,14 +68,14 @@ describe('GET /api/reporting/jobs/download', () => { jobType: 'unencodedJobType', jobContentExtension: 'csv', validLicenses: ['basic', 'gold'], - } as ExportTypeDefinition); + } as ExportTypeDefinition); exportTypesRegistry.register({ id: 'base64Encoded', jobType: 'base64EncodedJobType', jobContentEncoding: 'base64', jobContentExtension: 'pdf', validLicenses: ['basic', 'gold'], - } as ExportTypeDefinition); + } as ExportTypeDefinition); core.getExportTypesRegistry = () => exportTypesRegistry; }); diff --git a/x-pack/plugins/reporting/server/routes/jobs.ts b/x-pack/plugins/reporting/server/routes/jobs.ts index db62c0cc403fc..43e73c137fb13 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.ts @@ -128,7 +128,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) { } return res.ok({ - body: jobOutput, + body: jobOutput || {}, headers: { 'content-type': 'application/json', }, diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts index 84a98d6d1f1d7..b154978d041f4 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -9,9 +9,9 @@ import contentDisposition from 'content-disposition'; import { get } from 'lodash'; import { CSV_JOB_TYPE } from '../../../common/constants'; import { ExportTypesRegistry, statuses } from '../../lib'; -import { ExportTypeDefinition, JobSource, TaskRunResult } from '../../types'; - -type ExportTypeType = ExportTypeDefinition; +import { ReportDocument } from '../../lib/store'; +import { TaskRunResult } from '../../lib/tasks'; +import { ExportTypeDefinition } from '../../types'; interface ErrorFromPayload { message: string; @@ -27,10 +27,10 @@ interface Payload { const DEFAULT_TITLE = 'report'; -const getTitle = (exportType: ExportTypeType, title?: string): string => +const getTitle = (exportType: ExportTypeDefinition, title?: string): string => `${title || DEFAULT_TITLE}.${exportType.jobContentExtension}`; -const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeType) => { +const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeDefinition) => { const metaDataHeaders: Record = {}; if (exportType.jobType === CSV_JOB_TYPE) { @@ -45,7 +45,10 @@ const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeType) }; export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegistry) { - function encodeContent(content: string | null, exportType: ExportTypeType): Buffer | string { + function encodeContent( + content: string | null, + exportType: ExportTypeDefinition + ): Buffer | string { switch (exportType.jobContentEncoding) { case 'base64': return content ? Buffer.from(content, 'base64') : ''; // convert null to empty string @@ -55,7 +58,9 @@ export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegist } function getCompleted(output: TaskRunResult, jobType: string, title: string): Payload { - const exportType = exportTypesRegistry.get((item: ExportTypeType) => item.jobType === jobType); + const exportType = exportTypesRegistry.get( + (item: ExportTypeDefinition) => item.jobType === jobType + ); const filename = getTitle(exportType, title); const headers = getReportingHeaders(output, exportType); @@ -92,16 +97,18 @@ export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegist }; } - return function getDocumentPayload(doc: JobSource): Payload { + return function getDocumentPayload(doc: ReportDocument): Payload { const { status, jobtype: jobType, payload: { title } = { title: '' } } = doc._source; const { output } = doc._source; - if (status === statuses.JOB_STATUS_COMPLETED || status === statuses.JOB_STATUS_WARNINGS) { - return getCompleted(output, jobType, title); - } + if (output) { + if (status === statuses.JOB_STATUS_COMPLETED || status === statuses.JOB_STATUS_WARNINGS) { + return getCompleted(output, jobType, title); + } - if (status === statuses.JOB_STATUS_FAILED) { - return getFailure(output); + if (status === statuses.JOB_STATUS_FAILED) { + return getFailure(output); + } } // send a 503 indicating that the report isn't completed yet diff --git a/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts b/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts index bfa15a4022a4d..e685339c966ed 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_job_params_from_request.ts @@ -7,17 +7,13 @@ import { JobParamsPanelCsv } from '../../export_types/csv_from_savedobject/types'; import { CsvFromSavedObjectRequest } from '../generate_from_savedobject_immediate'; -export function getJobParamsFromRequest( - request: CsvFromSavedObjectRequest, - opts: { isImmediate: boolean } -): JobParamsPanelCsv { +export function getJobParamsFromRequest(request: CsvFromSavedObjectRequest): JobParamsPanelCsv { const { savedObjectType, savedObjectId } = request.params; const { timerange, state } = request.body; const post = timerange || state ? { timerange, state } : undefined; return { - isImmediate: opts.isImmediate, savedObjectType, savedObjectId, post, diff --git a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts index b01c880abe820..5a5cedc03aa0e 100644 --- a/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/lib/jobs_query.ts @@ -8,7 +8,8 @@ import { i18n } from '@kbn/i18n'; import { errors as elasticsearchErrors } from 'elasticsearch'; import { get } from 'lodash'; import { ReportingCore } from '../../'; -import { JobSource, ReportingUser } from '../../types'; +import { ReportDocument } from '../../lib/store'; +import { ReportingUser } from '../../types'; const esErrors = elasticsearchErrors as Record; const defaultSize = 10; @@ -130,7 +131,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { }); }, - get(user: ReportingUser, id: string, opts: GetOpts = {}): Promise | void> { + get(user: ReportingUser, id: string, opts: GetOpts = {}): Promise { if (!id) return Promise.resolve(); const username = getUsername(user); @@ -161,7 +162,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore) { async delete(deleteIndex: string, id: string) { try { - const query = { id, index: deleteIndex }; + const query = { id, index: deleteIndex, refresh: true }; return callAsInternalUser('delete', query); } catch (error) { throw new Error( diff --git a/x-pack/plugins/reporting/server/routes/types.d.ts b/x-pack/plugins/reporting/server/routes/types.d.ts index 5c34d466197fe..b3f9225c3dce5 100644 --- a/x-pack/plugins/reporting/server/routes/types.d.ts +++ b/x-pack/plugins/reporting/server/routes/types.d.ts @@ -18,11 +18,11 @@ export type HandlerFunction = ( export type HandlerErrorFunction = (res: KibanaResponseFactory, err: Error) => any; -export interface QueuedJobPayload { +export interface QueuedJobPayload { error?: boolean; source: { job: { - payload: BasePayload; + payload: BasePayload; }; }; } diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index a3c63a0fb539d..eb046a3eab075 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -13,81 +13,11 @@ import { CancellationToken } from '../../../plugins/reporting/common'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { AuthenticatedUser, SecurityPluginSetup } from '../../security/server'; -import { JobStatus } from '../common/types'; import { ReportingConfigType } from './config'; import { ReportingCore } from './core'; import { LevelLogger } from './lib'; -import { LayoutInstance } from './lib/layouts'; - -/* - * Routing types - */ - -export interface ReportingRequestPre { - management: { - jobTypes: string[]; - }; - user: string; -} - -// generate a report with unparsed jobParams -export interface GenerateExportTypePayload { - jobParams: string; -} - -export type ReportingRequestPayload = GenerateExportTypePayload | JobParamPostPayload; - -export interface TimeRangeParams { - timezone: string; - min?: Date | string | number | null; - max?: Date | string | number | null; -} - -// the "raw" data coming from the client, unencrypted -export interface JobParamPostPayload { - timerange?: TimeRangeParams; -} - -// the pre-processed, encrypted data ready for storage -export interface BasePayload { - headers: string; // serialized encrypted headers - jobParams: JobParamsType; - title: string; - type: string; - spaceId?: string; -} - -export interface JobSource { - _id: string; - _index: string; - _source: { - jobtype: string; - output: TaskRunResult; - payload: BasePayload; - status: JobStatus; - }; -} - -export interface TaskRunResult { - content_type: string | null; - content: string | null; - csv_contains_formulas?: boolean; - size: number; - max_size_reached?: boolean; - warnings?: string[]; -} - -interface ConditionalHeadersConditions { - protocol: string; - hostname: string; - port: number; - basePath: string; -} - -export interface ConditionalHeaders { - headers: Record; - conditions: ConditionalHeadersConditions; -} +import { LayoutParams } from './lib/layouts'; +import { ReportTaskParams, TaskRunResult } from './lib/tasks'; /* * Plugin Contract @@ -118,24 +48,29 @@ export type CaptureConfig = ReportingConfigType['capture']; export type ScrollConfig = ReportingConfigType['csv']['scroll']; export interface BaseParams { - browserTimezone: string; - layout?: LayoutInstance; // for screenshot type reports + browserTimezone?: string; // browserTimezone is optional: it is not in old POST URLs that were generated prior to being added to this interface + layout?: LayoutParams; objectType: string; + title: string; } -export interface BaseParamsEncryptedFields extends BaseParams { - headers: string; // encrypted headers +// base params decorated with encrypted headers that come into runJob functions +export interface BasePayload extends BaseParams { + headers: string; + spaceId?: string; } -export type CreateJobFn = ( +// default fn type for CreateJobFnFactory +export type CreateJobFn = ( jobParams: JobParamsType, context: RequestHandlerContext, - request: KibanaRequest -) => Promise; + request: KibanaRequest +) => Promise; -export type RunTaskFn = ( +// default fn type for RunTaskFnFactory +export type RunTaskFn = ( jobId: string, - job: TaskPayloadType, + payload: ReportTaskParams['payload'], cancellationToken: CancellationToken ) => Promise; @@ -149,12 +84,7 @@ export type RunTaskFnFactory = ( logger: LevelLogger ) => RunTaskFnType; -export interface ExportTypeDefinition< - JobParamsType, - CreateJobFnType, - JobPayloadType, - RunTaskFnType -> { +export interface ExportTypeDefinition { id: string; name: string; jobType: string; @@ -164,9 +94,3 @@ export interface ExportTypeDefinition< runTaskFnFactory: RunTaskFnFactory; validLicenses: string[]; } - -export interface DiagnosticResponse { - help: string[]; - success: boolean; - logs: string; -} diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts index fc2dce441c621..f12b76ccce847 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -467,6 +467,575 @@ describe('Ready for collection observable', () => { "fetch": [Function], "formatForBulkUpload": [Function], "isReady": [Function], + "schema": Object { + "PNG": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, + "_all": Object { + "type": "long", + }, + "available": Object { + "type": "boolean", + }, + "browser_type": Object { + "type": "keyword", + }, + "csv": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, + "enabled": Object { + "type": "boolean", + }, + "last7Days": Object { + "PNG": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, + "_all": Object { + "type": "long", + }, + "csv": Object { + "available": Object { + "type": "boolean", + }, + "total": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "app": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "available": Object { + "type": "boolean", + }, + "layout": Object { + "preserve_layout": Object { + "type": "long", + }, + "print": Object { + "type": "long", + }, + }, + "total": Object { + "type": "long", + }, + }, + "status": Object { + "cancelled": Object { + "type": "long", + }, + "completed": Object { + "type": "long", + }, + "completed_with_warnings": Object { + "type": "long", + }, + "failed": Object { + "type": "long", + }, + "pending": Object { + "type": "long", + }, + "processing": Object { + "type": "long", + }, + }, + "statuses": Object { + "cancelled": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed_with_warnings": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "failed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "pending": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "processing": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + }, + }, + "printable_pdf": Object { + "app": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "available": Object { + "type": "boolean", + }, + "layout": Object { + "preserve_layout": Object { + "type": "long", + }, + "print": Object { + "type": "long", + }, + }, + "total": Object { + "type": "long", + }, + }, + "status": Object { + "cancelled": Object { + "type": "long", + }, + "completed": Object { + "type": "long", + }, + "completed_with_warnings": Object { + "type": "long", + }, + "failed": Object { + "type": "long", + }, + "pending": Object { + "type": "long", + }, + "processing": Object { + "type": "long", + }, + }, + "statuses": Object { + "cancelled": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "completed_with_warnings": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "failed": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "pending": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + "processing": Object { + "PNG": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "csv": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + "printable_pdf": Object { + "canvas workpad": Object { + "type": "long", + }, + "dashboard": Object { + "type": "long", + }, + "visualization": Object { + "type": "long", + }, + }, + }, + }, + }, "type": "reporting", } `); diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts index 8f26579726ff1..176d3dcb37dfc 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -13,6 +13,7 @@ import { ReportingSetupDeps } from '../types'; import { GetLicense } from './'; import { getReportingUsage } from './get_reporting_usage'; import { ReportingUsageType } from './types'; +import { reportingSchema } from './schema'; // places the reporting data as kibana stats const METATYPE = 'kibana_stats'; @@ -41,6 +42,7 @@ export function getReportingUsageCollector( return getReportingUsage(config, getLicense, callCluster, exportTypesRegistry); }, isReady, + schema: reportingSchema, /* * Format the response data into a model for internal upload * 1. Make this data part of the "kibana_stats" type diff --git a/x-pack/plugins/reporting/server/usage/schema.ts b/x-pack/plugins/reporting/server/usage/schema.ts new file mode 100644 index 0000000000000..63ac3a7152e77 --- /dev/null +++ b/x-pack/plugins/reporting/server/usage/schema.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 { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { AppCounts, AvailableTotal, JobTypes, RangeStats, ReportingUsageType } from './types'; + +const appCountsSchema: MakeSchemaFrom = { + 'canvas workpad': { type: 'long' }, + dashboard: { type: 'long' }, + visualization: { type: 'long' }, +}; + +const byAppCountsSchema: MakeSchemaFrom = { + csv: appCountsSchema, + PNG: appCountsSchema, + printable_pdf: appCountsSchema, +}; + +const availableTotalSchema: MakeSchemaFrom = { + available: { type: 'boolean' }, + total: { type: 'long' }, +}; + +const jobTypesSchema: MakeSchemaFrom = { + csv: availableTotalSchema, + PNG: availableTotalSchema, + printable_pdf: { + ...availableTotalSchema, + app: appCountsSchema, + layout: { + print: { type: 'long' }, + preserve_layout: { type: 'long' }, + }, + }, +}; + +const rangeStatsSchema: MakeSchemaFrom = { + ...jobTypesSchema, + _all: { type: 'long' }, + status: { + cancelled: { type: 'long' }, + completed: { type: 'long' }, + completed_with_warnings: { type: 'long' }, + failed: { type: 'long' }, + pending: { type: 'long' }, + processing: { type: 'long' }, + }, + statuses: { + cancelled: byAppCountsSchema, + completed: byAppCountsSchema, + completed_with_warnings: byAppCountsSchema, + failed: byAppCountsSchema, + pending: byAppCountsSchema, + processing: byAppCountsSchema, + }, +}; + +export const reportingSchema: MakeSchemaFrom = { + ...rangeStatsSchema, + available: { type: 'boolean' }, + browser_type: { type: 'keyword' }, + enabled: { type: 'boolean' }, + last7Days: rangeStatsSchema, +}; diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index 5430a1cfc33bd..1ff680eff8eaf 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -162,8 +162,3 @@ export interface SearchResponse { }; }; } - -export interface AvailableTotal { - available: boolean; - total: number; -} diff --git a/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx index 3141f5bedc8f9..7e7d74155b2d9 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx +++ b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx @@ -56,6 +56,12 @@ export const Editor = memo(({ licenseEnabled, initialValue, onEditorReady }: Pro setTextArea(licenseEnabled ? containerRef.current!.querySelector('textarea') : null); onEditorReady(createEditorShim(editorInstanceRef.current)); + + return () => { + if (editorInstanceRef.current) { + editorInstanceRef.current.destroy(); + } + }; }, [initialValue, onEditorReady, licenseEnabled]); return ( diff --git a/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts b/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts index 3ad92531e4367..b43506e1323da 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts +++ b/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts @@ -5,7 +5,7 @@ */ import ace from 'brace'; -import { installXJsonMode } from '../../../../../../src/plugins/es_ui_shared/public'; +import { installXJsonMode } from '@kbn/ace'; export function initializeEditor({ el, diff --git a/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts b/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts index 58a62c4636c25..7832d7bcb63f7 100644 --- a/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts +++ b/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { collapseLiteralStrings } from '../../../../../../src/plugins/es_ui_shared/public'; +import { XJson } from '../../../../../../src/plugins/es_ui_shared/public'; + +const { collapseLiteralStrings } = XJson; export function checkForParseErrors(json: string) { const sanitizedJson = collapseLiteralStrings(json); diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index 7ada34ff5ccac..86d1b68ba761e 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -609,22 +609,83 @@ describe('#find', () => { await expectGeneralError(client.find, { type: type1 }); }); - test(`throws decorated ForbiddenError when type's singular and unauthorized`, async () => { + test(`returns empty result when unauthorized`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( + getMockCheckPrivilegesFailure + ); + const options = Object.freeze({ type: type1, namespaces: ['some-ns'] }); - await expectForbiddenError(client.find, { options }); - }); + const result = await client.find(options); - test(`throws decorated ForbiddenError when type's an array and unauthorized`, async () => { - const options = Object.freeze({ type: [type1, type2], namespaces: ['some-ns'] }); - await expectForbiddenError(client.find, { options }); + expect(clientOpts.baseClient.find).not.toHaveBeenCalled(); + expect(clientOpts.auditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledTimes(1); + expect(clientOpts.auditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledWith( + USERNAME, + 'find', + [type1], + options.namespaces, + [{ spaceId: 'some-ns', privilege: 'mock-saved_object:foo/find' }], + { options } + ); + expect(result).toEqual({ page: 1, per_page: 20, total: 0, saved_objects: [] }); }); - test(`returns result of baseClient.find when authorized`, async () => { + test(`returns result of baseClient.find when fully authorized`, async () => { const apiCallReturnValue = { saved_objects: [], foo: 'bar' }; clientOpts.baseClient.find.mockReturnValue(apiCallReturnValue as any); const options = Object.freeze({ type: type1, namespaces: ['some-ns'] }); const result = await expectSuccess(client.find, { options }); + expect(clientOpts.baseClient.find.mock.calls[0][0]).toEqual({ + ...options, + typeToNamespacesMap: undefined, + }); + expect(result).toEqual(apiCallReturnValue); + }); + + test(`returns result of baseClient.find when partially authorized`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockResolvedValue({ + hasAllRequested: false, + username: USERNAME, + privileges: { + kibana: [ + { resource: 'some-ns', privilege: 'mock-saved_object:foo/find', authorized: true }, + { resource: 'some-ns', privilege: 'mock-saved_object:bar/find', authorized: true }, + { resource: 'some-ns', privilege: 'mock-saved_object:baz/find', authorized: false }, + { resource: 'some-ns', privilege: 'mock-saved_object:qux/find', authorized: false }, + { resource: 'another-ns', privilege: 'mock-saved_object:foo/find', authorized: true }, + { resource: 'another-ns', privilege: 'mock-saved_object:bar/find', authorized: false }, + { resource: 'another-ns', privilege: 'mock-saved_object:baz/find', authorized: true }, + { resource: 'another-ns', privilege: 'mock-saved_object:qux/find', authorized: false }, + { resource: 'forbidden-ns', privilege: 'mock-saved_object:foo/find', authorized: false }, + { resource: 'forbidden-ns', privilege: 'mock-saved_object:bar/find', authorized: false }, + { resource: 'forbidden-ns', privilege: 'mock-saved_object:baz/find', authorized: false }, + { resource: 'forbidden-ns', privilege: 'mock-saved_object:qux/find', authorized: false }, + ], + }, + }); + + const apiCallReturnValue = { saved_objects: [], foo: 'bar' }; + clientOpts.baseClient.find.mockReturnValue(apiCallReturnValue as any); + + const options = Object.freeze({ + type: ['foo', 'bar', 'baz', 'qux'], + namespaces: ['some-ns', 'another-ns', 'forbidden-ns'], + }); + const result = await client.find(options); + // 'expect(clientOpts.baseClient.find).toHaveBeenCalledWith' resulted in false negatives, resorting to manually comparing mock call args + expect(clientOpts.baseClient.find.mock.calls[0][0]).toEqual({ + ...options, + typeToNamespacesMap: new Map([ + ['foo', ['some-ns', 'another-ns']], + ['bar', ['some-ns']], + ['baz', ['another-ns']], + // qux is not authorized, so there is no entry for it + // forbidden-ns is completely forbidden, so there are no entries with this namespace + ]), + type: '', + namespaces: [], + }); expect(result).toEqual(apiCallReturnValue); }); diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts index 16e52c69f274f..f5de8f4b226f3 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts @@ -16,6 +16,7 @@ import { SavedObjectsUpdateOptions, SavedObjectsAddToNamespacesOptions, SavedObjectsDeleteFromNamespacesOptions, + SavedObjectsUtils, } from '../../../../../src/core/server'; import { SecurityAuditLogger } from '../audit'; import { Actions, CheckSavedObjectsPrivileges } from '../authorization'; @@ -39,8 +40,19 @@ interface SavedObjectsNamespaces { saved_objects: SavedObjectNamespaces[]; } -function uniq(arr: T[]): T[] { - return Array.from(new Set(arr)); +interface EnsureAuthorizedOptions { + args?: Record; + auditAction?: string; + requireFullAuthorization?: boolean; +} + +interface EnsureAuthorizedResult { + status: 'fully_authorized' | 'partially_authorized' | 'unauthorized'; + typeMap: Map; +} +interface EnsureAuthorizedTypeResult { + authorizedSpaces: string[]; + isGloballyAuthorized?: boolean; } export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContract { @@ -72,7 +84,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra attributes: T = {} as T, options: SavedObjectsCreateOptions = {} ) { - await this.ensureAuthorized(type, 'create', options.namespace, { type, attributes, options }); + const args = { type, attributes, options }; + await this.ensureAuthorized(type, 'create', options.namespace, { args }); const savedObject = await this.baseClient.create(type, attributes, options); return await this.redactSavedObjectNamespaces(savedObject); @@ -82,9 +95,12 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra objects: SavedObjectsCheckConflictsObject[] = [], options: SavedObjectsBaseOptions = {} ) { - const types = this.getUniqueObjectTypes(objects); const args = { objects, options }; - await this.ensureAuthorized(types, 'bulk_create', options.namespace, args, 'checkConflicts'); + const types = this.getUniqueObjectTypes(objects); + await this.ensureAuthorized(types, 'bulk_create', options.namespace, { + args, + auditAction: 'checkConflicts', + }); const response = await this.baseClient.checkConflicts(objects, options); return response; @@ -94,11 +110,12 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra objects: Array>, options: SavedObjectsBaseOptions = {} ) { + const args = { objects, options }; await this.ensureAuthorized( this.getUniqueObjectTypes(objects), 'bulk_create', options.namespace, - { objects, options } + { args } ); const response = await this.baseClient.bulkCreate(objects, options); @@ -106,7 +123,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra } public async delete(type: string, id: string, options: SavedObjectsBaseOptions = {}) { - await this.ensureAuthorized(type, 'delete', options.namespace, { type, id, options }); + const args = { type, id, options }; + await this.ensureAuthorized(type, 'delete', options.namespace, { args }); return await this.baseClient.delete(type, id, options); } @@ -121,9 +139,29 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra `_find across namespaces is not permitted when the Spaces plugin is disabled.` ); } - await this.ensureAuthorized(options.type, 'find', options.namespaces, { options }); + const args = { options }; + const { status, typeMap } = await this.ensureAuthorized( + options.type, + 'find', + options.namespaces, + { args, requireFullAuthorization: false } + ); + + if (status === 'unauthorized') { + // return empty response + return SavedObjectsUtils.createEmptyFindResponse(options); + } - const response = await this.baseClient.find(options); + const typeToNamespacesMap = Array.from(typeMap).reduce>( + (acc, [type, { authorizedSpaces, isGloballyAuthorized }]) => + isGloballyAuthorized ? acc.set(type, options.namespaces) : acc.set(type, authorizedSpaces), + new Map() + ); + const response = await this.baseClient.find({ + ...options, + typeToNamespacesMap: undefined, // if the user is fully authorized, use `undefined` as the typeToNamespacesMap to prevent privilege escalation + ...(status === 'partially_authorized' && { typeToNamespacesMap, type: '', namespaces: [] }), // the repository requires that `type` and `namespaces` must be empty if `typeToNamespacesMap` is defined + }); return await this.redactSavedObjectsNamespaces(response); } @@ -131,9 +169,9 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra objects: SavedObjectsBulkGetObject[] = [], options: SavedObjectsBaseOptions = {} ) { + const args = { objects, options }; await this.ensureAuthorized(this.getUniqueObjectTypes(objects), 'bulk_get', options.namespace, { - objects, - options, + args, }); const response = await this.baseClient.bulkGet(objects, options); @@ -141,7 +179,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra } public async get(type: string, id: string, options: SavedObjectsBaseOptions = {}) { - await this.ensureAuthorized(type, 'get', options.namespace, { type, id, options }); + const args = { type, id, options }; + await this.ensureAuthorized(type, 'get', options.namespace, { args }); const savedObject = await this.baseClient.get(type, id, options); return await this.redactSavedObjectNamespaces(savedObject); @@ -154,7 +193,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra options: SavedObjectsUpdateOptions = {} ) { const args = { type, id, attributes, options }; - await this.ensureAuthorized(type, 'update', options.namespace, args); + await this.ensureAuthorized(type, 'update', options.namespace, { args }); const savedObject = await this.baseClient.update(type, id, attributes, options); return await this.redactSavedObjectNamespaces(savedObject); @@ -169,13 +208,19 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra const args = { type, id, namespaces, options }; const { namespace } = options; // To share an object, the user must have the "create" permission in each of the destination namespaces. - await this.ensureAuthorized(type, 'create', namespaces, args, 'addToNamespacesCreate'); + await this.ensureAuthorized(type, 'create', namespaces, { + args, + auditAction: 'addToNamespacesCreate', + }); // To share an object, the user must also have the "update" permission in one or more of the source namespaces. Because the // `addToNamespaces` operation is scoped to the current namespace, we can just check if the user has the "update" permission in the // current namespace. If the user has permission, but the saved object doesn't exist in this namespace, the base client operation will // result in a 404 error. - await this.ensureAuthorized(type, 'update', namespace, args, 'addToNamespacesUpdate'); + await this.ensureAuthorized(type, 'update', namespace, { + args, + auditAction: 'addToNamespacesUpdate', + }); const result = await this.baseClient.addToNamespaces(type, id, namespaces, options); return await this.redactSavedObjectNamespaces(result); @@ -189,7 +234,10 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra ) { const args = { type, id, namespaces, options }; // To un-share an object, the user must have the "delete" permission in each of the target namespaces. - await this.ensureAuthorized(type, 'delete', namespaces, args, 'deleteFromNamespaces'); + await this.ensureAuthorized(type, 'delete', namespaces, { + args, + auditAction: 'deleteFromNamespaces', + }); const result = await this.baseClient.deleteFromNamespaces(type, id, namespaces, options); return await this.redactSavedObjectNamespaces(result); @@ -205,9 +253,9 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra .filter(({ namespace }) => namespace !== undefined) .map(({ namespace }) => namespace!); const namespaces = [options?.namespace, ...objectNamespaces]; + const args = { objects, options }; await this.ensureAuthorized(this.getUniqueObjectTypes(objects), 'bulk_update', namespaces, { - objects, - options, + args, }); const response = await this.baseClient.bulkUpdate(objects, options); @@ -228,11 +276,10 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra private async ensureAuthorized( typeOrTypes: string | string[], action: string, - namespaceOrNamespaces?: string | Array, - args?: Record, - auditAction: string = action, - requiresAll = true - ) { + namespaceOrNamespaces: undefined | string | Array, + options: EnsureAuthorizedOptions = {} + ): Promise { + const { args, auditAction = action, requireFullAuthorization = true } = options; const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes]; const actionsToTypesMap = new Map( types.map((type) => [this.actions.savedObject.get(type, action), type]) @@ -245,27 +292,60 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra privileges.kibana.map(({ resource }) => resource).filter((x) => x !== undefined) ).sort() as string[]; - const isAuthorized = - (requiresAll && hasAllRequested) || - (!requiresAll && privileges.kibana.some(({ authorized }) => authorized)); - if (isAuthorized) { - this.auditLogger.savedObjectsAuthorizationSuccess( + const missingPrivileges = this.getMissingPrivileges(privileges); + const typeMap = privileges.kibana.reduce>( + (acc, { resource, privilege, authorized }) => { + if (!authorized) { + return acc; + } + const type = actionsToTypesMap.get(privilege)!; // always defined + const value = acc.get(type) ?? { authorizedSpaces: [] }; + if (resource === undefined) { + return acc.set(type, { ...value, isGloballyAuthorized: true }); + } + const authorizedSpaces = value.authorizedSpaces.concat(resource); + return acc.set(type, { ...value, authorizedSpaces }); + }, + new Map() + ); + + const logAuthorizationFailure = () => { + this.auditLogger.savedObjectsAuthorizationFailure( username, auditAction, types, spaceIds, + missingPrivileges, args ); - } else { - const missingPrivileges = this.getMissingPrivileges(privileges); - this.auditLogger.savedObjectsAuthorizationFailure( + }; + const logAuthorizationSuccess = (typeArray: string[], spaceIdArray: string[]) => { + this.auditLogger.savedObjectsAuthorizationSuccess( username, auditAction, - types, - spaceIds, - missingPrivileges, + typeArray, + spaceIdArray, args ); + }; + + if (hasAllRequested) { + logAuthorizationSuccess(types, spaceIds); + return { typeMap, status: 'fully_authorized' }; + } else if (!requireFullAuthorization) { + const isPartiallyAuthorized = privileges.kibana.some(({ authorized }) => authorized); + if (isPartiallyAuthorized) { + for (const [type, { isGloballyAuthorized, authorizedSpaces }] of typeMap.entries()) { + // generate an individual audit record for each authorized type + logAuthorizationSuccess([type], isGloballyAuthorized ? spaceIds : authorizedSpaces); + } + return { typeMap, status: 'partially_authorized' }; + } else { + logAuthorizationFailure(); + return { typeMap, status: 'unauthorized' }; + } + } else { + logAuthorizationFailure(); const targetTypes = uniq( missingPrivileges.map(({ privilege }) => actionsToTypesMap.get(privilege)).sort() ).join(','); @@ -303,19 +383,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra } private redactAndSortNamespaces(spaceIds: string[], privilegeMap: Record) { - const comparator = (a: string, b: string) => { - const _a = a.toLowerCase(); - const _b = b.toLowerCase(); - if (_a === '?') { - return 1; - } else if (_a < _b) { - return -1; - } else if (_a > _b) { - return 1; - } - return 0; - }; - return spaceIds.map((spaceId) => (privilegeMap[spaceId] ? spaceId : '?')).sort(comparator); + return spaceIds.map((x) => (privilegeMap[x] ? x : '?')).sort(namespaceComparator); } private async redactSavedObjectNamespaces( @@ -362,3 +430,25 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra }; } } + +/** + * Returns all unique elements of an array. + */ +function uniq(arr: T[]): T[] { + return Array.from(new Set(arr)); +} + +/** + * Utility function to sort potentially redacted namespaces. + * Sorts in a case-insensitive manner, and ensures that redacted namespaces ('?') always show up at the end of the array. + */ +function namespaceComparator(a: string, b: string) { + const A = a.toUpperCase(); + const B = b.toUpperCase(); + if (A === '?' && B !== '?') { + return 1; + } else if (A !== '?' && B === '?') { + return -1; + } + return A > B ? 1 : A < B ? -1 : 0; +} diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts index 11e58f7f95fc2..90483d7c0a4d5 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts @@ -62,10 +62,16 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens type: 'number', }, enabledAuthProviders: { - type: 'keyword', + type: 'array', + items: { + type: 'keyword', + }, }, httpAuthSchemes: { - type: 'keyword', + type: 'array', + items: { + type: 'keyword', + }, }, }, fetch: () => { diff --git a/x-pack/plugins/security_solution/.storybook/main.js b/x-pack/plugins/security_solution/.storybook/main.js new file mode 100644 index 0000000000000..1818aa44a9399 --- /dev/null +++ b/x-pack/plugins/security_solution/.storybook/main.js @@ -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. + */ + +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 9321aa769423f..2910f02a187f4 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -11,7 +11,6 @@ export const APP_ICON = 'securityAnalyticsApp'; export const APP_ICON_SOLUTION = 'logoSecurity'; export const APP_PATH = `/app/security`; export const ADD_DATA_PATH = `/app/home#/tutorial_directory/security`; -export const ADD_INDEX_PATH = `/app/management/kibana/indexPatterns/create`; export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern'; export const DEFAULT_DATE_FORMAT = 'dateFormat'; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz'; @@ -35,6 +34,7 @@ export const DEFAULT_INTERVAL_TYPE = 'manual'; export const DEFAULT_INTERVAL_VALUE = 300000; // ms export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges'; export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled'; +export const GLOBAL_HEADER_HEIGHT = 98; // px export const FILTERS_GLOBAL_HEIGHT = 109; // px export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled'; export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51'; @@ -58,6 +58,8 @@ export const APP_TIMELINES_PATH = `${APP_PATH}/timelines`; export const APP_CASES_PATH = `${APP_PATH}/cases`; export const APP_MANAGEMENT_PATH = `${APP_PATH}/administration`; +export const DETECTIONS_SUB_PLUGIN_ID = `${APP_ID}:${SecurityPageName.detections}`; + /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts index 72ef230a42342..0224caafb41a8 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getQueryFilter, buildExceptionFilter } from './get_query_filter'; +import { getQueryFilter, buildExceptionFilter, buildEqlSearchRequest } from './get_query_filter'; import { Filter, EsQueryConfig } from 'src/plugins/data/public'; import { getExceptionListItemSchemaMock } from '../../../lists/common/schemas/response/exception_list_item_schema.mock'; @@ -1085,4 +1085,138 @@ describe('get_filter', () => { }); }); }); + + describe('buildEqlSearchRequest', () => { + test('should build a basic request with time range', () => { + const request = buildEqlSearchRequest( + 'process where true', + ['testindex1', 'testindex2'], + 'now-5m', + 'now', + 100, + undefined, + [], + undefined + ); + expect(request).toEqual({ + method: 'POST', + path: `/testindex1,testindex2/_eql/search?allow_no_indices=true`, + body: { + size: 100, + query: 'process where true', + filter: { + range: { + '@timestamp': { + gte: 'now-5m', + lte: 'now', + }, + }, + }, + }, + }); + }); + + test('should build a request with timestamp and event category overrides', () => { + const request = buildEqlSearchRequest( + 'process where true', + ['testindex1', 'testindex2'], + 'now-5m', + 'now', + 100, + 'event.ingested', + [], + 'event.other_category' + ); + expect(request).toEqual({ + method: 'POST', + path: `/testindex1,testindex2/_eql/search?allow_no_indices=true`, + event_category_field: 'event.other_category', + body: { + size: 100, + query: 'process where true', + filter: { + range: { + 'event.ingested': { + gte: 'now-5m', + lte: 'now', + }, + }, + }, + }, + }); + }); + + test('should build a request with exceptions', () => { + const request = buildEqlSearchRequest( + 'process where true', + ['testindex1', 'testindex2'], + 'now-5m', + 'now', + 100, + undefined, + [getExceptionListItemSchemaMock()], + undefined + ); + expect(request).toEqual({ + method: 'POST', + path: `/testindex1,testindex2/_eql/search?allow_no_indices=true`, + body: { + size: 100, + query: 'process where true', + filter: { + range: { + '@timestamp': { + gte: 'now-5m', + lte: 'now', + }, + }, + bool: { + must_not: { + bool: { + should: [ + { + bool: { + filter: [ + { + nested: { + path: 'some.parentField', + query: { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + 'some.parentField.nested.field': 'some value', + }, + }, + ], + }, + }, + score_mode: 'none', + }, + }, + { + bool: { + minimum_should_match: 1, + should: [ + { + match_phrase: { + 'some.not.nested.field': 'some value', + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts index 466a004c14c66..05c706164ab44 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/get_query_filter.ts @@ -17,7 +17,12 @@ import { CreateExceptionListItemSchema, } from '../../../lists/common/schemas'; import { buildExceptionListQueries } from './build_exceptions_query'; -import { Query as QueryString, Language, Index } from './schemas/common/schemas'; +import { + Query as QueryString, + Language, + Index, + TimestampOverrideOrUndefined, +} from './schemas/common/schemas'; export const getQueryFilter = ( query: QueryString, @@ -67,6 +72,78 @@ export const getQueryFilter = ( return buildEsQuery(indexPattern, initialQuery, enabledFilters, config); }; +interface EqlSearchRequest { + method: string; + path: string; + body: object; + event_category_field?: string; +} + +export const buildEqlSearchRequest = ( + query: string, + index: string[], + from: string, + to: string, + size: number, + timestampOverride: TimestampOverrideOrUndefined, + exceptionLists: ExceptionListItemSchema[], + eventCategoryOverride: string | undefined +): EqlSearchRequest => { + const timestamp = timestampOverride ?? '@timestamp'; + const indexPattern: IIndexPattern = { + fields: [], + title: index.join(), + }; + const config: EsQueryConfig = { + allowLeadingWildcards: true, + queryStringOptions: { analyze_wildcard: true }, + ignoreFilterIfFieldNotInIndex: false, + dateFormatTZ: 'Zulu', + }; + const exceptionQueries = buildExceptionListQueries({ language: 'kuery', lists: exceptionLists }); + let exceptionFilter: Filter | undefined; + if (exceptionQueries.length > 0) { + // Assume that `indices.query.bool.max_clause_count` is at least 1024 (the default value), + // allowing us to make 1024-item chunks of exception list items. + // Discussion at https://issues.apache.org/jira/browse/LUCENE-4835 indicates that 1024 is a + // very conservative value. + exceptionFilter = buildExceptionFilter(exceptionQueries, indexPattern, config, true, 1024); + } + const indexString = index.join(); + const baseRequest = { + method: 'POST', + path: `/${indexString}/_eql/search?allow_no_indices=true`, + body: { + size, + query, + filter: { + range: { + [timestamp]: { + gte: from, + lte: to, + }, + }, + bool: + exceptionFilter !== undefined + ? { + must_not: { + bool: exceptionFilter?.query.bool, + }, + } + : undefined, + }, + }, + }; + if (eventCategoryOverride) { + return { + ...baseRequest, + event_category_field: eventCategoryOverride, + }; + } else { + return baseRequest; + } +}; + export const buildExceptionFilter = ( exceptionQueries: Query[], indexPattern: IIndexPattern, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index 5fbba84467ecf..e8d7f409de20a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -40,6 +40,12 @@ export type Enabled = t.TypeOf; export const enabledOrUndefined = t.union([enabled, t.undefined]); export type EnabledOrUndefined = t.TypeOf; +export const event_category_override = t.string; +export type EventCategoryOverride = t.TypeOf; + +export const eventCategoryOverrideOrUndefined = t.union([event_category_override, t.undefined]); +export type EventCategoryOverrideOrUndefined = t.TypeOf; + export const false_positives = t.array(t.string); export type FalsePositives = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts index 69538f025d95d..3f338c57dd930 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts @@ -44,6 +44,7 @@ import { Author, RiskScoreMapping, SeverityMapping, + event_category_override, } from '../common/schemas'; import { threat_index, @@ -96,6 +97,7 @@ export const addPrepackagedRulesSchema = t.intersection([ author: DefaultStringArray, // defaults to empty array of strings if not set during decode building_block_type, // defaults to undefined if not set during decode enabled: DefaultBooleanFalse, // defaults to false if not set during decode + event_category_override, // defaults to "undefined" if not set during decode false_positives: DefaultStringArray, // defaults to empty string array if not set during decode filters, // defaults to undefined if not set during decode from: DefaultFromString, // defaults to "now-6m" if not set during decode diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts index c024ba1c48f8d..2489210a26c8f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts @@ -45,6 +45,7 @@ import { Author, RiskScoreMapping, SeverityMapping, + event_category_override, } from '../common/schemas'; import { threat_index, @@ -88,6 +89,7 @@ export const createRulesSchema = t.intersection([ author: DefaultStringArray, // defaults to empty array of strings if not set during decode building_block_type, // defaults to undefined if not set during decode enabled: DefaultBooleanTrue, // defaults to true if not set during decode + event_category_override, // defaults to "undefined" if not set during decode false_positives: DefaultStringArray, // defaults to empty string array if not set during decode filters, // defaults to undefined if not set during decode from: DefaultFromString, // defaults to "now-6m" if not set during decode diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts index b63d70783b7b5..a411b3d439a1f 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts @@ -51,6 +51,7 @@ import { Author, RiskScoreMapping, SeverityMapping, + event_category_override, } from '../common/schemas'; import { threat_index, @@ -107,6 +108,7 @@ export const importRulesSchema = t.intersection([ author: DefaultStringArray, // defaults to empty array of strings if not set during decode building_block_type, // defaults to undefined if not set during decode enabled: DefaultBooleanTrue, // defaults to true if not set during decode + event_category_override, // defaults to "undefined" if not set during decode false_positives: DefaultStringArray, // defaults to empty string array if not set during decode filters, // defaults to undefined if not set during decode from: DefaultFromString, // defaults to "now-6m" if not set during decode diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts index a674ac86af87b..40e79d96a9e6b 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts @@ -46,6 +46,7 @@ import { timestamp_override, risk_score_mapping, severity_mapping, + event_category_override, } from '../common/schemas'; import { listArrayOrUndefined } from '../types/lists'; @@ -65,6 +66,7 @@ export const patchRulesSchema = t.exact( actions, anomaly_threshold, enabled, + event_category_override, false_positives, filters, from, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts index 1299dada065e1..8a13dd2f4e908 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts @@ -47,6 +47,7 @@ import { Author, RiskScoreMapping, SeverityMapping, + event_category_override, } from '../common/schemas'; import { @@ -90,6 +91,7 @@ export const updateRulesSchema = t.intersection([ author: DefaultStringArray, // defaults to empty array of strings if not set during decode building_block_type, // defaults to undefined if not set during decode enabled: DefaultBooleanTrue, // defaults to true if not set during decode + event_category_override, false_positives: DefaultStringArray, // defaults to empty string array if not set during decode filters, // defaults to undefined if not set during decode from: DefaultFromString, // defaults to "now-6m" if not set during decode diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts index a462b297d37f8..aaa246c82d9d7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts @@ -52,7 +52,7 @@ export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): RulesSchem severity: 'high', severity_mapping: [], updated_by: 'elastic_kibana', - tags: [], + tags: ['some fake tag 1', 'some fake tag 2'], to: 'now', type: 'query', threat: [], @@ -61,7 +61,7 @@ export const getRulesSchemaMock = (anchorDate: string = ANCHOR_DATE): RulesSchem status_date: '2020-02-22T16:47:50.047Z', last_success_at: '2020-02-22T16:47:50.047Z', last_success_message: 'succeeded', - output_index: '.siem-signals-hassanabad-frank-default', + output_index: '.siem-signals-default', max_signals: 100, risk_score: 55, risk_score_mapping: [], @@ -110,3 +110,12 @@ export const getThreatMatchingSchemaMock = (anchorDate: string = ANCHOR_DATE): R ], }; }; + +export const getRulesEqlSchemaMock = (anchorDate: string = ANCHOR_DATE): RulesSchema => { + return { + ...getRulesSchemaMock(anchorDate), + language: 'eql', + type: 'eql', + query: 'process where true', + }; +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts index 3a47d4af6ac14..c5bad3c55066b 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts @@ -18,6 +18,7 @@ import { addTimelineTitle, addMlFields, addThreatMatchFields, + addEqlFields, } from './rules_schema'; import { exactCheck } from '../../../exact_check'; import { foldLeftRight, getPaths } from '../../../test_utils'; @@ -26,6 +27,7 @@ import { getRulesSchemaMock, getRulesMlSchemaMock, getThreatMatchingSchemaMock, + getRulesEqlSchemaMock, } from './rules_schema.mocks'; import { ListArray } from '../types/lists'; @@ -628,6 +630,19 @@ describe('rules_schema', () => { ]); expect(message.schema).toEqual({}); }); + + test('it validates an eql rule response', () => { + const payload = getRulesEqlSchemaMock(); + + const dependents = getDependents(payload); + const decoded = dependents.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + const expected = getRulesEqlSchemaMock(); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(expected); + }); }); describe('addSavedId', () => { @@ -668,11 +683,6 @@ describe('rules_schema', () => { expect(fields.length).toEqual(2); }); - test('should return two fields for a rule of type "eql"', () => { - const fields = addQueryFields({ type: 'eql' }); - expect(fields.length).toEqual(2); - }); - test('should return two fields for a rule of type "threshold"', () => { const fields = addQueryFields({ type: 'threshold' }); expect(fields.length).toEqual(2); @@ -757,4 +767,17 @@ describe('rules_schema', () => { expect(fields.length).toEqual(5); }); }); + + describe('addEqlFields', () => { + test('should return empty array if type is not "eql"', () => { + const fields = addEqlFields({ type: 'query' }); + const expected: t.Mixed[] = []; + expect(fields).toEqual(expected); + }); + + test('should return 3 fields for a rule of type "eql"', () => { + const fields = addEqlFields({ type: 'eql' }); + expect(fields.length).toEqual(3); + }); + }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts index 1c2254f9f8f09..908425a7496d0 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts @@ -16,6 +16,7 @@ import { anomaly_threshold, description, enabled, + event_category_override, false_positives, from, id, @@ -121,6 +122,9 @@ export const dependentRulesSchema = t.partial({ language, query, + // eql only fields + event_category_override, + // when type = saved_query, saved_id is required saved_id, @@ -219,9 +223,7 @@ export const addTimelineTitle = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mi }; export const addQueryFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { - if ( - ['eql', 'query', 'saved_query', 'threshold', 'threat_match'].includes(typeAndTimelineOnly.type) - ) { + if (['query', 'saved_query', 'threshold', 'threat_match'].includes(typeAndTimelineOnly.type)) { return [ t.exact(t.type({ query: dependentRulesSchema.props.query })), t.exact(t.type({ language: dependentRulesSchema.props.language })), @@ -255,6 +257,20 @@ export const addThresholdFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t. } }; +export const addEqlFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { + if (typeAndTimelineOnly.type === 'eql') { + return [ + t.exact( + t.partial({ event_category_override: dependentRulesSchema.props.event_category_override }) + ), + t.exact(t.type({ query: dependentRulesSchema.props.query })), + t.exact(t.type({ language: dependentRulesSchema.props.language })), + ]; + } else { + return []; + } +}; + export const addThreatMatchFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed[] => { if (typeAndTimelineOnly.type === 'threat_match') { return [ @@ -278,6 +294,7 @@ export const getDependents = (typeAndTimelineOnly: TypeAndTimelineOnly): t.Mixed ...addQueryFields(typeAndTimelineOnly), ...addMlFields(typeAndTimelineOnly), ...addThresholdFields(typeAndTimelineOnly), + ...addEqlFields(typeAndTimelineOnly), ...addThreatMatchFields(typeAndTimelineOnly), ]; diff --git a/x-pack/plugins/security_solution/common/ecs/file/index.ts b/x-pack/plugins/security_solution/common/ecs/file/index.ts index 808e9eaa3c854..dd96ac0104851 100644 --- a/x-pack/plugins/security_solution/common/ecs/file/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/file/index.ts @@ -4,6 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +export interface CodeSignature { + subject_name: string[]; + trusted: string[]; +} +export interface Ext { + code_signature: CodeSignature[] | CodeSignature; +} +export interface Hash { + sha256: string[]; +} + export interface FileEcs { name?: string[]; @@ -13,6 +24,8 @@ export interface FileEcs { extension?: string[]; + Ext?: Ext; + type?: string[]; device?: string[]; @@ -34,4 +47,6 @@ export interface FileEcs { mtime?: string[]; ctime?: string[]; + + hash?: Hash; } diff --git a/x-pack/plugins/security_solution/common/ecs/network/index.ts b/x-pack/plugins/security_solution/common/ecs/network/index.ts index c2fc3cb4b9f48..18f7583d12231 100644 --- a/x-pack/plugins/security_solution/common/ecs/network/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/network/index.ts @@ -6,14 +6,9 @@ export interface NetworkEcs { bytes?: number[]; - community_id?: string[]; - direction?: string[]; - packets?: number[]; - protocol?: string[]; - transport?: string[]; } diff --git a/x-pack/plugins/security_solution/common/ecs/rule/index.ts b/x-pack/plugins/security_solution/common/ecs/rule/index.ts index 47316c7791e4b..fa200b46b37b4 100644 --- a/x-pack/plugins/security_solution/common/ecs/rule/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/rule/index.ts @@ -7,6 +7,7 @@ export interface RuleEcs { id?: string[]; rule_id?: string[]; + name?: string[]; false_positives: string[]; saved_id?: string[]; timeline_id?: string[]; diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index 74ccf9105ba6b..4cfa9347b2b58 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -7,8 +7,8 @@ export const eventsIndexPattern = 'logs-endpoint.events.*'; export const alertsIndexPattern = 'logs-endpoint.alerts-*'; export const metadataIndexPattern = 'metrics-endpoint.metadata-*'; -export const metadataCurrentIndexPattern = 'metrics-endpoint.metadata_current-*'; -export const metadataTransformPrefix = 'metrics-endpoint.metadata-current-default'; +export const metadataCurrentIndexPattern = 'metrics-endpoint.metadata_current_*'; +export const metadataTransformPrefix = 'endpoint.metadata_current-default'; export const policyIndexPattern = 'metrics-endpoint.policy-*'; export const telemetryIndexPattern = 'metrics-endpoint.telemetry-*'; export const LIMITED_CONCURRENCY_ENDPOINT_ROUTE_TAG = 'endpoint:limited-concurrency'; diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 7f31c71fe712b..abdbbf1986bcd 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -67,7 +67,7 @@ const Windows: OSFields[] = [ full: 'Windows 10', version: '10.0', platform: 'Windows', - family: 'Windows', + family: 'windows', Ext: { variant: 'Windows Pro', }, @@ -77,7 +77,7 @@ const Windows: OSFields[] = [ full: 'Windows Server 2016', version: '10.0', platform: 'Windows', - family: 'Windows', + family: 'windows', Ext: { variant: 'Windows Server', }, @@ -87,7 +87,7 @@ const Windows: OSFields[] = [ full: 'Windows Server 2012', version: '6.2', platform: 'Windows', - family: 'Windows', + family: 'windows', Ext: { variant: 'Windows Server', }, @@ -97,7 +97,7 @@ const Windows: OSFields[] = [ full: 'Windows Server 2012R2', version: '6.3', platform: 'Windows', - family: 'Windows', + family: 'windows', Ext: { variant: 'Windows Server Release 2', }, diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts index 2b0aa1601ab37..fed32293e00a1 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { EndpointDocGenerator } from '../generate_data'; -import { descriptiveName, isProcessRunning } from './event'; -import { ResolverEvent, SafeResolverEvent } from '../types'; +import { isProcessRunning } from './event'; +import { SafeResolverEvent } from '../types'; describe('Generated documents', () => { let generator: EndpointDocGenerator; @@ -13,50 +13,6 @@ describe('Generated documents', () => { generator = new EndpointDocGenerator('seed'); }); - describe('Event descriptive names', () => { - it('returns the right name for a registry event', () => { - const extensions = { registry: { key: `HKLM/Windows/Software/abc` } }; - const event = generator.generateEvent({ eventCategory: 'registry', extensions }); - // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies - // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. - expect(descriptiveName(event as ResolverEvent)).toEqual({ - subject: `HKLM/Windows/Software/abc`, - }); - }); - - it('returns the right name for a network event', () => { - const randomIP = `${generator.randomIP()}`; - const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } }; - const event = generator.generateEvent({ eventCategory: 'network', extensions }); - // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies - // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. - expect(descriptiveName(event as ResolverEvent)).toEqual({ - subject: `${randomIP}`, - descriptor: 'outbound', - }); - }); - - it('returns the right name for a file event', () => { - const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } }; - const event = generator.generateEvent({ eventCategory: 'file', extensions }); - // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies - // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. - expect(descriptiveName(event as ResolverEvent)).toEqual({ - subject: 'C:\\My Documents\\business\\January\\processName', - }); - }); - - it('returns the right name for a dns event', () => { - const extensions = { dns: { question: { name: `${generator.randomIP()}` } } }; - const event = generator.generateEvent({ eventCategory: 'dns', extensions }); - // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies - // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. - expect(descriptiveName(event as ResolverEvent)).toEqual({ - subject: extensions.dns.question.name, - }); - }); - }); - describe('Process running events', () => { it('is a running event when event.type is a string', () => { const event: SafeResolverEvent = generator.generateEvent({ diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 9634659b1a5dd..3e39ed6eb7a69 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -104,11 +104,17 @@ export function timestampAsDateSafeVersion(event: TimestampFields): Date | undef } } -export function eventTimestamp(event: ResolverEvent): string | undefined | number { - return event['@timestamp']; +/** + * The @timestamp ECS field + */ +export function eventTimestamp(event: SafeResolverEvent): string | undefined | number { + return firstNonNullValue(event['@timestamp']); } -export function eventName(event: ResolverEvent): string { +/** + * Find the name of the related process. + */ +export function processName(event: ResolverEvent): string { if (isLegacyEvent(event)) { return event.endgame.process_name ? event.endgame.process_name : ''; } else { @@ -116,6 +122,58 @@ export function eventName(event: ResolverEvent): string { } } +/** + * First non-null value in the `user.name` field. + */ +export function userName(event: SafeResolverEvent): string | undefined { + if (isLegacyEventSafeVersion(event)) { + return undefined; + } else { + return firstNonNullValue(event.user?.name); + } +} + +/** + * Returns the process event's parent PID + */ +export function parentPID(event: SafeResolverEvent): number | undefined { + return firstNonNullValue( + isLegacyEventSafeVersion(event) ? event.endgame.ppid : event.process?.parent?.pid + ); +} + +/** + * First non-null value for the `process.hash.md5` field. + */ +export function md5HashForProcess(event: SafeResolverEvent): string | undefined { + return firstNonNullValue(isLegacyEventSafeVersion(event) ? undefined : event.process?.hash?.md5); +} + +/** + * First non-null value for the `event.process.args` field. + */ +export function argsForProcess(event: SafeResolverEvent): string | undefined { + if (isLegacyEventSafeVersion(event)) { + // There is not currently a key for this on Legacy event types + return undefined; + } + return firstNonNullValue(event.process?.args); +} + +/** + * First non-null value in the `user.name` field. + */ +export function userDomain(event: SafeResolverEvent): string | undefined { + if (isLegacyEventSafeVersion(event)) { + return undefined; + } else { + return firstNonNullValue(event.user?.domain); + } +} + +/** + * Find the name of the related process. + */ export function processNameSafeVersion(event: SafeResolverEvent): string | undefined { if (isLegacyEventSafeVersion(event)) { return firstNonNullValue(event.endgame.process_name); @@ -124,11 +182,10 @@ export function processNameSafeVersion(event: SafeResolverEvent): string | undef } } -export function eventId(event: ResolverEvent): number | undefined | string { - if (isLegacyEvent(event)) { - return event.endgame.serial_event_id; - } - return event.event.id; +export function eventID(event: SafeResolverEvent): number | undefined | string { + return firstNonNullValue( + isLegacyEventSafeVersion(event) ? event.endgame.serial_event_id : event.event?.id + ); } /** @@ -159,12 +216,18 @@ export function eventSequence(event: EventSequenceFields): number | undefined { return firstNonNullValue(event.event?.sequence); } +/** + * The event.id ECS field. + */ export function eventIDSafeVersion(event: SafeResolverEvent): number | undefined | string { return firstNonNullValue( isLegacyEventSafeVersion(event) ? event.endgame?.serial_event_id : event.event?.id ); } +/** + * The event.entity_id field. + */ export function entityId(event: ResolverEvent): string { if (isLegacyEvent(event)) { return event.endgame.unique_pid ? String(event.endgame.unique_pid) : ''; @@ -204,6 +267,9 @@ export function entityIDSafeVersion(event: EntityIDFields): string | undefined { } } +/** + * The process.parent.entity_id ECS field. + */ export function parentEntityId(event: ResolverEvent): string | undefined { if (isLegacyEvent(event)) { return event.endgame.unique_ppid ? String(event.endgame.unique_ppid) : undefined; @@ -275,18 +341,14 @@ export function ancestryArray(event: AncestryArrayFields): string[] | undefined /** * Minimum fields needed from the `SafeResolverEvent` type for the function below to operate correctly. */ -type GetAncestryArrayFields = AncestryArrayFields & ParentEntityIDFields; +type AncestryFields = AncestryArrayFields & ParentEntityIDFields; /** * Returns an array of strings representing the ancestry for a process. * * @param event an ES document */ -export function getAncestryAsArray(event: GetAncestryArrayFields | undefined): string[] { - if (!event) { - return []; - } - +export function ancestry(event: AncestryFields): string[] { const ancestors = ancestryArray(event); if (ancestors) { return ancestors; @@ -300,35 +362,13 @@ export function getAncestryAsArray(event: GetAncestryArrayFields | undefined): s return []; } -/** - * @param event The event to get the category for - */ -export function primaryEventCategory(event: ResolverEvent): string | undefined { - if (isLegacyEvent(event)) { - const legacyFullType = event.endgame.event_type_full; - if (legacyFullType) { - return legacyFullType; - } - } else { - const eventCategories = event.event.category; - const category = typeof eventCategories === 'string' ? eventCategories : eventCategories[0]; - - return category; - } -} - /** * @param event The event to get the full ECS category for */ -export function allEventCategories(event: ResolverEvent): string | string[] | undefined { - if (isLegacyEvent(event)) { - const legacyFullType = event.endgame.event_type_full; - if (legacyFullType) { - return legacyFullType; - } - } else { - return event.event.category; - } +export function eventCategory(event: SafeResolverEvent): string[] { + return values( + isLegacyEventSafeVersion(event) ? event.endgame.event_type_full : event.event?.category + ); } /** @@ -336,71 +376,19 @@ export function allEventCategories(event: ResolverEvent): string | string[] | un * see: https://www.elastic.co/guide/en/ecs/current/ecs-event.html * @param event The ResolverEvent to get the ecs type for */ -export function ecsEventType(event: ResolverEvent): Array { - if (isLegacyEvent(event)) { - return [event.endgame.event_subtype_full]; - } - return typeof event.event.type === 'string' ? [event.event.type] : event.event.type; +export function eventType(event: SafeResolverEvent): string[] { + return values( + isLegacyEventSafeVersion(event) ? event.endgame.event_subtype_full : event.event?.type + ); } /** - * #Descriptive Names For Related Events: - * - * The following section provides facilities for deriving **Descriptive Names** for ECS-compliant event data. - * There are drawbacks to trying to do this: It *will* require ongoing maintenance. It presents temptations to overarticulate. - * On balance, however, it seems that the benefit of giving the user some form of information they can recognize & scan outweighs the drawbacks. + * event.kind as an array. */ -type DeepPartial = T extends object ? { [K in keyof T]?: DeepPartial } : T; -/** - * Based on the ECS category of the event, attempt to provide a more descriptive name - * (e.g. the `event.registry.key` for `registry` or the `dns.question.name` for `dns`, etc.). - * This function returns the data in the form of `{subject, descriptor}` where `subject` will - * tend to be the more distinctive term (e.g. 137.213.212.7 for a network event) and the - * `descriptor` can be used to present more useful/meaningful view (e.g. `inbound 137.213.212.7` - * in the example above). - * see: https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html - * @param event The ResolverEvent to get the descriptive name for - * @returns { descriptiveName } An attempt at providing a readable name to the user - */ -export function descriptiveName(event: ResolverEvent): { subject: string; descriptor?: string } { - if (isLegacyEvent(event)) { - return { subject: eventName(event) }; - } - - // To be somewhat defensive, we'll check for the presence of these. - const partialEvent: DeepPartial = event; - - /** - * This list of attempts can be expanded/adjusted as the underlying model changes over time: - */ - - // Stable, per ECS 1.5: https://www.elastic.co/guide/en/ecs/current/ecs-allowed-values-event-category.html - - if (partialEvent.network?.forwarded_ip) { - return { - subject: String(partialEvent.network?.forwarded_ip), - descriptor: String(partialEvent.network?.direction), - }; - } - - if (partialEvent.file?.path) { - return { - subject: String(partialEvent.file?.path), - }; - } - - // Extended categories (per ECS 1.5): - const pathOrKey = partialEvent.registry?.path || partialEvent.registry?.key; - if (pathOrKey) { - return { - subject: String(pathOrKey), - }; - } - - if (partialEvent.dns?.question?.name) { - return { subject: String(partialEvent.dns?.question?.name) }; +export function eventKind(event: SafeResolverEvent): string[] { + if (isLegacyEventSafeVersion(event)) { + return []; + } else { + return values(event.event?.kind); } - - // Fall back on entityId if we can't fish a more descriptive name out. - return { subject: entityId(event) }; } diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts index 311aa0c04c9ab..a6d59615794a6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/resolver.ts @@ -26,7 +26,7 @@ export const validateTree = { /** * Used to validate GET requests for non process events for a specific event. */ -export const validateEvents = { +export const validateRelatedEvents = { params: schema.object({ id: schema.string({ minLength: 1 }) }), query: schema.object({ events: schema.number({ defaultValue: 1000, min: 1, max: 10000 }), @@ -40,6 +40,22 @@ export const validateEvents = { ), }; +/** + * Used to validate POST requests for `/resolver/events` api. + */ +export const validateEvents = { + query: schema.object({ + // keeping the max as 10k because the limit in ES for a single query is also 10k + limit: schema.number({ defaultValue: 1000, min: 1, max: 10000 }), + afterEvent: schema.maybe(schema.string()), + }), + body: schema.nullable( + schema.object({ + filter: schema.maybe(schema.string()), + }) + ), +}; + /** * Used to validate GET requests for alerts for a specific process. */ diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index fc94e9a7c312a..c0fbebf73ed8a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => { os: 'windows', entries: [ { - field: 'process.path', + field: 'process.path.text', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -194,7 +194,7 @@ describe('When invoking Trusted Apps Schema', () => { }; expect(() => body.validate(bodyMsg2)).toThrow(); - ['process.hash.*', 'process.path'].forEach((field) => { + ['process.hash.*', 'process.path.text'].forEach((field) => { const bodyMsg3 = { ...getCreateTrustedAppItem(), entries: [ diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 72e24a7d694d4..3b3bec4a47804 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -26,7 +26,10 @@ export const PostTrustedAppCreateRequestSchema = { os: schema.oneOf([schema.literal('linux'), schema.literal('macos'), schema.literal('windows')]), entries: schema.arrayOf( schema.object({ - field: schema.oneOf([schema.literal('process.hash.*'), schema.literal('process.path')]), + field: schema.oneOf([ + schema.literal('process.hash.*'), + schema.literal('process.path.text'), + ]), type: schema.literal('match'), operator: schema.literal('included'), value: schema.string({ minLength: 1 }), diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 6afec75903477..abb0ccee8d909 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -183,7 +183,7 @@ export interface ResolverTree { relatedEvents: Omit; relatedAlerts: Omit; ancestry: ResolverAncestry; - lifecycle: ResolverEvent[]; + lifecycle: SafeResolverEvent[]; stats: ResolverNodeStats; } @@ -209,7 +209,7 @@ export interface SafeResolverTree { */ export interface ResolverLifecycleNode { entityID: string; - lifecycle: ResolverEvent[]; + lifecycle: SafeResolverEvent[]; /** * stats are only set when the entire tree is being fetched */ @@ -263,7 +263,7 @@ export interface SafeResolverAncestry { */ export interface ResolverRelatedEvents { entityID: string; - events: ResolverEvent[]; + events: SafeResolverEvent[]; nextEvent: string | null; } @@ -276,6 +276,15 @@ export interface SafeResolverRelatedEvents { nextEvent: string | null; } +/** + * Response structure for the events route. + * `nextEvent` will be set to null when at the time of querying there were no more results to retrieve from ES. + */ +export interface ResolverPaginatedEvents { + events: SafeResolverEvent[]; + nextEvent: string | null; +} + /** * Response structure for the alerts route. */ diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 3356fc67d2682..62793388e34a6 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -6,12 +6,17 @@ import { TypeOf } from '@kbn/config-schema'; import { + DeleteTrustedAppsRequestSchema, GetTrustedAppsRequestSchema, PostTrustedAppCreateRequestSchema, } from '../schema/trusted_apps'; +/** API request params for deleting Trusted App entry */ +export type DeleteTrustedAppsRequestParams = TypeOf; + /** API request params for retrieving a list of Trusted Apps */ export type GetTrustedAppsListRequest = TypeOf; + export interface GetTrustedListAppsResponse { per_page: number; page: number; @@ -21,12 +26,13 @@ export interface GetTrustedListAppsResponse { /** API Request body for creating a new Trusted App entry */ export type PostTrustedAppCreateRequest = TypeOf; + export interface PostTrustedAppCreateResponse { data: TrustedApp; } export interface MacosLinuxConditionEntry { - field: 'process.hash.*' | 'process.path'; + field: 'process.hash.*' | 'process.path.text'; type: 'match'; operator: 'included'; value: string; diff --git a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts index 48437e12f75a5..0c1f13dac2e69 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts @@ -71,7 +71,7 @@ export interface PaginationInputPaginated { export interface DocValueFields { field: string; - format: string; + format?: string | null; } export interface Explanation { diff --git a/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts new file mode 100644 index 0000000000000..259a767f8cf70 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IIndexPattern } from 'src/plugins/data/public'; +import { + IEsSearchRequest, + IEsSearchResponse, + IFieldSubType, +} from '../../../../../../src/plugins/data/common'; +import { DocValueFields, Maybe } from '../common'; + +export type BeatFieldsFactoryQueryType = 'beatFields'; + +interface FieldInfo { + category: string; + description?: string; + example?: string | number; + format?: string; + name: string; + type?: string; +} + +export interface IndexField { + /** Where the field belong */ + category: string; + /** Example of field's value */ + example?: Maybe; + /** whether the field's belong to an alias index */ + indexes: Array>; + /** The name of the field */ + name: string; + /** The type of the field's values as recognized by Kibana */ + type: string; + /** Whether the field's values can be efficiently searched for */ + searchable: boolean; + /** Whether the field's values can be aggregated */ + aggregatable: boolean; + /** Description of the field */ + description?: Maybe; + format?: Maybe; + /** the elastic type as mapped in the index */ + esTypes?: string[]; + subType?: IFieldSubType; + readFromDocValues: boolean; +} + +export type BeatFields = Record; + +export interface IndexFieldsStrategyRequest extends IEsSearchRequest { + indices: string[]; + onlyCheckIfIndicesExist: boolean; +} + +export interface IndexFieldsStrategyResponse extends IEsSearchResponse { + indexFields: IndexField[]; + indicesExist: string[]; +} + +export interface BrowserField { + aggregatable: boolean; + category: string; + description: string | null; + example: string | number | null; + fields: Readonly>>; + format: string; + indexes: string[]; + name: string; + searchable: boolean; + type: string; +} + +export type BrowserFields = Readonly>>; + +export const EMPTY_BROWSER_FIELDS = {}; +export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; +export const EMPTY_INDEX_PATTERN: IIndexPattern = { + fields: [], + title: '', +}; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts index 63a57c20a8593..a39638e48892d 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts @@ -9,6 +9,7 @@ export * from './authentications'; export * from './common'; export * from './details'; export * from './first_last_seen'; +export * from './kpi'; export * from './overview'; export * from './uncommon_processes'; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/authentications/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/authentications/index.ts new file mode 100644 index 0000000000000..cbf1f32c3b5fa --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/authentications/index.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 { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; +import { Inspect, Maybe } from '../../../../common'; +import { RequestBasicOptions } from '../../..'; +import { HostsKpiHistogramData } from '../common'; + +export interface HostsKpiAuthenticationsHistogramCount { + doc_count: number; +} + +export type HostsKpiAuthenticationsRequestOptions = RequestBasicOptions; + +export interface HostsKpiAuthenticationsStrategyResponse extends IEsSearchResponse { + authenticationsSuccess: Maybe; + authenticationsSuccessHistogram: Maybe; + authenticationsFailure: Maybe; + authenticationsFailureHistogram: Maybe; + inspect?: Maybe; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.ts new file mode 100644 index 0000000000000..52e65bb995796 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.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. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { Maybe } from '../../../../common'; + +export interface HostsKpiHistogramData { + x?: Maybe; + y?: Maybe; +} + +export interface HostsKpiHistogram { + key_as_string: string; + key: number; + doc_count: number; + count: T; +} + +export interface HostsKpiGeneralHistogramCount { + value: number; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.ts new file mode 100644 index 0000000000000..8e8bd97c9b60b --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.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 { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; +import { Inspect, Maybe } from '../../../../common'; +import { RequestBasicOptions } from '../../..'; +import { HostsKpiHistogramData } from '../common'; + +export type HostsKpiHostsRequestOptions = RequestBasicOptions; + +export interface HostsKpiHostsStrategyResponse extends IEsSearchResponse { + hosts: Maybe; + hostsHistogram: Maybe; + inspect?: Maybe; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts new file mode 100644 index 0000000000000..dc34f619e0362 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './authentications'; +export * from './common'; +export * from './hosts'; +export * from './unique_ips'; + +import { HostsKpiAuthenticationsStrategyResponse } from './authentications'; +import { HostsKpiHostsStrategyResponse } from './hosts'; +import { HostsKpiUniqueIpsStrategyResponse } from './unique_ips'; + +export enum HostsKpiQueries { + kpiAuthentications = 'hostsKpiAuthentications', + kpiHosts = 'hostsKpiHosts', + kpiUniqueIps = 'hostsKpiUniqueIps', +} + +export type HostsKpiStrategyResponse = + | Omit + | Omit + | Omit; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.ts new file mode 100644 index 0000000000000..18a603725f401 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; +import { Inspect, Maybe } from '../../../../common'; +import { RequestBasicOptions } from '../../..'; +import { HostsKpiHistogramData } from '../common'; + +export type HostsKpiUniqueIpsRequestOptions = RequestBasicOptions; + +export interface HostsKpiUniqueIpsStrategyResponse extends IEsSearchResponse { + uniqueSourceIps: Maybe; + uniqueSourceIpsHistogram: Maybe; + uniqueDestinationIps: Maybe; + uniqueDestinationIpsHistogram: Maybe; + inspect?: Maybe; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts index 569ed611bd35b..4416cbb023f10 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/overview/index.ts @@ -10,7 +10,7 @@ import { RequestBasicOptions } from '../..'; export type HostOverviewRequestOptions = RequestBasicOptions; -export interface HostOverviewStrategyResponse extends IEsSearchResponse { +export interface HostsOverviewStrategyResponse extends IEsSearchResponse { inspect?: Maybe; overviewHost: { auditbeatAuditd?: Maybe; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts index 28c0ccb7f6f4f..19fc9333de7a4 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/uncommon_processes/index.ts @@ -7,6 +7,7 @@ import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/comm import { HostEcs } from '../../../../ecs/host'; import { UserEcs } from '../../../../ecs/user'; +import { ProcessEcs } from '../../../../ecs/process'; import { RequestOptionsPaginated, SortField, @@ -20,56 +21,32 @@ import { Hits, } from '../../..'; -export interface HostUncommonProcessesRequestOptions extends RequestOptionsPaginated { +export interface HostsUncommonProcessesRequestOptions extends RequestOptionsPaginated { sort: SortField; defaultIndex: string[]; } -export interface HostUncommonProcessesStrategyResponse extends IEsSearchResponse { - edges: UncommonProcessesEdges[]; +export interface HostsUncommonProcessesStrategyResponse extends IEsSearchResponse { + edges: HostsUncommonProcessesEdges[]; totalCount: number; pageInfo: PageInfoPaginated; inspect?: Maybe; } -export interface UncommonProcessesEdges { - node: UncommonProcessItem; +export interface HostsUncommonProcessesEdges { + node: HostsUncommonProcessItem; cursor: CursorType; } -export interface UncommonProcessItem { +export interface HostsUncommonProcessItem { _id: string; instances: number; - process: ProcessEcsFields; + process: ProcessEcs; hosts: HostEcs[]; user?: Maybe; } -export interface ProcessEcsFields { - hash?: Maybe; - pid?: Maybe; - name?: Maybe; - ppid?: Maybe; - args?: Maybe; - entity_id?: Maybe; - executable?: Maybe; - title?: Maybe; - thread?: Maybe; - working_directory?: Maybe; -} - -export interface ProcessHashData { - md5?: Maybe; - sha1?: Maybe; - sha256?: Maybe; -} - -export interface Thread { - id?: Maybe; - start?: Maybe; -} - -export interface UncommonProcessHit extends Hit { +export interface HostsUncommonProcessHit extends Hit { total: TotalHit; host: Array<{ id: string[] | undefined; @@ -77,10 +54,10 @@ export interface UncommonProcessHit extends Hit { }>; _source: { '@timestamp': string; - process: ProcessEcsFields; + process: ProcessEcs; }; cursor: string; sort: StringOrNumber[]; } -export type ProcessHits = Hits; +export type ProcessHits = Hits; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts index 95f3cd4fd7da7..39443e596273a 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts @@ -9,7 +9,7 @@ import { ESQuery } from '../../typed_json'; import { HostDetailsStrategyResponse, HostDetailsRequestOptions, - HostOverviewStrategyResponse, + HostsOverviewStrategyResponse, HostAuthenticationsRequestOptions, HostAuthenticationsStrategyResponse, HostOverviewRequestOptions, @@ -18,8 +18,15 @@ import { HostsQueries, HostsRequestOptions, HostsStrategyResponse, - HostUncommonProcessesStrategyResponse, - HostUncommonProcessesRequestOptions, + HostsUncommonProcessesStrategyResponse, + HostsUncommonProcessesRequestOptions, + HostsKpiQueries, + HostsKpiAuthenticationsStrategyResponse, + HostsKpiAuthenticationsRequestOptions, + HostsKpiHostsStrategyResponse, + HostsKpiHostsRequestOptions, + HostsKpiUniqueIpsStrategyResponse, + HostsKpiUniqueIpsRequestOptions, } from './hosts'; import { NetworkQueries, @@ -70,6 +77,7 @@ export * from './network'; export type FactoryQueryTypes = | HostsQueries + | HostsKpiQueries | NetworkQueries | NetworkKpiQueries | typeof MatrixHistogramQuery; @@ -99,13 +107,19 @@ export type StrategyResponseType = T extends HostsQ : T extends HostsQueries.details ? HostDetailsStrategyResponse : T extends HostsQueries.overview - ? HostOverviewStrategyResponse + ? HostsOverviewStrategyResponse : T extends HostsQueries.authentications ? HostAuthenticationsStrategyResponse : T extends HostsQueries.firstLastSeen ? HostFirstLastSeenStrategyResponse : T extends HostsQueries.uncommonProcesses - ? HostUncommonProcessesStrategyResponse + ? HostsUncommonProcessesStrategyResponse + : T extends HostsKpiQueries.kpiAuthentications + ? HostsKpiAuthenticationsStrategyResponse + : T extends HostsKpiQueries.kpiHosts + ? HostsKpiHostsStrategyResponse + : T extends HostsKpiQueries.kpiUniqueIps + ? HostsKpiUniqueIpsStrategyResponse : T extends NetworkQueries.details ? NetworkDetailsStrategyResponse : T extends NetworkQueries.dns @@ -147,7 +161,13 @@ export type StrategyRequestType = T extends HostsQu : T extends HostsQueries.firstLastSeen ? HostFirstLastSeenRequestOptions : T extends HostsQueries.uncommonProcesses - ? HostUncommonProcessesRequestOptions + ? HostsUncommonProcessesRequestOptions + : T extends HostsKpiQueries.kpiAuthentications + ? HostsKpiAuthenticationsRequestOptions + : T extends HostsKpiQueries.kpiHosts + ? HostsKpiHostsRequestOptions + : T extends HostsKpiQueries.kpiUniqueIps + ? HostsKpiUniqueIpsRequestOptions : T extends NetworkQueries.details ? NetworkDetailsRequestOptions : T extends NetworkQueries.dns diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/common/index.ts index 19521741c5f66..b557755b07a03 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/common/index.ts @@ -7,6 +7,17 @@ import { GeoEcs } from '../../../../ecs/geo'; import { Maybe } from '../../..'; +export enum NetworkDirectionEcs { + inbound = 'inbound', + outbound = 'outbound', + internal = 'internal', + external = 'external', + incoming = 'incoming', + outgoing = 'outgoing', + listening = 'listening', + unknown = 'unknown', +} + export enum NetworkTopTablesFields { bytes_in = 'bytes_in', bytes_out = 'bytes_out', diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts index 196317e7587bf..8c4e19a804148 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/users/index.ts @@ -40,9 +40,9 @@ export interface NetworkUsersNode { export interface NetworkUsersItem { name?: Maybe; - id?: Maybe; - groupId?: Maybe; - groupName?: Maybe; + id?: Maybe; + groupId?: Maybe; + groupName?: Maybe; count?: Maybe; } diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts index 6f9192be40150..9fa7f96599deb 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts @@ -22,7 +22,6 @@ export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse export interface TimelineEventsDetailsRequestOptions extends Partial { - defaultIndex: string[]; indexName: string; eventId: string; } diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index 84a007e322f11..3888d37a547f7 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -239,6 +239,7 @@ export const SavedTimelineRuntimeType = runtimeTypes.partial({ excludedRowRendererIds: unionWithNullType(runtimeTypes.array(RowRendererIdRuntimeType)), favorite: unionWithNullType(runtimeTypes.array(SavedFavoriteRuntimeType)), filters: unionWithNullType(runtimeTypes.array(SavedFilterRuntimeType)), + indexNames: unionWithNullType(runtimeTypes.array(runtimeTypes.string)), kqlMode: unionWithNullType(runtimeTypes.string), kqlQuery: unionWithNullType(SavedFilterQueryQueryRuntimeType), title: unionWithNullType(runtimeTypes.string), @@ -398,3 +399,5 @@ export const importTimelineResultSchema = runtimeTypes.exact( ); export type ImportTimelineResultSchema = runtimeTypes.TypeOf; + +export type TimelineEventsType = 'all' | 'raw' | 'alert' | 'signal' | 'custom'; diff --git a/x-pack/plugins/security_solution/cypress/cypress.json b/x-pack/plugins/security_solution/cypress/cypress.json index b097b0432e75d..173514565c8bb 100644 --- a/x-pack/plugins/security_solution/cypress/cypress.json +++ b/x-pack/plugins/security_solution/cypress/cypress.json @@ -1,6 +1,9 @@ { "baseUrl": "http://localhost:5601", "defaultCommandTimeout": 120000, + "retries": { + "runMode": 2 + }, "screenshotsFolder": "../../../target/kibana-security-solution/cypress/screenshots", "trashAssetsBeforeRuns": false, "video": false, diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index fdfa042e8fcc9..db841d2a732c4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -30,8 +30,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/77957 -describe.skip('Alerts', () => { +describe('Alerts', () => { context('Closing alerts', () => { beforeEach(() => { esArchiverLoad('alerts'); @@ -141,24 +140,25 @@ describe.skip('Alerts', () => { waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeClosed; - cy.get(NUMBER_OF_ALERTS).invoke('text').should('eq', expectedNumberOfAlerts.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfAlerts.toString()} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${expectedNumberOfAlerts.toString()} alerts` + ); goToClosedAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS) - .invoke('text') - .should('eql', numberOfAlertsToBeClosed.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${numberOfAlertsToBeClosed.toString()} alert`); + cy.get(NUMBER_OF_ALERTS).should('have.text', numberOfAlertsToBeClosed.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${numberOfAlertsToBeClosed.toString()} alert` + ); cy.get(ALERTS).should('have.length', numberOfAlertsToBeClosed); }); }); }); + context('Opening alerts', () => { beforeEach(() => { esArchiverLoad('closed_alerts'); @@ -187,24 +187,25 @@ describe.skip('Alerts', () => { waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; - cy.get(NUMBER_OF_ALERTS).invoke('text').should('eq', expectedNumberOfAlerts.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfAlerts.toString()} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${expectedNumberOfAlerts.toString()} alerts` + ); goToOpenedAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS) - .invoke('text') - .should('eql', numberOfAlertsToBeOpened.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${numberOfAlertsToBeOpened.toString()} alert`); + cy.get(NUMBER_OF_ALERTS).should('have.text', numberOfAlertsToBeOpened.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${numberOfAlertsToBeOpened.toString()} alert` + ); cy.get(ALERTS).should('have.length', numberOfAlertsToBeOpened); }); }); }); + context('Marking alerts as in-progress', () => { beforeEach(() => { esArchiverLoad('alerts'); @@ -229,23 +230,25 @@ describe.skip('Alerts', () => { cy.reload(); goToOpenedAlerts(); waitForAlertsToBeLoaded(); - waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress; - cy.get(NUMBER_OF_ALERTS).invoke('text').should('eq', expectedNumberOfAlerts.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfAlerts.toString()} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${expectedNumberOfAlerts.toString()} alerts` + ); goToInProgressAlerts(); waitForAlerts(); - cy.get(NUMBER_OF_ALERTS) - .invoke('text') - .should('eql', numberOfAlertsToBeMarkedInProgress.toString()); - cy.get(SHOWING_ALERTS) - .invoke('text') - .should('eql', `Showing ${numberOfAlertsToBeMarkedInProgress.toString()} alert`); + cy.get(NUMBER_OF_ALERTS).should( + 'have.text', + numberOfAlertsToBeMarkedInProgress.toString() + ); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${numberOfAlertsToBeMarkedInProgress.toString()} alert` + ); cy.get(ALERTS).should('have.length', numberOfAlertsToBeMarkedInProgress); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts index 20cf624b3360d..3fa304ab7cf19 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts @@ -43,6 +43,7 @@ describe('Alerts detection rules', () => { waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + cy.get(RULE_NAME) .eq(FIFTH_RULE) .invoke('text') @@ -56,7 +57,6 @@ describe('Alerts detection rules', () => { activateRule(SEVENTH_RULE); waitForRuleToBeActivated(); sortByActivatedRules(); - cy.get(RULE_NAME) .eq(FIRST_RULE) .invoke('text') @@ -70,7 +70,6 @@ describe('Alerts detection rules', () => { cy.wrap(expectedRulesNames).should('include', seventhRuleName); }); }); - cy.get(RULE_SWITCH).eq(FIRST_RULE).should('have.attr', 'role', 'switch'); cy.get(RULE_SWITCH).eq(SECOND_RULE).should('have.attr', 'role', 'switch'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index 17ff1dad79960..f999c5cecc392 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newRule, existingRule } from '../objects/rule'; +import { newRule, existingRule, indexPatterns, editedRule } from '../objects/rule'; +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, @@ -12,20 +20,49 @@ import { RULE_NAME, RULES_ROW, RULES_TABLE, + RULE_SWITCH, SEVERITY, SHOWING_RULES_TEXT, } from '../screens/alerts_detection_rules'; import { + ABOUT_CONTINUE_BTN, + ABOUT_EDIT_BUTTON, + ACTIONS_THROTTLE_INPUT, + CUSTOM_QUERY_INPUT, + DEFINE_CONTINUE_BUTTON, + DEFINE_EDIT_BUTTON, + DEFINE_INDEX_INPUT, + RISK_INPUT, + RULE_DESCRIPTION_INPUT, + RULE_NAME_INPUT, + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, + SEVERITY_DROPDOWN, + TAGS_FIELD, +} from '../screens/create_new_rule'; +import { + ADDITIONAL_LOOK_BACK_DETAILS, + ABOUT_DETAILS, ABOUT_INVESTIGATION_NOTES, ABOUT_RULE_DESCRIPTION, + CUSTOM_QUERY_DETAILS, + DEFINITION_DETAILS, + FALSE_POSITIVES_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -37,46 +74,46 @@ import { changeToThreeHundredRowsPerPage, deleteFirstRule, deleteSelectedRules, + editFirstRule, filterByCustomRules, goToCreateNewRule, goToRuleDetails, selectNumberOfRules, waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, - editFirstRule, } from '../tasks/alerts_detection_rules'; import { createAndActivateRule, + fillAboutRule, fillAboutRuleAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, + fillScheduleRuleAndContinue, goToAboutStepTab, - goToScheduleStepTab, goToActionsStepTab, - fillAboutRule, + goToScheduleStepTab, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; +import { saveEditedRule } from '../tasks/edit_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; -import { - ACTIONS_THROTTLE_INPUT, - CUSTOM_QUERY_INPUT, - DEFINE_INDEX_INPUT, - RULE_NAME_INPUT, - RULE_DESCRIPTION_INPUT, - TAGS_FIELD, - SEVERITY_DROPDOWN, - RISK_INPUT, - SCHEDULE_INTERVAL_AMOUNT_INPUT, - SCHEDULE_INTERVAL_UNITS_INPUT, - DEFINE_EDIT_BUTTON, - DEFINE_CONTINUE_BUTTON, - ABOUT_EDIT_BUTTON, - ABOUT_CONTINUE_BTN, -} from '../screens/create_new_rule'; -import { saveEditedRule } from '../tasks/edit_rule'; -describe('Detection rules, custom', () => { +const expectedUrls = newRule.referenceUrls.join(''); +const expectedFalsePositives = newRule.falsePositivesExamples.join(''); +const expectedTags = newRule.tags.join(''); +const expectedMitre = newRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = 1; +const expectedEditedtags = editedRule.tags.join(''); +const expectedEditedIndexPatterns = + editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns; + +describe('Custom detection rules creation', () => { before(() => { esArchiverLoad('timeline'); }); @@ -85,7 +122,7 @@ describe('Detection rules, custom', () => { esArchiverUnload('timeline'); }); - it('Creates and activates a new custom rule', () => { + it('Creates and activates a new rule', () => { loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); @@ -94,27 +131,27 @@ describe('Detection rules, custom', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newRule); fillAboutRuleAndContinue(newRule); + fillScheduleRuleAndContinue(newRule); // expect define step to repopulate cy.get(DEFINE_EDIT_BUTTON).click(); - cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', newRule.customQuery); + cy.get(CUSTOM_QUERY_INPUT).should('have.text', newRule.customQuery); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); cy.get(DEFINE_CONTINUE_BUTTON).should('not.exist'); // expect about step to populate cy.get(ABOUT_EDIT_BUTTON).click(); - cy.get(RULE_NAME_INPUT).invoke('val').should('eq', newRule.name); + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', newRule.name); cy.get(ABOUT_CONTINUE_BTN).should('exist').click({ force: true }); cy.get(ABOUT_CONTINUE_BTN).should('not.exist'); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -124,78 +161,59 @@ describe('Detection rules, custom', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newRule.name); + cy.get(RISK_SCORE).should('have.text', newRule.riskScore); + cy.get(SEVERITY).should('have.text', newRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newRule.description); + cy.get(RULE_NAME_HEADER).should('have.text', `${newRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', newRule.severity); - getDescriptionForTitle('Risk score').invoke('text').should('eql', newRule.riskScore); - getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); - getDescriptionForTitle('False positive examples') - .invoke('text') - .should('eql', expectedFalsePositives); - getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDetails(SEVERITY_DETAILS).should('have.text', newRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); }); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); - cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Index patterns') - .invoke('text') - .should('eql', expectedIndexPatterns.join('')); - getDescriptionForTitle('Custom query') - .invoke('text') - .should('eql', `${newRule.customQuery} `); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newRule.runsEvery.interval}${newRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newRule.lookBack.interval}${newRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.above', 0); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', newRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'query'); + cy.get(ALERT_RULE_SEVERITY).first().should('have.text', newRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newRule.riskScore); }); }); -describe('Deletes custom rules', () => { +describe('Custom detection rules deletion and edition', () => { beforeEach(() => { esArchiverLoad('custom_rules'); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); @@ -208,143 +226,132 @@ describe('Deletes custom rules', () => { esArchiverUnload('custom_rules'); }); - it('Deletes one rule', () => { - cy.get(RULES_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; - - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${initialNumberOfRules} rules`); - - deleteFirstRule(); - waitForRulesToBeLoaded(); - - cy.get(RULES_TABLE).then(($table) => { - cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); - }); - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rules`); - cy.get(CUSTOM_RULES_BTN) - .invoke('text') - .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); - }); - }); - - it('Deletes more than one rule', () => { - cy.get(RULES_TABLE) - .find(RULES_ROW) - .then((rules) => { - const initialNumberOfRules = rules.length; - const numberOfRulesToBeDeleted = 3; - const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted; - - selectNumberOfRules(numberOfRulesToBeDeleted); - deleteSelectedRules(); - waitForRulesToBeLoaded(); - - cy.get(RULES_TABLE).then(($table) => { - cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); + context('Deletion', () => { + it('Deletes one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then((rules) => { + const initialNumberOfRules = rules.length; + const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - 1; + + cy.get(SHOWING_RULES_TEXT).should('have.text', `Showing ${initialNumberOfRules} rules`); + + deleteFirstRule(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should( + 'eql', + expectedNumberOfRulesAfterDeletion + ); + }); + cy.get(SHOWING_RULES_TEXT).should( + 'have.text', + `Showing ${expectedNumberOfRulesAfterDeletion} rules` + ); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); }); - cy.get(SHOWING_RULES_TEXT) - .invoke('text') - .should('eql', `Showing ${expectedNumberOfRulesAfterDeletion} rule`); - cy.get(CUSTOM_RULES_BTN) - .invoke('text') - .should('eql', `Custom rules (${expectedNumberOfRulesAfterDeletion})`); - }); - }); - - it('Allows a rule to be edited', () => { - editFirstRule(); - - // expect define step to populate - cy.get(CUSTOM_QUERY_INPUT).invoke('text').should('eq', existingRule.customQuery); - if (existingRule.index && existingRule.index.length > 0) { - cy.get(DEFINE_INDEX_INPUT).invoke('text').should('eq', existingRule.index.join('')); - } - - goToAboutStepTab(); - - // expect about step to populate - cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name); - cy.get(RULE_DESCRIPTION_INPUT).invoke('text').should('eql', existingRule.description); - cy.get(TAGS_FIELD).invoke('text').should('eql', existingRule.tags.join('')); - - cy.get(SEVERITY_DROPDOWN).invoke('text').should('eql', existingRule.severity); - cy.get(RISK_INPUT).invoke('val').should('eql', existingRule.riskScore); - - goToScheduleStepTab(); - - // expect schedule step to populate - const intervalParts = existingRule.interval && existingRule.interval.match(/[0-9]+|[a-zA-Z]+/g); - if (intervalParts) { - const [amount, unit] = intervalParts; - cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount); - cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit); - } else { - throw new Error('Cannot assert scheduling info on a rule without an interval'); - } - - goToActionsStepTab(); - - cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); - - goToAboutStepTab(); - - const editedRule = { - ...existingRule, - severity: 'Medium', - description: 'Edited Rule description', - }; - - fillAboutRule(editedRule); - saveEditedRule(); - - const expectedTags = editedRule.tags.join(''); - const expectedIndexPatterns = - editedRule.index && editedRule.index.length - ? editedRule.index - : [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${editedRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', editedRule.description); - cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', editedRule.severity); - getDescriptionForTitle('Risk score').invoke('text').should('eql', editedRule.riskScore); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); }); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); - cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', editedRule.note); - - cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Index patterns') - .invoke('text') - .should('eql', expectedIndexPatterns.join('')); - getDescriptionForTitle('Custom query') - .invoke('text') - .should('eql', `${editedRule.customQuery} `); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + it('Deletes more than one rule', () => { + cy.get(RULES_TABLE) + .find(RULES_ROW) + .then((rules) => { + const initialNumberOfRules = rules.length; + const numberOfRulesToBeDeleted = 3; + const expectedNumberOfRulesAfterDeletion = + initialNumberOfRules - numberOfRulesToBeDeleted; + + selectNumberOfRules(numberOfRulesToBeDeleted); + deleteSelectedRules(); + waitForRulesToBeLoaded(); + + cy.get(RULES_TABLE).then(($table) => { + cy.wrap($table.find(RULES_ROW).length).should( + 'eql', + expectedNumberOfRulesAfterDeletion + ); + }); + cy.get(SHOWING_RULES_TEXT).should( + 'have.text', + `Showing ${expectedNumberOfRulesAfterDeletion} rule` + ); + cy.get(CUSTOM_RULES_BTN).should( + 'have.text', + `Custom rules (${expectedNumberOfRulesAfterDeletion})` + ); + }); }); + }); - if (editedRule.interval) { - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', editedRule.interval); + context('Edition', () => { + it('Allows a rule to be edited', () => { + editFirstRule(); + + // expect define step to populate + cy.get(CUSTOM_QUERY_INPUT).should('have.text', existingRule.customQuery); + if (existingRule.index && existingRule.index.length > 0) { + cy.get(DEFINE_INDEX_INPUT).should('have.text', existingRule.index.join('')); + } + + goToAboutStepTab(); + + // expect about step to populate + cy.get(RULE_NAME_INPUT).invoke('val').should('eql', existingRule.name); + cy.get(RULE_DESCRIPTION_INPUT).should('have.text', existingRule.description); + cy.get(TAGS_FIELD).should('have.text', existingRule.tags.join('')); + cy.get(SEVERITY_DROPDOWN).should('have.text', existingRule.severity); + cy.get(RISK_INPUT).invoke('val').should('eql', existingRule.riskScore); + + goToScheduleStepTab(); + + // expect schedule step to populate + const intervalParts = + existingRule.interval && existingRule.interval.match(/[0-9]+|[a-zA-Z]+/g); + if (intervalParts) { + const [amount, unit] = intervalParts; + cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', amount); + cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', unit); + } else { + throw new Error('Cannot assert scheduling info on a rule without an interval'); + } + + goToActionsStepTab(); + + cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); + + goToAboutStepTab(); + fillAboutRule(editedRule); + saveEditedRule(); + + cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', editedRule.description); + cy.get(ABOUT_DETAILS).within(() => { + getDetails(SEVERITY_DETAILS).should('have.text', editedRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', editedRule.riskScore); + getDetails(TAGS_DETAILS).should('have.text', expectedEditedtags); + }); + cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE) + .eq(INVESTIGATION_NOTES_TOGGLE) + .click({ force: true }); + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', editedRule.note); + cy.get(DEFINITION_DETAILS).within(() => { + getDetails(INDEX_PATTERNS_DETAILS).should( + 'have.text', + expectedEditedIndexPatterns.join('') + ); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${editedRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - } + if (editedRule.interval) { + cy.get(SCHEDULE_DETAILS).within(() => { + getDetails(RUNS_EVERY_DETAILS).should('have.text', editedRule.interval); + }); + } + }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index 76871929fe050..e2ff51dd544a2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { eqlRule } from '../objects/rule'; +import { eqlRule, indexPatterns } from '../objects/rule'; import { CUSTOM_RULES_BTN, @@ -12,19 +12,32 @@ import { RULE_NAME, RULES_ROW, RULES_TABLE, + RULE_SWITCH, SEVERITY, } from '../screens/alerts_detection_rules'; import { ABOUT_DETAILS, ABOUT_INVESTIGATION_NOTES, ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + CUSTOM_QUERY_DETAILS, DEFINITION_DETAILS, - getDescriptionForTitle, + FALSE_POSITIVES_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -43,14 +56,25 @@ import { import { createAndActivateRule, fillAboutRuleAndContinue, - selectEqlRuleType, fillDefineEqlRuleAndContinue, + fillScheduleRuleAndContinue, + selectEqlRuleType, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = eqlRule.referenceUrls.join(''); +const expectedFalsePositives = eqlRule.falsePositivesExamples.join(''); +const expectedTags = eqlRule.tags.join(''); +const expectedMitre = eqlRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = 1; + describe('Detection rules, EQL', () => { before(() => { esArchiverLoad('timeline'); @@ -70,14 +94,14 @@ describe('Detection rules, EQL', () => { selectEqlRuleType(); fillDefineEqlRuleAndContinue(eqlRule); fillAboutRuleAndContinue(eqlRule); + fillScheduleRuleAndContinue(eqlRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -87,73 +111,40 @@ describe('Detection rules, EQL', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', eqlRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', eqlRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', eqlRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', eqlRule.name); + cy.get(RISK_SCORE).should('have.text', eqlRule.riskScore); + cy.get(SEVERITY).should('have.text', eqlRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - eqlRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - eqlRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - eqlRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - eqlRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${eqlRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', eqlRule.description); + cy.get(RULE_NAME_HEADER).should('have.text', `${eqlRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', eqlRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', eqlRule.severity); - getDescriptionForTitle('Risk score').invoke('text').should('eql', eqlRule.riskScore); - getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); - getDescriptionForTitle('False positive examples') - .invoke('text') - .should('eql', expectedFalsePositives); - getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDetails(SEVERITY_DETAILS).should('have.text', eqlRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', eqlRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); }); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); - cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Index patterns') - .invoke('text') - .should('eql', expectedIndexPatterns.join('')); - getDescriptionForTitle('Custom query') - .invoke('text') - .should('eql', `${eqlRule.customQuery} `); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Event Correlation'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${eqlRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Event Correlation'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${eqlRule.runsEvery.interval}${eqlRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${eqlRule.lookBack.interval}${eqlRule.lookBack.type}` + ); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index 47e49d48e2aec..49ec6381cbc89 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -16,14 +16,25 @@ import { SEVERITY, } from '../screens/alerts_detection_rules'; import { + ABOUT_DETAILS, ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + ANOMALY_SCORE_DETAILS, + DEFINITION_DETAILS, + FALSE_POSITIVES_DETAILS, + getDetails, MACHINE_LEARNING_JOB_ID, MACHINE_LEARNING_JOB_STATUS, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -43,6 +54,7 @@ import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineMachineLearningRuleAndContinue, + fillScheduleRuleAndContinue, selectMachineLearningRuleType, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; @@ -50,6 +62,16 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = machineLearningRule.referenceUrls.join(''); +const expectedFalsePositives = machineLearningRule.falsePositivesExamples.join(''); +const expectedTags = machineLearningRule.tags.join(''); +const expectedMitre = machineLearningRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); +const expectedNumberOfRules = totalNumberOfPrebuiltRulesInEsArchive + 1; + describe('Detection rules, machine learning', () => { before(() => { esArchiverLoad('prebuilt_rules_loaded'); @@ -69,6 +91,7 @@ describe('Detection rules, machine learning', () => { selectMachineLearningRuleType(); fillDefineMachineLearningRuleAndContinue(machineLearningRule); fillAboutRuleAndContinue(machineLearningRule); + fillScheduleRuleAndContinue(machineLearningRule); createAndActivateRule(); cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); @@ -76,7 +99,6 @@ describe('Detection rules, machine learning', () => { changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - const expectedNumberOfRules = totalNumberOfPrebuiltRulesInEsArchive + 1; cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRules); }); @@ -86,67 +108,42 @@ describe('Detection rules, machine learning', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', machineLearningRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', machineLearningRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', machineLearningRule.severity); + cy.get(RULE_NAME).should('have.text', machineLearningRule.name); + cy.get(RISK_SCORE).should('have.text', machineLearningRule.riskScore); + cy.get(SEVERITY).should('have.text', machineLearningRule.severity); cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - machineLearningRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - machineLearningRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - machineLearningRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - machineLearningRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${machineLearningRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', machineLearningRule.description); + cy.get(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', machineLearningRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', machineLearningRule.severity); - getDescriptionForTitle('Risk score') - .invoke('text') - .should('eql', machineLearningRule.riskScore); - getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); - getDescriptionForTitle('False positive examples') - .invoke('text') - .should('eql', expectedFalsePositives); - getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDetails(SEVERITY_DETAILS).should('have.text', machineLearningRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', machineLearningRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); }); - cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Anomaly score') - .invoke('text') - .should('eql', machineLearningRule.anomalyScoreThreshold); - getDescriptionForTitle('Anomaly score') - .invoke('text') - .should('eql', machineLearningRule.anomalyScoreThreshold); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Machine Learning'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); - cy.get(MACHINE_LEARNING_JOB_STATUS).invoke('text').should('eql', 'Stopped'); - cy.get(MACHINE_LEARNING_JOB_ID) - .invoke('text') - .should('eql', machineLearningRule.machineLearningJob); + getDetails(ANOMALY_SCORE_DETAILS).should( + 'have.text', + machineLearningRule.anomalyScoreThreshold + ); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Machine Learning'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + cy.get(MACHINE_LEARNING_JOB_STATUS).should('have.text', 'Stopped'); + cy.get(MACHINE_LEARNING_JOB_ID).should('have.text', machineLearningRule.machineLearningJob); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${machineLearningRule.runsEvery.interval}${machineLearningRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${machineLearningRule.lookBack.interval}${machineLearningRule.lookBack.type}` + ); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index 4edf5e1866087..090012de72534 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -4,33 +4,58 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newOverrideRule } from '../objects/rule'; +import { indexPatterns, newOverrideRule, severitiesOverride } from '../objects/rule'; +import { + NUMBER_OF_ALERTS, + ALERT_RULE_NAME, + ALERT_RULE_METHOD, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, + RULE_SWITCH, RULES_ROW, RULES_TABLE, SEVERITY, } from '../screens/alerts_detection_rules'; import { ABOUT_INVESTIGATION_NOTES, + ABOUT_DETAILS, ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + CUSTOM_QUERY_DETAILS, + DEFINITION_DETAILS, + DETAILS_DESCRIPTION, + DETAILS_TITLE, + FALSE_POSITIVES_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, + RISK_SCORE_OVERRIDE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - ABOUT_DETAILS, - getDescriptionForTitle, - DEFINITION_DETAILS, + RULE_NAME_OVERRIDE_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, - DETAILS_TITLE, - DETAILS_DESCRIPTION, + SEVERITY_DETAILS, + TAGS_DETAILS, + TIMELINE_TEMPLATE_DETAILS, + TIMESTAMP_OVERRIDE_DETAILS, } from '../screens/rule_details'; import { goToManageAlertsDetectionRules, + sortRiskScore, waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded, } from '../tasks/alerts'; @@ -46,12 +71,24 @@ import { createAndActivateRule, fillAboutRuleWithOverrideAndContinue, fillDefineCustomRuleWithImportedQueryAndContinue, + fillScheduleRuleAndContinue, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = newOverrideRule.referenceUrls.join(''); +const expectedFalsePositives = newOverrideRule.falsePositivesExamples.join(''); +const expectedTags = newOverrideRule.tags.join(''); +const expectedMitre = newOverrideRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); + describe('Detection rules, override', () => { before(() => { esArchiverLoad('timeline'); @@ -70,9 +107,10 @@ describe('Detection rules, override', () => { goToCreateNewRule(); fillDefineCustomRuleWithImportedQueryAndContinue(newOverrideRule); fillAboutRuleWithOverrideAndContinue(newOverrideRule); + fillScheduleRuleAndContinue(newOverrideRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -87,98 +125,75 @@ describe('Detection rules, override', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newOverrideRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newOverrideRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newOverrideRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newOverrideRule.name); + cy.get(RISK_SCORE).should('have.text', newOverrideRule.riskScore); + cy.get(SEVERITY).should('have.text', newOverrideRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newOverrideRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newOverrideRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newOverrideRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newOverrideRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newOverrideRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newOverrideRule.description); - - const expectedOverrideSeverities = ['Low', 'Medium', 'High', 'Critical']; - + cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newOverrideRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', newOverrideRule.severity); - getDescriptionForTitle('Risk score').invoke('text').should('eql', newOverrideRule.riskScore); - getDescriptionForTitle('Risk score override') - .invoke('text') - .should('eql', `${newOverrideRule.riskOverride}signal.rule.risk_score`); - getDescriptionForTitle('Rule name override') - .invoke('text') - .should('eql', newOverrideRule.nameOverride); - getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); - getDescriptionForTitle('False positive examples') - .invoke('text') - .should('eql', expectedFalsePositives); - getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); - getDescriptionForTitle('Timestamp override') - .invoke('text') - .should('eql', newOverrideRule.timestampOverride); + getDetails(SEVERITY_DETAILS).should('have.text', newOverrideRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newOverrideRule.riskScore); + getDetails(RISK_SCORE_OVERRIDE_DETAILS).should( + 'have.text', + `${newOverrideRule.riskOverride}signal.rule.risk_score` + ); + getDetails(RULE_NAME_OVERRIDE_DETAILS).should('have.text', newOverrideRule.nameOverride); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); + getDetails(TIMESTAMP_OVERRIDE_DETAILS).should('have.text', newOverrideRule.timestampOverride); cy.contains(DETAILS_TITLE, 'Severity override') .invoke('index', DETAILS_TITLE) // get index relative to other titles, not all siblings .then((severityOverrideIndex) => { newOverrideRule.severityOverride.forEach((severity, i) => { cy.get(DETAILS_DESCRIPTION) .eq(severityOverrideIndex + i) - .invoke('text') .should( - 'eql', - `${severity.sourceField}:${severity.sourceValue}${expectedOverrideSeverities[i]}` + 'have.text', + `${severity.sourceField}:${severity.sourceValue}${severitiesOverride[i]}` ); }); }); }); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); - cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Index patterns') - .invoke('text') - .should('eql', expectedIndexPatterns.join('')); - getDescriptionForTitle('Custom query') - .invoke('text') - .should('eql', `${newOverrideRule.customQuery} `); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Query'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newOverrideRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Query'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newOverrideRule.runsEvery.interval}${newOverrideRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newOverrideRule.lookBack.interval}${newOverrideRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.above', 0); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', 'auditbeat'); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'query'); + cy.get(ALERT_RULE_SEVERITY).first().should('have.text', 'critical'); + + sortRiskScore(); + + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', '80'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts index 986a7c7177a79..6088a9dedbd06 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts @@ -56,7 +56,7 @@ describe('Alerts rules, prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN).invoke('text').should('eql', expectedElasticRulesBtnText); + cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -81,7 +81,7 @@ describe('Deleting prebuilt rules', () => { loadPrebuiltDetectionRules(); waitForPrebuiltDetectionRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN).invoke('text').should('eql', expectedElasticRulesBtnText); + cy.get(ELASTIC_RULES_BTN).should('have.text', expectedElasticRulesBtnText); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -113,16 +113,15 @@ describe('Deleting prebuilt rules', () => { changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterDeletion})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterDeletion})` + ); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); }); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); - cy.get(RELOAD_PREBUILT_RULES_BTN) - .invoke('text') - .should('eql', 'Install 1 Elastic prebuilt rule '); + cy.get(RELOAD_PREBUILT_RULES_BTN).should('have.text', 'Install 1 Elastic prebuilt rule '); reloadDeletedRules(); @@ -135,9 +134,10 @@ describe('Deleting prebuilt rules', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterRecovering); }); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterRecovering})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterRecovering})` + ); }); it('Deletes and recovers more than one rule', () => { @@ -152,12 +152,14 @@ describe('Deleting prebuilt rules', () => { waitForRulesToBeLoaded(); cy.get(RELOAD_PREBUILT_RULES_BTN).should('exist'); - cy.get(RELOAD_PREBUILT_RULES_BTN) - .invoke('text') - .should('eql', `Install ${numberOfRulesToBeSelected} Elastic prebuilt rules `); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterDeletion})`); + cy.get(RELOAD_PREBUILT_RULES_BTN).should( + 'have.text', + `Install ${numberOfRulesToBeSelected} Elastic prebuilt rules ` + ); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterDeletion})` + ); cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterDeletion); }); @@ -173,8 +175,9 @@ describe('Deleting prebuilt rules', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', expectedNumberOfRulesAfterRecovering); }); - cy.get(ELASTIC_RULES_BTN) - .invoke('text') - .should('eql', `Elastic rules (${expectedNumberOfRulesAfterRecovering})`); + cy.get(ELASTIC_RULES_BTN).should( + 'have.text', + `Elastic rules (${expectedNumberOfRulesAfterRecovering})` + ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 00175ed3baeb8..5ee7e69e877e3 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -4,27 +4,49 @@ * you may not use this file except in compliance with the Elastic License. */ -import { newThresholdRule } from '../objects/rule'; +import { indexPatterns, newThresholdRule } from '../objects/rule'; +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, RISK_SCORE, RULE_NAME, + RULE_SWITCH, RULES_ROW, RULES_TABLE, SEVERITY, } from '../screens/alerts_detection_rules'; import { + ABOUT_DETAILS, ABOUT_INVESTIGATION_NOTES, ABOUT_RULE_DESCRIPTION, + ADDITIONAL_LOOK_BACK_DETAILS, + CUSTOM_QUERY_DETAILS, + FALSE_POSITIVES_DETAILS, + DEFINITION_DETAILS, + getDetails, + INDEX_PATTERNS_DETAILS, INVESTIGATION_NOTES_MARKDOWN, INVESTIGATION_NOTES_TOGGLE, + MITRE_ATTACK_DETAILS, + REFERENCE_URLS_DETAILS, + RISK_SCORE_DETAILS, RULE_ABOUT_DETAILS_HEADER_TOGGLE, RULE_NAME_HEADER, - getDescriptionForTitle, - ABOUT_DETAILS, - DEFINITION_DETAILS, + RULE_TYPE_DETAILS, + RUNS_EVERY_DETAILS, SCHEDULE_DETAILS, + SEVERITY_DETAILS, + TAGS_DETAILS, + THRESHOLD_DETAILS, + TIMELINE_TEMPLATE_DETAILS, } from '../screens/rule_details'; import { @@ -44,13 +66,25 @@ import { createAndActivateRule, fillAboutRuleAndContinue, fillDefineThresholdRuleAndContinue, + fillScheduleRuleAndContinue, selectThresholdRuleType, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; +const expectedUrls = newThresholdRule.referenceUrls.join(''); +const expectedFalsePositives = newThresholdRule.falsePositivesExamples.join(''); +const expectedTags = newThresholdRule.tags.join(''); +const expectedMitre = newThresholdRule.mitre + .map(function (mitre) { + return mitre.tactic + mitre.techniques.join(''); + }) + .join(''); + describe('Detection rules, threshold', () => { before(() => { esArchiverLoad('timeline'); @@ -70,9 +104,10 @@ describe('Detection rules, threshold', () => { selectThresholdRuleType(); fillDefineThresholdRuleAndContinue(newThresholdRule); fillAboutRuleAndContinue(newThresholdRule); + fillScheduleRuleAndContinue(newThresholdRule); createAndActivateRule(); - cy.get(CUSTOM_RULES_BTN).invoke('text').should('eql', 'Custom rules (1)'); + cy.get(CUSTOM_RULES_BTN).should('have.text', 'Custom rules (1)'); changeToThreeHundredRowsPerPage(); waitForRulesToBeLoaded(); @@ -87,79 +122,60 @@ describe('Detection rules, threshold', () => { cy.get(RULES_TABLE).then(($table) => { cy.wrap($table.find(RULES_ROW).length).should('eql', 1); }); - cy.get(RULE_NAME).invoke('text').should('eql', newThresholdRule.name); - cy.get(RISK_SCORE).invoke('text').should('eql', newThresholdRule.riskScore); - cy.get(SEVERITY).invoke('text').should('eql', newThresholdRule.severity); - cy.get('[data-test-subj="rule-switch"]').should('have.attr', 'aria-checked', 'true'); + cy.get(RULE_NAME).should('have.text', newThresholdRule.name); + cy.get(RISK_SCORE).should('have.text', newThresholdRule.riskScore); + cy.get(SEVERITY).should('have.text', newThresholdRule.severity); + cy.get(RULE_SWITCH).should('have.attr', 'aria-checked', 'true'); goToRuleDetails(); - let expectedUrls = ''; - newThresholdRule.referenceUrls.forEach((url) => { - expectedUrls = expectedUrls + url; - }); - let expectedFalsePositives = ''; - newThresholdRule.falsePositivesExamples.forEach((falsePositive) => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - let expectedTags = ''; - newThresholdRule.tags.forEach((tag) => { - expectedTags = expectedTags + tag; - }); - let expectedMitre = ''; - newThresholdRule.mitre.forEach((mitre) => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach((technique) => { - expectedMitre = expectedMitre + technique; - }); - }); - const expectedIndexPatterns = [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ]; - - cy.get(RULE_NAME_HEADER).invoke('text').should('eql', `${newThresholdRule.name} Beta`); - - cy.get(ABOUT_RULE_DESCRIPTION).invoke('text').should('eql', newThresholdRule.description); + cy.get(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name} Beta`); + cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThresholdRule.description); cy.get(ABOUT_DETAILS).within(() => { - getDescriptionForTitle('Severity').invoke('text').should('eql', newThresholdRule.severity); - getDescriptionForTitle('Risk score').invoke('text').should('eql', newThresholdRule.riskScore); - getDescriptionForTitle('Reference URLs').invoke('text').should('eql', expectedUrls); - getDescriptionForTitle('False positive examples') - .invoke('text') - .should('eql', expectedFalsePositives); - getDescriptionForTitle('MITRE ATT&CK').invoke('text').should('eql', expectedMitre); - getDescriptionForTitle('Tags').invoke('text').should('eql', expectedTags); + getDetails(SEVERITY_DETAILS).should('have.text', newThresholdRule.severity); + getDetails(RISK_SCORE_DETAILS).should('have.text', newThresholdRule.riskScore); + getDetails(REFERENCE_URLS_DETAILS).should('have.text', expectedUrls); + getDetails(FALSE_POSITIVES_DETAILS).should('have.text', expectedFalsePositives); + getDetails(MITRE_ATTACK_DETAILS).should('have.text', expectedMitre); + getDetails(TAGS_DETAILS).should('have.text', expectedTags); }); - cy.get(RULE_ABOUT_DETAILS_HEADER_TOGGLE).eq(INVESTIGATION_NOTES_TOGGLE).click({ force: true }); - cy.get(ABOUT_INVESTIGATION_NOTES).invoke('text').should('eql', INVESTIGATION_NOTES_MARKDOWN); - + cy.get(ABOUT_INVESTIGATION_NOTES).should('have.text', INVESTIGATION_NOTES_MARKDOWN); cy.get(DEFINITION_DETAILS).within(() => { - getDescriptionForTitle('Index patterns') - .invoke('text') - .should('eql', expectedIndexPatterns.join('')); - getDescriptionForTitle('Custom query') - .invoke('text') - .should('eql', `${newThresholdRule.customQuery} `); - getDescriptionForTitle('Rule type').invoke('text').should('eql', 'Threshold'); - getDescriptionForTitle('Timeline template').invoke('text').should('eql', 'None'); - getDescriptionForTitle('Threshold') - .invoke('text') - .should( - 'eql', - `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` - ); + getDetails(INDEX_PATTERNS_DETAILS).should('have.text', indexPatterns.join('')); + getDetails(CUSTOM_QUERY_DETAILS).should('have.text', `${newThresholdRule.customQuery} `); + getDetails(RULE_TYPE_DETAILS).should('have.text', 'Threshold'); + getDetails(TIMELINE_TEMPLATE_DETAILS).should('have.text', 'None'); + getDetails(THRESHOLD_DETAILS).should( + 'have.text', + `Results aggregated by ${newThresholdRule.thresholdField} >= ${newThresholdRule.threshold}` + ); }); - cy.get(SCHEDULE_DETAILS).within(() => { - getDescriptionForTitle('Runs every').invoke('text').should('eql', '5m'); - getDescriptionForTitle('Additional look-back time').invoke('text').should('eql', '1m'); + getDetails(RUNS_EVERY_DETAILS).should( + 'have.text', + `${newThresholdRule.runsEvery.interval}${newThresholdRule.runsEvery.type}` + ); + getDetails(ADDITIONAL_LOOK_BACK_DETAILS).should( + 'have.text', + `${newThresholdRule.lookBack.interval}${newThresholdRule.lookBack.type}` + ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('be.below', 100); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', newThresholdRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'threshold'); + cy.get(ALERT_RULE_SEVERITY) + .first() + .should('have.text', newThresholdRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', newThresholdRule.riskScore); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts index 2fed23755963b..31d8e4666d91d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts @@ -35,7 +35,7 @@ describe('Alerts timeline', () => { .invoke('text') .then((eventId) => { investigateFirstAlertInTimeline(); - cy.get(PROVIDER_BADGE).invoke('text').should('eql', `_id: "${eventId}"`); + cy.get(PROVIDER_BADGE).should('have.text', `_id: "${eventId}"`); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts index d193330dc54ff..13fa9592469e4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts @@ -10,13 +10,10 @@ import { FIELDS_BROWSER_SELECTED_CATEGORY_TITLE, } from '../screens/fields_browser'; import { - EVENTS_PAGE, HEADER_SUBTITLE, HOST_GEO_CITY_NAME_HEADER, HOST_GEO_COUNTRY_NAME_HEADER, INSPECT_MODAL, - LOAD_MORE, - LOCAL_EVENTS_COUNT, } from '../screens/hosts/events'; import { HEADERS_GROUP } from '../screens/timeline'; @@ -47,8 +44,7 @@ const defaultHeadersInDefaultEcsCategory = [ { id: 'destination.ip' }, ]; -// https://github.com/elastic/kibana/issues/70757 -describe.skip('Events Viewer', () => { +describe('Events Viewer', () => { context('Fields rendering', () => { before(() => { loginAndWaitForPage(HOSTS_URL); @@ -144,18 +140,9 @@ describe.skip('Events Viewer', () => { cy.get(HEADER_SUBTITLE).invoke('text').should('not.equal', initialNumberOfEvents); }); }); - - it('loads more events when the load more button is clicked', () => { - const defaultNumberOfLoadedEvents = '25'; - cy.get(LOCAL_EVENTS_COUNT).invoke('text').should('equal', defaultNumberOfLoadedEvents); - - cy.get(LOAD_MORE).click({ force: true }); - - cy.get(LOCAL_EVENTS_COUNT).invoke('text').should('not.equal', defaultNumberOfLoadedEvents); - }); }); - context('Events columns', () => { + context.skip('Events columns', () => { before(() => { loginAndWaitForPage(HOSTS_URL); openEvents(); @@ -173,7 +160,7 @@ describe.skip('Events Viewer', () => { const expectedOrderAfterDragAndDrop = 'message@timestamphost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; - cy.get(EVENTS_PAGE).scrollTo('bottom'); + cy.scrollTo('bottom'); cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder); dragAndDropColumn({ column: 0, newPosition: 1 }); cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop); diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts index 0b302efd655a8..3b89163392626 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts @@ -24,7 +24,20 @@ import { mlNetworkSingleIpNullKqlQuery, } from '../urls/ml_conditional_links'; -describe('ml conditional links', () => { +// FLAKY: https://github.com/elastic/kibana/issues/78512 +// FLAKY: https://github.com/elastic/kibana/issues/78511 +// FLAKY: https://github.com/elastic/kibana/issues/78510 +// FLAKY: https://github.com/elastic/kibana/issues/78509 +// FLAKY: https://github.com/elastic/kibana/issues/78508 +// FLAKY: https://github.com/elastic/kibana/issues/78507 +// FLAKY: https://github.com/elastic/kibana/issues/78506 +// FLAKY: https://github.com/elastic/kibana/issues/78505 +// FLAKY: https://github.com/elastic/kibana/issues/78504 +// FLAKY: https://github.com/elastic/kibana/issues/78503 +// FLAKY: https://github.com/elastic/kibana/issues/78502 +// FLAKY: https://github.com/elastic/kibana/issues/78501 +// FLAKY: https://github.com/elastic/kibana/issues/78500 +describe.skip('ml conditional links', () => { it('sets the KQL from a single IP with a value for the query', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.get(KQL_INPUT) @@ -94,7 +107,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', - '/app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + 'app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -102,7 +115,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -110,7 +123,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27))' + 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -118,7 +131,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -126,7 +139,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkNullKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -134,7 +147,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery); cy.url().should( 'include', - '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))' + '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-08-28T11:00:00.000Z%27,kind:absolute,to:%272019-08-28T13:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -142,7 +155,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -150,7 +163,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -158,7 +171,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -166,7 +179,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -174,7 +187,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -182,7 +195,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); @@ -190,7 +203,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery); cy.url().should( 'include', - '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))' + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))&sourcerer=(default:!(%27auditbeat-*%27))' ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts index 5dc3182cd9f83..fdccf164c7465 100644 --- a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts @@ -35,6 +35,7 @@ describe('Pagination', () => { .then((processNameFirstPage) => { goToThirdPage(); waitForUncommonProcessesToBeLoaded(); + cy.wait(1500); cy.get(PROCESS_NAME_FIELD) .first() .invoke('text') diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index 6c1d73492f30a..906fba28a7721 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -14,7 +14,7 @@ import { import { HOSTS_NAMES } from '../screens/hosts/all_hosts'; import { ANOMALIES_TAB } from '../screens/hosts/main'; import { BREADCRUMBS, HOSTS, KQL_INPUT, NETWORK } from '../screens/security_header'; -import { SERVER_SIDE_EVENT_COUNT, TIMELINE_TITLE } from '../screens/timeline'; +import { TIMELINE_TITLE } from '../screens/timeline'; import { loginAndWaitForPage, loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { @@ -32,16 +32,18 @@ import { waitForIpsTableToBeLoaded } from '../tasks/network/flows'; import { clearSearchBar, kqlSearch, navigateFromHeaderTo } from '../tasks/security_header'; import { openTimelineUsingToggle } from '../tasks/security_main'; import { - addDescriptionToTimeline, addNameToTimeline, closeTimeline, - executeTimelineKQL, + populateTimeline, waitForTimelineChanges, } from '../tasks/timeline'; import { HOSTS_URL } from '../urls/navigation'; import { ABSOLUTE_DATE_RANGE } from '../urls/state'; +import { timeline } from '../objects/timeline'; +import { TIMELINE } from '../screens/create_new_case'; + const ABSOLUTE_DATE = { endTime: '2019-08-01T20:33:29.186Z', endTimeTimeline: '2019-08-02T21:03:29.186Z', @@ -165,7 +167,7 @@ describe('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2019-08-01T20:33:29.186Z')))` ); }); @@ -178,12 +180,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -194,21 +196,21 @@ describe('url state', () => { cy.get(ANOMALIES_TAB).should( 'have.attr', 'href', - "/app/security/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))" + "/app/security/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!('auditbeat-*'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))" ); cy.get(BREADCRUMBS) .eq(1) .should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` + `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&sourcerer=(default:!(\'auditbeat-*\'))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2020-01-01T21:33:29.186Z')))` ); }); @@ -221,23 +223,12 @@ describe('url state', () => { it('sets and reads the url state for timeline by id', () => { loginAndWaitForPage(HOSTS_URL); openTimelineUsingToggle(); - executeTimelineKQL('host.name: *'); - - cy.get(SERVER_SIDE_EVENT_COUNT) - .invoke('text') - .then((strCount) => { - const intCount = +strCount; - cy.wrap(intCount).should('be.above', 0); - }); + populateTimeline(); cy.server(); cy.route('PATCH', '**/api/timeline').as('timeline'); - const timelineName = 'Security'; - const timelineDescription = 'This is the best timeline of the world'; - addNameToTimeline(timelineName); - waitForTimelineChanges(); - addDescriptionToTimeline(timelineDescription); + addNameToTimeline(timeline.title); waitForTimelineChanges(); cy.wait('@timeline').then((response) => { @@ -248,9 +239,10 @@ describe('url state', () => { cy.visit('/app/home'); cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('exist'); - cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).invoke('text').should('not.equal', 'Updating'); + cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('not.have.text', 'Updating'); + cy.get(TIMELINE).should('be.visible'); cy.get(TIMELINE_TITLE).should('be.visible'); - cy.get(TIMELINE_TITLE).should('have.attr', 'value', timelineName); + cy.get(TIMELINE_TITLE).should('have.attr', 'value', timeline.title); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 2a5c60815f450..e84e2b7b1669f 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -23,6 +23,12 @@ interface SeverityOverride { sourceValue: string; } +interface Interval { + interval: string; + timeType: string; + type: string; +} + export interface CustomRule { customQuery: string; name: string; @@ -38,6 +44,8 @@ export interface CustomRule { mitre: Mitre[]; note: string; timelineId: string; + runsEvery: Interval; + lookBack: Interval; } export interface ThresholdRule extends CustomRule { @@ -65,6 +73,8 @@ export interface MachineLearningRule { falsePositivesExamples: string[]; mitre: Mitre[]; note: string; + runsEvery: Interval; + lookBack: Interval; } const mitre1: Mitre = { @@ -83,8 +93,8 @@ const severityOverride1: SeverityOverride = { }; const severityOverride2: SeverityOverride = { - sourceField: 'agent.type', - sourceValue: 'endpoint', + sourceField: '@timestamp', + sourceValue: '10/02/2020', }; const severityOverride3: SeverityOverride = { @@ -93,8 +103,20 @@ const severityOverride3: SeverityOverride = { }; const severityOverride4: SeverityOverride = { - sourceField: '@timestamp', - sourceValue: '10/02/2020', + sourceField: 'agent.type', + sourceValue: 'auditbeat', +}; + +const runsEvery: Interval = { + interval: '1', + timeType: 'Seconds', + type: 's', +}; + +const lookBack: Interval = { + interval: '17520', + timeType: 'Hours', + type: 'h', }; export const newRule: CustomRule = { @@ -109,6 +131,8 @@ export const newRule: CustomRule = { mitre: [mitre1, mitre2], note: '# test markdown', timelineId: '0162c130-78be-11ea-9718-118a926974a4', + runsEvery, + lookBack, }; export const existingRule: CustomRule = { @@ -132,6 +156,8 @@ export const existingRule: CustomRule = { mitre: [], note: 'This is my note', timelineId: '', + runsEvery, + lookBack, }; export const newOverrideRule: OverrideRule = { @@ -150,6 +176,8 @@ export const newOverrideRule: OverrideRule = { riskOverride: 'destination.port', nameOverride: 'agent.type', timestampOverride: '@timestamp', + runsEvery, + lookBack, }; export const newThresholdRule: ThresholdRule = { @@ -166,6 +194,8 @@ export const newThresholdRule: ThresholdRule = { timelineId: '0162c130-78be-11ea-9718-118a926974a4', thresholdField: 'host.name', threshold: '10', + runsEvery, + lookBack, }; export const machineLearningRule: MachineLearningRule = { @@ -180,6 +210,8 @@ export const machineLearningRule: MachineLearningRule = { falsePositivesExamples: ['False1'], mitre: [mitre1], note: '# test markdown', + runsEvery, + lookBack, }; export const eqlRule: CustomRule = { @@ -194,4 +226,24 @@ export const eqlRule: CustomRule = { mitre: [mitre1, mitre2], note: '# test markdown', timelineId: '0162c130-78be-11ea-9718-118a926974a4', + runsEvery, + lookBack, +}; + +export const indexPatterns = [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', +]; + +export const severitiesOverride = ['Low', 'Medium', 'High', 'Critical']; + +export const editedRule = { + ...existingRule, + severity: 'Medium', + description: 'Edited Rule description', }; diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index fb7e7e73986b9..ed05874bd4c4d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -4,45 +4,57 @@ * you may not use this file except in compliance with the Elastic License. */ -export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; +export const ALERTS = '[data-test-subj="event"]'; -export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]'; +export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; -export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-detection-rules"]'; +export const ALERT_ID = '[data-test-subj="draggable-content-_id"]'; -export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text'; +export const ALERT_RISK_SCORE_HEADER = '[data-test-subj="header-text-signal.rule.risk_score"]'; -export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; +export const ALERT_RULE_METHOD = '[data-test-subj="draggable-content-signal.rule.type"]'; -export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; +export const ALERT_RULE_NAME = '[data-test-subj="draggable-content-signal.rule.name"]'; -export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]'; +export const ALERT_RULE_RISK_SCORE = '[data-test-subj="draggable-content-signal.rule.risk_score"]'; -export const SELECTED_ALERTS = '[data-test-subj="selectedAlerts"]'; +export const ALERT_RULE_SEVERITY = '[data-test-subj="draggable-content-signal.rule.severity"]'; -export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; +export const ALERT_RULE_VERSION = '[data-test-subj="draggable-content-signal.rule.version"]'; -export const SHOWING_ALERTS = '[data-test-subj="showingAlerts"]'; +export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; -export const ALERTS = '[data-test-subj="event"]'; +export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closeSelectedAlertsButton"]'; -export const ALERT_ID = '[data-test-subj="draggable-content-_id"]'; +export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; -export const ALERT_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; +export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; -export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="alertActionPopover"] button'; +export const IN_PROGRESS_ALERTS_FILTER_BTN = '[data-test-subj="inProgressAlerts"]'; -export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]'; +export const LOADING_ALERTS_PANEL = '[data-test-subj="loading-alerts-panel"]'; -export const OPEN_SELECTED_ALERTS_BTN = '[data-test-subj="openSelectedAlertsButton"]'; +export const MANAGE_ALERT_DETECTION_RULES_BTN = '[data-test-subj="manage-alert-detection-rules"]'; -export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="closeSelectedAlertsButton"]'; +export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]'; export const MARK_SELECTED_ALERTS_IN_PROGRESS_BTN = '[data-test-subj="markSelectedAlertsInProgressButton"]'; +export const NUMBER_OF_ALERTS = '[data-test-subj="server-side-event-count"] .euiBadge__text'; + export const OPEN_ALERT_BTN = '[data-test-subj="open-alert-status"]'; -export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; +export const OPEN_SELECTED_ALERTS_BTN = '[data-test-subj="openSelectedAlertsButton"]'; -export const MARK_ALERT_IN_PROGRESS_BTN = '[data-test-subj="in-progress-alert-status"]'; +export const OPENED_ALERTS_FILTER_BTN = '[data-test-subj="openAlerts"]'; + +export const SELECTED_ALERTS = '[data-test-subj="selectedAlerts"]'; + +export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; + +export const SHOWING_ALERTS = '[data-test-subj="showingAlerts"]'; + +export const TAKE_ACTION_POPOVER_BTN = '[data-test-subj="alertActionPopover"] button'; + +export const TIMELINE_CONTEXT_MENU_BTN = '[data-test-subj="timeline-context-menu-button"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index dda371126d5aa..e1ab5ff30572f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -57,6 +57,12 @@ export const INVESTIGATION_NOTES_TEXTAREA = export const FALSE_POSITIVES_INPUT = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] input'; +export const LOOK_BACK_INTERVAL = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="interval"]'; + +export const LOOK_BACK_TIME_TYPE = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="timeType"]'; + export const MACHINE_LEARNING_DROPDOWN = '[data-test-subj="mlJobSelect"] button'; export const MACHINE_LEARNING_LIST = '.euiContextMenuItem__text'; @@ -73,6 +79,8 @@ export const MITRE_TECHNIQUES_INPUT = export const REFERENCE_URLS_INPUT = '[data-test-subj="detectionEngineStepAboutRuleReferenceUrls"] input'; +export const REFRESH_BUTTON = '[data-test-subj="refreshButton"]'; + export const RISK_INPUT = '.euiRangeInput'; export const RISK_MAPPING_OVERRIDE_OPTION = '#risk_score-mapping-override'; @@ -88,21 +96,29 @@ export const RULE_NAME_INPUT = export const RULE_NAME_OVERRIDE = '[data-test-subj="detectionEngineStepAboutRuleRuleNameOverride"]'; +export const RULE_STATUS = '[data-test-subj="ruleStatus"]'; + export const RULE_TIMESTAMP_OVERRIDE = '[data-test-subj="detectionEngineStepAboutRuleTimestampOverride"]'; +export const RUNS_EVERY_INTERVAL = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="interval"]'; + +export const RUNS_EVERY_TIME_TYPE = + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="timeType"]'; + export const SCHEDULE_CONTINUE_BUTTON = '[data-test-subj="schedule-continue"]'; export const SCHEDULE_EDIT_TAB = '[data-test-subj="edit-rule-schedule-tab"]'; export const SCHEDULE_INTERVAL_AMOUNT_INPUT = - '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-amount-input"]'; + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="interval"]'; export const SCHEDULE_INTERVAL_UNITS_INPUT = - '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="schedule-units-input"]'; + '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="timeType"]'; export const SCHEDULE_LOOKBACK_AMOUNT_INPUT = - '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-amount-input"]'; + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="timeType"]'; export const SCHEDULE_LOOKBACK_UNITS_INPUT = '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="schedule-units-input"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts index 05f517b5de662..0434de7bff88e 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts @@ -6,8 +6,6 @@ export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]'; -export const EVENTS_PAGE = '[data-test-subj="pageContainer"]'; - export const EVENTS_VIEWER_FIELDS_BUTTON = '[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]'; @@ -32,9 +30,6 @@ export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]'; export const INSPECT_QUERY = '[data-test-subj="events-viewer-panel"] [data-test-subj="inspect-icon-button"]'; -export const LOCAL_EVENTS_COUNT = - '[data-test-subj="events-viewer-panel"] [data-test-subj="local-events-count"'; - export const LOAD_MORE = '[data-test-subj="events-viewer-panel"] [data-test-subj="TimelineMoreButton"'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts index 98fc7b06a9908..5a376e95e38dd 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rule_details.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const getDescriptionForTitle = (title: string) => - cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); - -export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; -export const DETAILS_TITLE = '.euiDescriptionList__title'; - export const ABOUT_INVESTIGATION_NOTES = '[data-test-subj="stepAboutDetailsNoteContent"]'; export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; @@ -17,9 +11,23 @@ export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggl export const ABOUT_DETAILS = '[data-test-subj="aboutRule"] [data-test-subj="listItemColumnStepRuleDescription"]'; +export const ADDITIONAL_LOOK_BACK_DETAILS = 'Additional look-back time'; + +export const ANOMALY_SCORE_DETAILS = 'Anomaly score'; + +export const CUSTOM_QUERY_DETAILS = 'Custom query'; + export const DEFINITION_DETAILS = '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"]'; +export const DETAILS_DESCRIPTION = '.euiDescriptionList__description'; + +export const DETAILS_TITLE = '.euiDescriptionList__title'; + +export const FALSE_POSITIVES_DETAILS = 'False positive examples'; + +export const INDEX_PATTERNS_DETAILS = 'Index patterns'; + export const INVESTIGATION_NOTES_MARKDOWN = 'test markdown'; export const INVESTIGATION_NOTES_TOGGLE = 1; @@ -28,11 +36,38 @@ export const MACHINE_LEARNING_JOB_ID = '[data-test-subj="machineLearningJobId"]' export const MACHINE_LEARNING_JOB_STATUS = '[data-test-subj="machineLearningJobStatus"]'; +export const MITRE_ATTACK_DETAILS = 'MITRE ATT&CK'; + export const RULE_ABOUT_DETAILS_HEADER_TOGGLE = '[data-test-subj="stepAboutDetailsToggle"]'; export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; +export const RULE_NAME_OVERRIDE_DETAILS = 'Rule name override'; + +export const RISK_SCORE_DETAILS = 'Risk score'; + +export const RISK_SCORE_OVERRIDE_DETAILS = 'Risk score override'; + +export const REFERENCE_URLS_DETAILS = 'Reference URLs'; + +export const RULE_TYPE_DETAILS = 'Rule type'; + +export const RUNS_EVERY_DETAILS = 'Runs every'; + export const SCHEDULE_DETAILS = '[data-test-subj=schedule] [data-test-subj="listItemColumnStepRuleDescription"]'; export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; + +export const SEVERITY_DETAILS = 'Severity'; + +export const TAGS_DETAILS = 'Tags'; + +export const THRESHOLD_DETAILS = 'Threshold'; + +export const TIMELINE_TEMPLATE_DETAILS = 'Timeline template'; + +export const TIMESTAMP_OVERRIDE_DETAILS = 'Timestamp override'; + +export const getDetails = (title: string) => + cy.get(DETAILS_TITLE).contains(title).next(DETAILS_DESCRIPTION); diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index ebd37b522924f..c846ced2febfd 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -22,8 +22,10 @@ import { OPEN_SELECTED_ALERTS_BTN, MARK_ALERT_IN_PROGRESS_BTN, MARK_SELECTED_ALERTS_IN_PROGRESS_BTN, + ALERT_RISK_SCORE_HEADER, } from '../screens/alerts'; import { REFRESH_BUTTON } from '../screens/security_header'; +import { TIMELINE_COLUMN_SPINNER } from '../screens/timeline'; export const closeFirstAlert = () => { cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true }); @@ -40,7 +42,7 @@ export const expandFirstAlert = () => { }; export const goToClosedAlerts = () => { - cy.get(CLOSED_ALERTS_FILTER_BTN).click({ force: true }); + cy.get(CLOSED_ALERTS_FILTER_BTN).click(); }; export const goToManageAlertsDetectionRules = () => { @@ -62,7 +64,7 @@ export const openAlerts = () => { }; export const goToInProgressAlerts = () => { - cy.get(IN_PROGRESS_ALERTS_FILTER_BTN).click({ force: true }); + cy.get(IN_PROGRESS_ALERTS_FILTER_BTN).click(); }; export const markInProgressFirstAlert = () => { @@ -81,12 +83,18 @@ export const selectNumberOfAlerts = (numberOfAlerts: number) => { } }; +export const sortRiskScore = () => { + cy.get(ALERT_RISK_SCORE_HEADER).click(); + cy.get(TIMELINE_COLUMN_SPINNER).should('exist'); + cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); +}; + export const investigateFirstAlertInTimeline = () => { cy.get(SEND_ALERT_TO_TIMELINE_BTN).first().click({ force: true }); }; export const waitForAlerts = () => { - cy.get(REFRESH_BUTTON).invoke('text').should('not.equal', 'Updating'); + cy.get(REFRESH_BUTTON).should('not.have.text', 'Updating'); }; export const waitForAlertsIndexToBeCreated = () => { diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index dc89a39d082bc..914566a13a9a9 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -28,6 +28,8 @@ import { IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK, INPUT, INVESTIGATION_NOTES_TEXTAREA, + LOOK_BACK_INTERVAL, + LOOK_BACK_TIME_TYPE, MACHINE_LEARNING_DROPDOWN, MACHINE_LEARNING_LIST, MACHINE_LEARNING_TYPE, @@ -36,13 +38,17 @@ import { MITRE_TACTIC_DROPDOWN, MITRE_TECHNIQUES_INPUT, REFERENCE_URLS_INPUT, + REFRESH_BUTTON, RISK_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, RULE_NAME_INPUT, RULE_NAME_OVERRIDE, + RULE_STATUS, RULE_TIMESTAMP_OVERRIDE, + RUNS_EVERY_INTERVAL, + RUNS_EVERY_TIME_TYPE, SCHEDULE_CONTINUE_BUTTON, SCHEDULE_EDIT_TAB, SEVERITY_DROPDOWN, @@ -190,6 +196,13 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( cy.get(CUSTOM_QUERY_INPUT).should('not.exist'); }; +export const fillScheduleRuleAndContinue = (rule: CustomRule | MachineLearningRule) => { + cy.get(RUNS_EVERY_INTERVAL).clear().type(rule.runsEvery.interval); + cy.get(RUNS_EVERY_TIME_TYPE).select(rule.runsEvery.timeType); + cy.get(LOOK_BACK_INTERVAL).clear().type(rule.lookBack.interval); + cy.get(LOOK_BACK_TIME_TYPE).select(rule.lookBack.timeType); +}; + export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { const thresholdField = 0; const threshold = 1; @@ -251,6 +264,14 @@ export const selectThresholdRuleType = () => { cy.get(THRESHOLD_TYPE).click({ force: true }); }; +export const waitForTheRuleToBeExecuted = async () => { + let status = ''; + while (status !== 'succeeded') { + cy.get(REFRESH_BUTTON).click(); + status = await cy.get(RULE_STATUS).invoke('text').promisify(); + } +}; + export const selectEqlRuleType = () => { cy.get(EQL_TYPE).click({ force: true }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/security_header.ts b/x-pack/plugins/security_solution/cypress/tasks/security_header.ts index 7427104a9d889..28efc47120d32 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/security_header.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/security_header.ts @@ -21,3 +21,7 @@ export const navigateFromHeaderTo = (page: string) => { export const refreshPage = () => { cy.get(REFRESH_BUTTON).click({ force: true }).invoke('text').should('not.equal', 'Updating'); }; + +export const waitForThePageToBeUpdated = () => { + cy.get(REFRESH_BUTTON).should('not.equal', 'Updating'); +}; diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index 6982c200a5afd..4c9e3bc06037e 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -6,11 +6,12 @@ "license": "Elastic-License", "scripts": { "extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js && node ../../../scripts/eslint ./public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix", + "build-beat-doc": "node scripts/beat_docs/build.js && node ../../../scripts/eslint ./server/utils/beat_schema/fields.ts --fix", "build-graphql-types": "node scripts/generate_types_from_graphql.js", "cypress:open": "cypress open --config-file ./cypress/cypress.json", "cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/visual_config.ts", "cypress:run": "cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;", - "cypress:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", + "cypress:run-as-ci": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", "test:generate": "node scripts/endpoint/resolver_generator" }, "devDependencies": { diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index b4e9ba3dd7a71..54b02c374e43f 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -28,8 +28,6 @@ import { ApolloClientContext } from '../common/utils/apollo_context'; import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { StartServices } from '../types'; import { PageRouter } from './routes'; -import { ManageSource } from '../common/containers/sourcerer'; - interface StartAppComponent extends AppFrontendLibs { children: React.ReactNode; history: History; @@ -56,15 +54,13 @@ const StartAppComponent: FC = ({ children, apolloClient, hist - - - - - {children} - - - - + + + + {children} + + + diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index b48ae4e6e2d75..68eb93f7e2fe8 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -4,33 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; +import React, { useRef } from 'react'; import styled from 'styled-components'; import { TimelineId } from '../../../common/types/timeline'; import { DragDropContextWrapper } from '../../common/components/drag_and_drop/drag_drop_context_wrapper'; import { Flyout } from '../../timelines/components/flyout'; +import { SecuritySolutionAppWrapper } from '../../common/components/page'; import { HeaderGlobal } from '../../common/components/header_global'; import { HelpMenu } from '../../common/components/help_menu'; import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning'; import { UseUrlState } from '../../common/components/url_state'; -import { useWithSource } from '../../common/containers/source'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; import { navTabs } from './home_navigations'; -import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; -import { useUserInfo } from '../../detections/components/user_info'; - -const SecuritySolutionAppWrapper = styled.div` +import { useInitSourcerer, useSourcererScope } from '../../common/containers/sourcerer'; +import { useKibana } from '../../common/lib/kibana'; +import { DETECTIONS_SUB_PLUGIN_ID } from '../../../common/constants'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; +import { useUpgradeEndpointPackage } from '../../common/hooks/endpoint/upgrade'; +import { useThrottledResizeObserver } from '../../common/components/utils'; + +const Main = styled.main.attrs<{ paddingTop: number }>(({ paddingTop }) => ({ + style: { + paddingTop: `${paddingTop}px`, + }, +}))<{ paddingTop: number }>` + overflow: auto; display: flex; flex-direction: column; - height: 100%; - width: 100%; -`; -SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper'; - -const Main = styled.main` - overflow: auto; - flex: 1; + flex: 1 1 auto; `; Main.displayName = 'Main'; @@ -42,26 +44,33 @@ interface HomePageProps { } const HomePageComponent: React.FC = ({ children }) => { - const { signalIndexExists, signalIndexName } = useSignalIndex(); - - const indexToAdd = useMemo(() => { - if (signalIndexExists && signalIndexName != null) { - return [signalIndexName]; - } - return null; - }, [signalIndexExists, signalIndexName]); - + const { application } = useKibana().services; + const subPluginId = useRef(''); + const { ref, height = 0 } = useThrottledResizeObserver(300); + application.currentAppId$.subscribe((appId) => { + subPluginId.current = appId ?? ''; + }); + + useInitSourcerer( + subPluginId.current === DETECTIONS_SUB_PLUGIN_ID + ? SourcererScopeName.detections + : SourcererScopeName.default + ); const [showTimeline] = useShowTimeline(); - const { browserFields, indexPattern, indicesExist } = useWithSource('default', indexToAdd); - // side effect: this will attempt to create the signals index if it doesn't exist - useUserInfo(); + const { browserFields, indexPattern, indicesExist } = useSourcererScope(); + // side effect: this will attempt to upgrade the endpoint package if it is not up to date + // this will run when a user navigates to the Security Solution app and when they navigate between + // tabs in the app. This is useful for keeping the endpoint package as up to date as possible until + // a background task solution can be built on the server side. Once a background task solution is available we + // can remove this. + useUpgradeEndpointPackage(); return ( - + -
+
{indicesExist && showTimeline && ( diff --git a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx index a800bd690f710..a85d7a310bc06 100644 --- a/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/add_comment/index.test.tsx @@ -17,8 +17,7 @@ import { usePostComment } from '../../containers/use_post_comment'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index e6e0823214195..e301e80c9561d 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; import moment from 'moment-timezone'; - +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { AllCases } from '.'; import { TestProviders } from '../../../common/mock'; @@ -85,16 +85,6 @@ describe('AllCases', () => { let navigateToApp: jest.Mock; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ beforeEach(() => { jest.clearAllMocks(); navigateToApp = jest.fn(); @@ -106,36 +96,38 @@ describe('AllCases', () => { moment.tz.setDefault('UTC'); }); - it('should render AllCases', () => { + it('should render AllCases', async () => { const wrapper = mount( ); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().prop('href')).toEqual( - `/${useGetCasesMockState.data.cases[0].id}` - ); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().text()).toEqual( - useGetCasesMockState.data.cases[0].title - ); - expect( - wrapper.find(`span[data-test-subj="case-table-column-tags-0"]`).first().prop('title') - ).toEqual(useGetCasesMockState.data.cases[0].tags[0]); - expect(wrapper.find(`[data-test-subj="case-table-column-createdBy"]`).first().text()).toEqual( - useGetCasesMockState.data.cases[0].createdBy.fullName - ); - expect( - wrapper - .find(`[data-test-subj="case-table-column-createdAt"]`) - .first() - .childAt(0) - .prop('value') - ).toBe(useGetCasesMockState.data.cases[0].createdAt); - expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( - 'Showing 10 cases' - ); + await waitFor(() => { + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().prop('href')).toEqual( + `/${useGetCasesMockState.data.cases[0].id}` + ); + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().text()).toEqual( + useGetCasesMockState.data.cases[0].title + ); + expect( + wrapper.find(`span[data-test-subj="case-table-column-tags-0"]`).first().prop('title') + ).toEqual(useGetCasesMockState.data.cases[0].tags[0]); + expect(wrapper.find(`[data-test-subj="case-table-column-createdBy"]`).first().text()).toEqual( + useGetCasesMockState.data.cases[0].createdBy.fullName + ); + expect( + wrapper + .find(`[data-test-subj="case-table-column-createdAt"]`) + .first() + .childAt(0) + .prop('value') + ).toBe(useGetCasesMockState.data.cases[0].createdAt); + expect(wrapper.find(`[data-test-subj="case-table-case-count"]`).first().text()).toEqual( + 'Showing 10 cases' + ); + }); }); - it('should render empty fields', () => { + it('should render empty fields', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -166,53 +158,63 @@ describe('AllCases', () => { expect(column.find('.euiTableRowCell--hideForDesktop').text()).toEqual(columnName); expect(column.find('span').text()).toEqual(emptyTag); }; - getCasesColumns([], 'open', false).map((i, key) => i.name != null && checkIt(`${i.name}`, key)); + await waitFor(() => { + getCasesColumns([], 'open', false).map( + (i, key) => i.name != null && checkIt(`${i.name}`, key) + ); + }); }); - it('should not render case link or actions on modal=true', () => { + it('should not render case link or actions on modal=true', async () => { const wrapper = mount( ); - const checkIt = (columnName: string) => { - expect(columnName).not.toEqual(i18n.ACTIONS); - }; - getCasesColumns([], 'open', true).map((i, key) => i.name != null && checkIt(`${i.name}`)); - expect(wrapper.find(`a[data-test-subj="case-details-link"]`).exists()).toBeFalsy(); + await waitFor(() => { + const checkIt = (columnName: string) => { + expect(columnName).not.toEqual(i18n.ACTIONS); + }; + getCasesColumns([], 'open', true).map((i, key) => i.name != null && checkIt(`${i.name}`)); + expect(wrapper.find(`a[data-test-subj="case-details-link"]`).exists()).toBeFalsy(); + }); }); - it('should tableHeaderSortButton AllCases', () => { + it('should tableHeaderSortButton AllCases', async () => { const wrapper = mount( ); - wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); - expect(setQueryParams).toBeCalledWith({ - page: 1, - perPage: 5, - sortField: 'createdAt', - sortOrder: 'asc', + await waitFor(() => { + wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); + expect(setQueryParams).toBeCalledWith({ + page: 1, + perPage: 5, + sortField: 'createdAt', + sortOrder: 'asc', + }); }); }); - it('closes case when row action icon clicked', () => { + it('closes case when row action icon clicked', async () => { const wrapper = mount( ); - wrapper.find('[data-test-subj="action-close"]').first().simulate('click'); - const firstCase = useGetCasesMockState.data.cases[0]; - expect(dispatchUpdateCaseProperty).toBeCalledWith({ - caseId: firstCase.id, - updateKey: 'status', - updateValue: 'closed', - refetchCasesStatus: fetchCasesStatus, - version: firstCase.version, + await waitFor(() => { + wrapper.find('[data-test-subj="action-close"]').first().simulate('click'); + const firstCase = useGetCasesMockState.data.cases[0]; + expect(dispatchUpdateCaseProperty).toBeCalledWith({ + caseId: firstCase.id, + updateKey: 'status', + updateValue: 'closed', + refetchCasesStatus: fetchCasesStatus, + version: firstCase.version, + }); }); }); - it('opens case when row action icon clicked', () => { + it('opens case when row action icon clicked', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, filterOptions: { ...defaultGetCases.filterOptions, status: 'closed' }, @@ -223,17 +225,19 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="action-open"]').first().simulate('click'); - const firstCase = useGetCasesMockState.data.cases[0]; - expect(dispatchUpdateCaseProperty).toBeCalledWith({ - caseId: firstCase.id, - updateKey: 'status', - updateValue: 'open', - refetchCasesStatus: fetchCasesStatus, - version: firstCase.version, + await waitFor(() => { + wrapper.find('[data-test-subj="action-open"]').first().simulate('click'); + const firstCase = useGetCasesMockState.data.cases[0]; + expect(dispatchUpdateCaseProperty).toBeCalledWith({ + caseId: firstCase.id, + updateKey: 'status', + updateValue: 'open', + refetchCasesStatus: fetchCasesStatus, + version: firstCase.version, + }); }); }); - it('Bulk delete', () => { + it('Bulk delete', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -254,21 +258,23 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().simulate('click'); - expect(handleToggleModal).toBeCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-delete-button"]').first().simulate('click'); + expect(handleToggleModal).toBeCalled(); - wrapper - .find( - '[data-test-subj="confirm-delete-case-modal"] [data-test-subj="confirmModalConfirmButton"]' - ) - .last() - .simulate('click'); - expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual( - useGetCasesMockState.data.cases.map(({ id }) => ({ id })) - ); + wrapper + .find( + '[data-test-subj="confirm-delete-case-modal"] [data-test-subj="confirmModalConfirmButton"]' + ) + .last() + .simulate('click'); + expect(handleOnDeleteConfirm.mock.calls[0][0]).toStrictEqual( + useGetCasesMockState.data.cases.map(({ id }) => ({ id })) + ); + }); }); - it('Bulk close status update', () => { + it('Bulk close status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -279,11 +285,13 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-close-button"]').first().simulate('click'); - expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'closed'); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-close-button"]').first().simulate('click'); + expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'closed'); + }); }); - it('Bulk open status update', () => { + it('Bulk open status update', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, selectedCases: useGetCasesMockState.data.cases, @@ -298,11 +306,13 @@ describe('AllCases', () => { ); - wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); - wrapper.find('[data-test-subj="cases-bulk-open-button"]').first().simulate('click'); - expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'open'); + await waitFor(() => { + wrapper.find('[data-test-subj="case-table-bulk-actions"] button').first().simulate('click'); + wrapper.find('[data-test-subj="cases-bulk-open-button"]').first().simulate('click'); + expect(updateBulkStatus).toBeCalledWith(useGetCasesMockState.data.cases, 'open'); + }); }); - it('isDeleted is true, refetch', () => { + it('isDeleted is true, refetch', async () => { useDeleteCasesMock.mockReturnValue({ ...defaultDeleteCases, isDeleted: true, @@ -313,11 +323,13 @@ describe('AllCases', () => { ); - expect(refetchCases).toBeCalled(); - expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsDeleted).toBeCalled(); + await waitFor(() => { + expect(refetchCases).toBeCalled(); + expect(fetchCasesStatus).toBeCalled(); + expect(dispatchResetIsDeleted).toBeCalled(); + }); }); - it('isUpdated is true, refetch', () => { + it('isUpdated is true, refetch', async () => { useUpdateCasesMock.mockReturnValue({ ...defaultUpdateCases, isUpdated: true, @@ -328,42 +340,51 @@ describe('AllCases', () => { ); - expect(refetchCases).toBeCalled(); - expect(fetchCasesStatus).toBeCalled(); - expect(dispatchResetIsUpdated).toBeCalled(); + await waitFor(() => { + expect(refetchCases).toBeCalled(); + expect(fetchCasesStatus).toBeCalled(); + expect(dispatchResetIsUpdated).toBeCalled(); + }); }); - it('should not render header when modal=true', () => { + it('should not render header when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="all-cases-header"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="all-cases-header"]').exists()).toBe(false); + }); }); - it('should not render table utility bar when modal=true', () => { + it('should not render table utility bar when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="case-table-utility-bar-actions"]').exists()).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-table-utility-bar-actions"]').exists()).toBe( + false + ); + }); }); - it('case table should not be selectable when modal=true', () => { + it('case table should not be selectable when modal=true', async () => { const wrapper = mount( ); - - expect(wrapper.find('[data-test-subj="cases-table"]').first().prop('isSelectable')).toBe(false); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="cases-table"]').first().prop('isSelectable')).toBe( + false + ); + }); }); - it('should call onRowClick with no cases and modal=true', () => { + it('should call onRowClick with no cases and modal=true', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -378,12 +399,13 @@ describe('AllCases', () => { ); - - wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); - expect(onRowClick).toHaveBeenCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); + expect(onRowClick).toHaveBeenCalled(); + }); }); - it('should call navigateToApp with no cases and modal=false', () => { + it('should call navigateToApp with no cases and modal=false', async () => { useGetCasesMock.mockReturnValue({ ...defaultGetCases, data: { @@ -398,30 +420,33 @@ describe('AllCases', () => { ); - - wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); - expect(navigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: '/create' }); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-add-case"]').first().simulate('click'); + expect(navigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: '/create' }); + }); }); - it('should call onRowClick when clicking a case with modal=true', () => { + it('should call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( ); - - wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); - expect(onRowClick).toHaveBeenCalledWith('1'); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); + expect(onRowClick).toHaveBeenCalledWith('1'); + }); }); - it('should NOT call onRowClick when clicking a case with modal=true', () => { + it('should NOT call onRowClick when clicking a case with modal=true', async () => { const wrapper = mount( ); - - wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); - expect(onRowClick).not.toHaveBeenCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="cases-table-row-1"]').first().simulate('click'); + expect(onRowClick).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx index b93de014f5c18..725759068a3ea 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx @@ -5,6 +5,7 @@ */ import { mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { AllCasesModal } from '.'; import { TestProviders } from '../../../common/mock'; @@ -96,16 +97,6 @@ describe('AllCasesModal', () => { dispatchResetIsUpdated, updateBulkStatus, }; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ beforeEach(() => { jest.resetAllMocks(); useUpdateCasesMock.mockImplementation(() => defaultUpdateCases); @@ -114,41 +105,49 @@ describe('AllCasesModal', () => { useGetCasesStatusMock.mockImplementation(() => defaultCasesStatus); }); - it('renders with unselectable rows', () => { + it('renders with unselectable rows', async () => { const wrapper = mount( ); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); - expect(wrapper.find(EuiTableRow).first().prop('isSelectable')).toBeFalsy(); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeTruthy(); + expect(wrapper.find(EuiTableRow).first().prop('isSelectable')).toBeFalsy(); + }); }); - it('does not render modal if showCaseModal: false', () => { + it('does not render modal if showCaseModal: false', async () => { const wrapper = mount( ); - expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); + await waitFor(() => { + expect(wrapper.find(`[data-test-subj='all-cases-modal']`).exists()).toBeFalsy(); + }); }); - it('onRowClick called when row is clicked', () => { + it('onRowClick called when row is clicked', async () => { const wrapper = mount( ); - const firstRow = wrapper.find(EuiTableRow).first(); - firstRow.simulate('click'); - expect(onRowClick.mock.calls[0][0]).toEqual(basicCaseId); + await waitFor(() => { + const firstRow = wrapper.find(EuiTableRow).first(); + firstRow.simulate('click'); + expect(onRowClick.mock.calls[0][0]).toEqual(basicCaseId); + }); }); - it('Closing modal calls onCloseCaseModal', () => { + it('Closing modal calls onCloseCaseModal', async () => { const wrapper = mount( ); - const modalClose = wrapper.find('.euiModal__closeIcon').first(); - modalClose.simulate('click'); - expect(onCloseCaseModal).toBeCalled(); + await waitFor(() => { + const modalClose = wrapper.find('.euiModal__closeIcon').first(); + modalClose.simulate('click'); + expect(onCloseCaseModal).toBeCalled(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx index 0ac6093f2ee04..4f7b17a730b6a 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx @@ -9,7 +9,9 @@ import React from 'react'; import { HeaderPage, HeaderPageProps } from '../../../common/components/header_page'; import * as i18n from './translations'; -const CaseHeaderPageComponent: React.FC = (props) => ; +const CaseHeaderPageComponent: React.FC = (props) => ( + +); CaseHeaderPageComponent.defaultProps = { badgeOptions: { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx index 246df1c94b817..3859b4527991b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.test.tsx @@ -15,9 +15,7 @@ import { TestProviders } from '../../../common/mock'; import { useUpdateCase } from '../../containers/use_update_case'; import { useGetCase } from '../../containers/use_get_case'; import { useGetCaseUserActions } from '../../containers/use_get_case_user_actions'; - -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { useConnectors } from '../../containers/configure/use_connectors'; import { connectorsMock } from '../../containers/configure/mock'; @@ -29,6 +27,7 @@ jest.mock('../../containers/use_get_case_user_actions'); jest.mock('../../containers/use_get_case'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); +jest.mock('../user_action_tree/user_action_timestamp'); const useUpdateCaseMock = useUpdateCase as jest.Mock; const useGetCaseUserActionsMock = useGetCaseUserActions as jest.Mock; @@ -63,16 +62,6 @@ describe('CaseView ', () => { updateCase, fetchCase, }; - /* eslint-disable no-console */ - // Silence until enzyme fixed to use ReactTestUtils.act() - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const defaultUpdateCaseState = { isLoading: false, @@ -96,6 +85,7 @@ describe('CaseView ', () => { beforeEach(() => { jest.resetAllMocks(); useUpdateCaseMock.mockImplementation(() => defaultUpdateCaseState); + jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation); useGetCaseUserActionsMock.mockImplementation(() => defaultUseGetCaseUserActions); usePostPushToServiceMock.mockImplementation(() => ({ isLoading: false, postPushToService })); @@ -191,7 +181,7 @@ describe('CaseView ', () => { }); }); - it('should display EditableTitle isLoading', () => { + it('should display EditableTitle isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -204,13 +194,17 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="editable-title-loading"]').first().exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists() - ).toBeFalsy(); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="editable-title-loading"]').first().exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists() + ).toBeFalsy(); + }); }); - it('should display Toggle Status isLoading', () => { + it('should display Toggle Status isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -223,12 +217,14 @@ describe('CaseView ', () => { ); - expect( - wrapper.find('[data-test-subj="toggle-case-status"]').first().prop('isLoading') - ).toBeTruthy(); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="toggle-case-status"]').first().prop('isLoading') + ).toBeTruthy(); + }); }); - it('should display description isLoading', () => { + it('should display description isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -241,21 +237,25 @@ describe('CaseView ', () => { ); - expect( - wrapper - .find('[data-test-subj="description-action"] [data-test-subj="user-action-title-loading"]') - .first() - .exists() - ).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="description-action"] [data-test-subj="property-actions"]') - .first() - .exists() - ).toBeFalsy(); + await waitFor(() => { + expect( + wrapper + .find( + '[data-test-subj="description-action"] [data-test-subj="user-action-title-loading"]' + ) + .first() + .exists() + ).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="description-action"] [data-test-subj="property-actions"]') + .first() + .exists() + ).toBeFalsy(); + }); }); - it('should display tags isLoading', () => { + it('should display tags isLoading', async () => { useUpdateCaseMock.mockImplementation(() => ({ ...defaultUpdateCaseState, isLoading: true, @@ -268,16 +268,18 @@ describe('CaseView ', () => { ); - expect( - wrapper - .find('[data-test-subj="case-view-tag-list"] [data-test-subj="tag-list-loading"]') - .first() - .exists() - ).toBeTruthy(); - expect(wrapper.find('[data-test-subj="tag-list-edit"]').first().exists()).toBeFalsy(); + await waitFor(() => { + expect( + wrapper + .find('[data-test-subj="case-view-tag-list"] [data-test-subj="tag-list-loading"]') + .first() + .exists() + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="tag-list-edit"]').first().exists()).toBeFalsy(); + }); }); - it('should update title', () => { + it('should update title', async () => { const wrapper = mount( @@ -285,21 +287,23 @@ describe('CaseView ', () => { ); - const newTitle = 'The new title'; - wrapper.find(`[data-test-subj="editable-title-edit-icon"]`).first().simulate('click'); - wrapper.update(); - wrapper - .find(`[data-test-subj="editable-title-input-field"]`) - .last() - .simulate('change', { target: { value: newTitle } }); - - wrapper.update(); - wrapper.find(`[data-test-subj="editable-title-submit-btn"]`).first().simulate('click'); - - wrapper.update(); - const updateObject = updateCaseProperty.mock.calls[0][0]; - expect(updateObject.updateKey).toEqual('title'); - expect(updateObject.updateValue).toEqual(newTitle); + await waitFor(() => { + const newTitle = 'The new title'; + wrapper.find(`[data-test-subj="editable-title-edit-icon"]`).first().simulate('click'); + wrapper.update(); + wrapper + .find(`[data-test-subj="editable-title-input-field"]`) + .last() + .simulate('change', { target: { value: newTitle } }); + + wrapper.update(); + wrapper.find(`[data-test-subj="editable-title-submit-btn"]`).first().simulate('click'); + + wrapper.update(); + const updateObject = updateCaseProperty.mock.calls[0][0]; + expect(updateObject.updateKey).toEqual('title'); + expect(updateObject.updateValue).toEqual(newTitle); + }); }); it('should push updates on button click', async () => { @@ -329,7 +333,7 @@ describe('CaseView ', () => { }); }); - it('should return null if error', () => { + it('should return null if error', async () => { (useGetCase as jest.Mock).mockImplementation(() => ({ ...defaultGetCase, isError: true, @@ -346,10 +350,12 @@ describe('CaseView ', () => { ); - expect(wrapper).toEqual({}); + await waitFor(() => { + expect(wrapper).toEqual({}); + }); }); - it('should return spinner if loading', () => { + it('should return spinner if loading', async () => { (useGetCase as jest.Mock).mockImplementation(() => ({ ...defaultGetCase, isLoading: true, @@ -366,10 +372,12 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="case-view-loading"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-view-loading"]').exists()).toBeTruthy(); + }); }); - it('should return case view when data is there', () => { + it('should return case view when data is there', async () => { (useGetCase as jest.Mock).mockImplementation(() => defaultGetCase); const wrapper = mount( @@ -383,10 +391,12 @@ describe('CaseView ', () => { ); - expect(wrapper.find('[data-test-subj="case-view-title"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="case-view-title"]').exists()).toBeTruthy(); + }); }); - it('should refresh data on refresh', () => { + it('should refresh data on refresh', async () => { (useGetCase as jest.Mock).mockImplementation(() => defaultGetCase); const wrapper = mount( @@ -400,12 +410,14 @@ describe('CaseView ', () => { ); - wrapper.find('[data-test-subj="case-refresh"]').first().simulate('click'); - expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); - expect(fetchCase).toBeCalled(); + await waitFor(() => { + wrapper.find('[data-test-subj="case-refresh"]').first().simulate('click'); + expect(fetchCaseUserActions).toBeCalledWith(caseProps.caseData.id); + expect(fetchCase).toBeCalled(); + }); }); - it('should disable the push button when connector is invalid', () => { + it('should disable the push button when connector is invalid', async () => { useGetCaseUserActionsMock.mockImplementation(() => ({ ...defaultUseGetCaseUserActions, hasDataToPush: true, @@ -424,10 +436,11 @@ describe('CaseView ', () => { ); - - expect( - wrapper.find('button[data-test-subj="push-to-external-service"]').first().prop('disabled') - ).toBeTruthy(); + await waitFor(() => { + expect( + wrapper.find('button[data-test-subj="push-to-external-service"]').first().prop('disabled') + ).toBeTruthy(); + }); }); it('should revert to the initial connector in case of failure', async () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index b23169af6ceb3..750ff49cd700c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -294,6 +294,7 @@ export const CaseComponent = React.memo( { } if (isLoading) { return ( - + diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 4f1e45ae7c115..d27f00aacff2c 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { mount } from 'enzyme'; - import { Create } from '.'; import { TestProviders } from '../../../common/mock'; import { getFormMock } from '../__mock__/form'; @@ -19,9 +18,16 @@ import { useGetTags } from '../../containers/use_get_tags'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useFormData } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form_data'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name + EuiFieldText: () => , + }; +}); jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline'); jest.mock('../../containers/use_post_case'); @@ -74,16 +80,6 @@ const defaultPostCase = { postCase, }; describe('Create case', () => { - // Suppress warnings about "noSuggestions" prop - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const fetchTags = jest.fn(); const formHookMock = getFormMock(sampleData); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx index e531b71e8c90c..12d549a2f71a9 100644 --- a/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/edit_connector/index.test.tsx @@ -11,9 +11,8 @@ import { EditConnector } from './index'; import { getFormMock, useFormMock } from '../__mock__/form'; import { TestProviders } from '../../../common/mock'; import { connectorsMock } from '../../containers/configure/mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; + jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' ); @@ -67,10 +66,8 @@ describe('EditConnector ', () => { expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); - await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector)); - }); + wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); + await waitFor(() => expect(onSubmit.mock.calls[0][0]).toBe(sampleConnector)); }); it('Revert to initial external service on error', async () => { @@ -90,12 +87,10 @@ describe('EditConnector ', () => { expect(wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().exists()).toBeTruthy(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connector', 'none'); - }); + wrapper.find(`[data-test-subj="edit-connectors-submit"]`).last().simulate('click'); + await waitFor(() => { + wrapper.update(); + expect(formHookMock.setFieldValue).toHaveBeenCalledWith('connector', 'none'); }); }); @@ -114,15 +109,13 @@ describe('EditConnector ', () => { wrapper.find('button[data-test-subj="dropdown-connector-servicenow-2"]').simulate('click'); wrapper.update(); - await act(async () => { - wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(formHookMock.setFieldValue).toBeCalledWith( - 'connector', - defaultProps.selectedConnector - ); - }); + wrapper.find(`[data-test-subj="edit-connectors-cancel"]`).last().simulate('click'); + await waitFor(() => { + wrapper.update(); + expect(formHookMock.setFieldValue).toBeCalledWith( + 'connector', + defaultProps.selectedConnector + ); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx index a60167a18762f..013f7bd0a9ba7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/tag_list/index.test.tsx @@ -6,13 +6,11 @@ import React from 'react'; import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import { TagList } from '.'; import { getFormMock } from '../__mock__/form'; import { TestProviders } from '../../../common/mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { useGetTags } from '../../containers/use_get_tags'; @@ -27,6 +25,14 @@ jest.mock( children({ tags: ['rad', 'dude'] }), }) ); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name + EuiFieldText: () => , + }; +}); const onSubmit = jest.fn(); const defaultProps = { disabled: false, @@ -36,16 +42,6 @@ const defaultProps = { }; describe('TagList ', () => { - // Suppress warnings about "noSuggestions" prop - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - /* eslint-enable no-console */ const sampleTags = ['coke', 'pepsi']; const fetchTags = jest.fn(); const formHookMock = getFormMock({ tags: sampleTags }); @@ -78,10 +74,8 @@ describe('TagList ', () => { ); wrapper.find(`[data-test-subj="tag-list-edit-button"]`).last().simulate('click'); - await act(async () => { - wrapper.find(`[data-test-subj="edit-tags-submit"]`).last().simulate('click'); - await waitFor(() => expect(onSubmit).toBeCalledWith(sampleTags)); - }); + wrapper.find(`[data-test-subj="edit-tags-submit"]`).last().simulate('click'); + await waitFor(() => expect(onSubmit).toBeCalledWith(sampleTags)); }); it('Tag options render with new tags added', () => { @@ -96,7 +90,7 @@ describe('TagList ', () => { ).toEqual([{ label: 'coke' }, { label: 'pepsi' }, { label: 'rad' }, { label: 'dude' }]); }); - it('Cancels on cancel', async () => { + it('Cancels on cancel', () => { const props = { ...defaultProps, tags: ['pepsi'], @@ -109,14 +103,11 @@ describe('TagList ', () => { expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); wrapper.find(`[data-test-subj="tag-list-edit-button"]`).last().simulate('click'); - await act(async () => { - expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeFalsy(); - wrapper.find(`[data-test-subj="edit-tags-cancel"]`).last().simulate('click'); - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); - }); - }); + + expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeFalsy(); + wrapper.find(`[data-test-subj="edit-tags-cancel"]`).last().simulate('click'); + wrapper.update(); + expect(wrapper.find(`[data-test-subj="tag-pepsi"]`).last().exists()).toBeTruthy(); }); it('Renders disabled button', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx index f7fc7963b3844..445ae675007cc 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_all_cases_modal/index.tsx @@ -5,13 +5,13 @@ */ import React, { useState, useCallback, useMemo } from 'react'; +import { useDispatch } from 'react-redux'; -import { useDispatch, useSelector } from 'react-redux'; +import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { APP_ID } from '../../../../common/constants'; import { SecurityPageName } from '../../../app/types'; import { useKibana } from '../../../common/lib/kibana'; import { getCaseDetailsUrl, getCreateCaseUrl } from '../../../common/components/link_to'; -import { State } from '../../../common/store'; import { setInsertTimeline } from '../../../timelines/store/timeline/actions'; import { timelineSelectors } from '../../../timelines/store/timeline'; @@ -34,7 +34,7 @@ export const useAllCasesModal = ({ }: UseAllCasesModalProps): UseAllCasesModalReturnedValues => { const dispatch = useDispatch(); const { navigateToApp } = useKibana().services.application; - const timeline = useSelector((state: State) => + const timeline = useShallowEqualSelector((state) => timelineSelectors.selectTimeline(state, timelineId) ); diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx index d2bb2fb243458..4d9b7d030fec0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/index.test.tsx @@ -6,9 +6,7 @@ import React from 'react'; import { mount } from 'enzyme'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router'; import { getFormMock, useFormMock, useFormDataMock } from '../__mock__/form'; @@ -34,6 +32,7 @@ const defaultProps = { }; const useUpdateCommentMock = useUpdateComment as jest.Mock; jest.mock('../../containers/use_update_comment'); +jest.mock('./user_action_timestamp'); const patchComment = jest.fn(); describe('UserActionTree ', () => { @@ -91,16 +90,14 @@ describe('UserActionTree ', () => { }, caseUserActions: ourActions, }; - - await act(async () => { - const wrapper = mount( - - - - - - ); - + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect(wrapper.find(`[data-test-subj="top-footer"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toBeTruthy(); }); @@ -122,14 +119,14 @@ describe('UserActionTree ', () => { }, }; - await act(async () => { - const wrapper = mount( - - - - - - ); + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect(wrapper.find(`[data-test-subj="top-footer"]`).exists()).toBeTruthy(); expect(wrapper.find(`[data-test-subj="bottom-footer"]`).exists()).toBeFalsy(); }); @@ -142,15 +139,15 @@ describe('UserActionTree ', () => { caseUserActions: ourActions, }; - await act(async () => { - const wrapper = mount( - - - - - - ); + const wrapper = mount( + + + + + + ); + await waitFor(() => { expect( wrapper .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) @@ -165,34 +162,32 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) - .first() - .hasClass('outlined') - ).toBeTruthy(); - }); + wrapper.update(); + expect( + wrapper + .find(`[data-test-subj="comment-create-action-${props.data.comments[0].id}"]`) + .first() + .hasClass('outlined') + ).toBeTruthy(); }); }); it('Switches to markdown when edit is clicked and back to panel when canceled', async () => { - await waitFor(() => { - const ourActions = [getUserAction(['comment'], 'create')]; - const props = { - ...defaultProps, - caseUserActions: ourActions, - }; - - const wrapper = mount( - - - - - - ); + const ourActions = [getUserAction(['comment'], 'create')]; + const props = { + ...defaultProps, + caseUserActions: ourActions, + }; + const wrapper = mount( + + + + + + ); + + await waitFor(() => { expect( wrapper .find( @@ -278,24 +273,22 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await act(async () => { - await waitFor(() => { - wrapper.update(); - expect( - wrapper - .find( - `[data-test-subj="comment-create-action-${props.data.comments[0].id}"] [data-test-subj="user-action-markdown-form"]` - ) - .exists() - ).toEqual(false); - expect(patchComment).toBeCalledWith({ - commentUpdate: sampleData.content, - caseId: props.data.id, - commentId: props.data.comments[0].id, - fetchUserActions, - updateCase, - version: props.data.comments[0].version, - }); + await waitFor(() => { + wrapper.update(); + expect( + wrapper + .find( + `[data-test-subj="comment-create-action-${props.data.comments[0].id}"] [data-test-subj="user-action-markdown-form"]` + ) + .exists() + ).toEqual(false); + expect(patchComment).toBeCalledWith({ + commentUpdate: sampleData.content, + caseId: props.data.id, + commentId: props.data.comments[0].id, + fetchUserActions, + updateCase, + version: props.data.comments[0].version, }); }); }); @@ -320,84 +313,80 @@ describe('UserActionTree ', () => { .first() .simulate('click'); - await act(async () => { - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="user-action-save-markdown"]`) - .first() - .simulate('click'); - }); - - wrapper.update(); + wrapper + .find(`[data-test-subj="description-action"] [data-test-subj="user-action-save-markdown"]`) + .first() + .simulate('click'); + await waitFor(() => { + wrapper.update(); - expect( - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="user-action-markdown-form"]`) - .exists() - ).toEqual(false); + expect( + wrapper + .find( + `[data-test-subj="description-action"] [data-test-subj="user-action-markdown-form"]` + ) + .exists() + ).toEqual(false); - expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content }); + expect(onUpdateField).toBeCalledWith({ key: 'description', value: sampleData.content }); + }); }); it('quotes', async () => { - await act(async () => { - const commentData = { - comment: '', - }; - const setFieldValue = jest.fn(); - - const formHookMock = getFormMock(commentData); - useFormMock.mockImplementation(() => ({ form: { ...formHookMock, setFieldValue } })); - - const props = defaultProps; - const wrapper = mount( - - - - - - ); + const commentData = { + comment: '', + }; + const setFieldValue = jest.fn(); - wrapper - .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-ellipses"]`) - .first() - .simulate('click'); + const formHookMock = getFormMock(commentData); + useFormMock.mockImplementation(() => ({ form: { ...formHookMock, setFieldValue } })); - await waitFor(() => { - wrapper.update(); - }); + const props = defaultProps; + const wrapper = mount( + + + + + + ); + + wrapper + .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-ellipses"]`) + .first() + .simulate('click'); + + await waitFor(() => { + wrapper.update(); wrapper .find(`[data-test-subj="description-action"] [data-test-subj="property-actions-quote"]`) .first() .simulate('click'); - - expect(setFieldValue).toBeCalledWith('comment', `> ${props.data.description} \n`); }); + + expect(setFieldValue).toBeCalledWith('comment', `> ${props.data.description} \n`); }); it('Outlines comment when url param is provided', async () => { const commentId = 'basic-comment-id'; jest.spyOn(routeData, 'useParams').mockReturnValue({ commentId }); - await act(async () => { - const ourActions = [getUserAction(['comment'], 'create')]; - const props = { - ...defaultProps, - caseUserActions: ourActions, - }; - - const wrapper = mount( - - - - - - ); - - await waitFor(() => { - wrapper.update(); - }); + const ourActions = [getUserAction(['comment'], 'create')]; + const props = { + ...defaultProps, + caseUserActions: ourActions, + }; + + const wrapper = mount( + + + + + + ); + await waitFor(() => { + wrapper.update(); expect( wrapper .find(`[data-test-subj="comment-create-action-${commentId}"]`) diff --git a/x-pack/plugins/security_solution/public/cases/components/wrappers/index.tsx b/x-pack/plugins/security_solution/public/cases/components/wrappers/index.tsx index 06715514e01bf..b89a7e8eefec3 100644 --- a/x-pack/plugins/security_solution/public/cases/components/wrappers/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/wrappers/index.tsx @@ -10,8 +10,7 @@ import { gutterTimeline } from '../../../common/lib/helpers'; export const WhitePageWrapper = styled.div` background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; border-top: ${({ theme }) => theme.eui.euiBorderThin}; - height: 100%; - min-height: 100vh; + flex: 1 1 auto; `; export const SectionWrapper = styled.div` diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx index eced73e9c3d67..c8e12adef656a 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx @@ -6,7 +6,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import { apolloClientObservable, mockGlobalState, @@ -157,39 +157,40 @@ describe('AddFilterToGlobalSearchBar Component', () => { ); + await waitFor(() => { + wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); + wrapper.update(); + jest.runAllTimers(); + wrapper.update(); - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - - wrapper - .find('[data-test-subj="hover-actions-container"] [data-euiicon-type]') - .first() - .simulate('click'); - wrapper.update(); + wrapper + .find('[data-test-subj="hover-actions-container"] [data-euiicon-type]') + .first() + .simulate('click'); + wrapper.update(); - expect(mockAddFilters.mock.calls[0][0]).toEqual({ - meta: { - alias: null, - disabled: false, - key: 'host.name', - negate: false, - params: { - query: 'siem-kibana', - }, - type: 'phrase', - value: 'siem-kibana', - }, - query: { - match: { - 'host.name': { + expect(mockAddFilters.mock.calls[0][0]).toEqual({ + meta: { + alias: null, + disabled: false, + key: 'host.name', + negate: false, + params: { query: 'siem-kibana', - type: 'phrase', }, + type: 'phrase', + value: 'siem-kibana', }, - }, + query: { + match: { + 'host.name': { + query: 'siem-kibana', + type: 'phrase', + }, + }, + }, + }); + expect(onFilterAdded).toHaveBeenCalledTimes(1); }); - expect(onFilterAdded).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx index 00879ace040b9..8617a65dd7305 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx @@ -13,6 +13,7 @@ import { alertsDefaultModel } from './default_headers'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; import * as i18n from './translations'; import { useKibana } from '../../lib/kibana'; +import { SourcererScopeName } from '../../store/sourcerer/model'; export interface OwnProps { end: string; @@ -89,6 +90,7 @@ const AlertsTableComponent: React.FC = ({ defaultModel={alertsDefaultModel} end={endDate} id={timelineId} + scopeId={SourcererScopeName.default} start={startDate} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx index d522e372d7734..0dcd29a2d965b 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx @@ -24,6 +24,7 @@ const AlertsViewComponent: React.FC = ({ deleteQuery, endDate, filterQuery, + indexNames, pageFilters, setQuery, startDate, @@ -62,6 +63,7 @@ const AlertsViewComponent: React.FC = ({ endDate={endDate} filterQuery={filterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...alertsHistogramConfigs} diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts index b2637eeb2c65e..280b9111042d0 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts @@ -15,7 +15,7 @@ type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps; export interface AlertsComponentsProps extends Pick< CommonQueryProps, - 'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate' + 'deleteQuery' | 'endDate' | 'filterQuery' | 'indexNames' | 'skip' | 'setQuery' | 'startDate' > { timelineId: TimelineIdLiteral; pageFilters: Filter[]; diff --git a/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx index 7465d3ca1e63a..af27f6ffabbf6 100644 --- a/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/and_or_badge/__examples__/index.stories.tsx @@ -18,7 +18,7 @@ const withTheme = (storyFn: () => ReactNode) => ( ({ eui: euiLightVars, darkMode: true })}>{storyFn()} ); -storiesOf('components/AndOrBadge', module) +storiesOf('Components/AndOrBadge', module) .addDecorator(withTheme) .add('and', () => ) .add('or', () => ) diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx index eef6e09d496db..e38aaeedad8fd 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_lists.test.tsx @@ -8,8 +8,7 @@ import { ThemeProvider } from 'styled-components'; import { mount } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; import { ListSchema } from '../../../lists_plugin_deps'; diff --git a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts index bbcbcbcf928b3..225b407e4649e 100644 --- a/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import moment from 'moment'; import '../../../common/mock/match_media'; import { getField } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; @@ -17,6 +17,8 @@ import { import { getOperators, paramIsValid, getGenericComboBoxProps } from './helpers'; describe('helpers', () => { + // @ts-ignore + moment.suppressDeprecationWarnings = true; describe('#getOperators', () => { test('it returns "isOperator" if passed in field is "undefined"', () => { const operator = getOperators(undefined); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx index 64c8fde87a6bc..aa638abf65f7e 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.test.tsx @@ -13,6 +13,7 @@ import { ThemeProvider } from 'styled-components'; import { escapeDataProviderId } from '../drag_and_drop/helpers'; import { TestProviders } from '../../mock'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { BarChartBaseComponent, BarChartComponent } from './barchart'; import { ChartSeriesData } from './common'; @@ -131,19 +132,6 @@ const mockConfig = { customHeight: 324, }; -// Suppress warnings about "react-beautiful-dnd" -/* eslint-disable no-console */ -const originalError = console.error; -const originalWarn = console.warn; -beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalError; - console.warn = originalWarn; -}); - describe('BarChartBaseComponent', () => { let shallowWrapper: ShallowWrapper; const mockBarChartData: ChartSeriesData[] = [ @@ -350,7 +338,10 @@ describe.each(chartDataSets)('BarChart with stackByField', () => { )}-${escapeDataProviderId(datum.key)}`; expect( - wrapper.find(`div [data-rbd-draggable-id="${dataProviderId}"]`).first().text() + wrapper + .find(`[draggableId="${dataProviderId}"] [data-test-subj="providerContainer"]`) + .first() + .text() ).toEqual(datum.key); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx index 8fd2fa1fdef12..ffc2404bd4321 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { TestProviders } from '../../mock'; import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend'; @@ -58,20 +59,6 @@ const legendItems: LegendItem[] = [ describe('DraggableLegend', () => { const height = 400; - - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - describe('rendering', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx index 9f6e614c3c285..72e44da3297ea 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import '../../mock/match_media'; +import '../../mock/react_beautiful_dnd'; import { TestProviders } from '../../mock'; import { DraggableLegendItem, LegendItem } from './draggable_legend_item'; @@ -17,19 +18,6 @@ import { DraggableLegendItem, LegendItem } from './draggable_legend_item'; const theme = () => ({ eui: euiDarkVars, darkMode: true }); describe('DraggableLegendItem', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - describe('rendering a regular (non "All others") legend item', () => { const legendItem: LegendItem = { color: '#1EA593', diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx index 9e8bde8d9ff92..eaaba90b35634 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.test.tsx @@ -6,9 +6,8 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -20,11 +19,9 @@ describe('DragDropContextWrapper', () => { const wrapper = shallow( - - - {message} - - + + {message} + ); expect(wrapper.find('DragDropContextWrapper')).toMatchSnapshot(); @@ -35,11 +32,9 @@ describe('DragDropContextWrapper', () => { const wrapper = mount( - - - {message} - - + + {message} + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx index ebfa9ac22bdc7..5223452c8b93d 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.test.tsx @@ -6,11 +6,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; import { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -30,11 +29,9 @@ describe('DraggableWrapper', () => { test('it renders against the snapshot', () => { const wrapper = shallow( - - - message} /> - - + + message} /> + ); @@ -44,11 +41,9 @@ describe('DraggableWrapper', () => { test('it renders the children passed to the render prop', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); @@ -58,33 +53,31 @@ describe('DraggableWrapper', () => { test('it does NOT render hover actions when the mouse is NOT over the draggable wrapper', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(false); }); - test('it renders hover actions when the mouse is over the text of draggable wrapper', () => { + test('it renders hover actions when the mouse is over the text of draggable wrapper', async () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); - wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); - wrapper.update(); - jest.runAllTimers(); - wrapper.update(); - expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + await waitFor(() => { + wrapper.find('[data-test-subj="withHoverActionsButton"]').simulate('mouseenter'); + wrapper.update(); + jest.runAllTimers(); + wrapper.update(); + expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true); + }); }); }); @@ -92,11 +85,9 @@ describe('DraggableWrapper', () => { test('it applies text truncation styling when truncate IS specified (implicit: and the user is not dragging)', () => { const wrapper = mount( - - - message} truncate /> - - + + message} truncate /> + ); @@ -108,11 +99,9 @@ describe('DraggableWrapper', () => { test('it does NOT apply text truncation styling when truncate is NOT specified', () => { const wrapper = mount( - - - message} /> - - + + message} /> + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx index b53da42da55f8..af7e9ad5f1492 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -8,14 +8,13 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; import { coreMock } from '../../../../../../../src/core/public/mocks'; -import { useWithSource } from '../../containers/source'; import { mockBrowserFields } from '../../containers/source/mock'; import '../../mock/match_media'; import { useKibana } from '../../lib/kibana'; import { TestProviders } from '../../mock'; import { FilterManager } from '../../../../../../../src/plugins/data/public'; import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; - +import { useSourcererScope } from '../../containers/sourcerer'; import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content'; import { ManageGlobalTimeline, @@ -26,12 +25,12 @@ import { TimelineId } from '../../../../common/types/timeline'; jest.mock('../link_to'); jest.mock('../../lib/kibana'); -jest.mock('../../containers/source', () => { - const original = jest.requireActual('../../containers/source'); +jest.mock('../../containers/sourcerer', () => { + const original = jest.requireActual('../../containers/sourcerer'); return { ...original, - useWithSource: jest.fn(), + useSourcererScope: jest.fn(), }; }); @@ -79,27 +78,13 @@ describe('DraggableWrapperHoverContent', () => { beforeAll(() => { // our mock implementation of the useAddToTimeline hook returns a mock startDragToTimeline function: (useAddToTimeline as jest.Mock).mockReturnValue(jest.fn()); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ browserFields: mockBrowserFields, + selectedPatterns: [], + indexPattern: {}, }); }); - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeEach(() => { - jest.clearAllMocks(); - }); - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - /** * The tests for "Filter for value" and "Filter out value" are similar enough * to combine them into "table tests" using this array @@ -203,7 +188,7 @@ describe('DraggableWrapperHoverContent', () => { wrapper = mount( ); @@ -311,7 +296,7 @@ describe('DraggableWrapperHoverContent', () => { {...{ ...defaultProps, onFilterAdded, - timelineId: 'not-active-timeline', + timelineId: TimelineId.test, value: '', }} /> @@ -606,9 +591,7 @@ describe('DraggableWrapperHoverContent', () => { test('filter manager, not active timeline', () => { mount( - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx index a951bfa98d64b..8c68551ddd981 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.tsx @@ -8,7 +8,7 @@ import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import React, { useCallback, useMemo, useState, useEffect } from 'react'; import { DraggableId } from 'react-beautiful-dnd'; -import { getAllFieldsByName, useWithSource } from '../../containers/source'; +import { getAllFieldsByName } from '../../containers/source'; import { useAddToTimeline } from '../../hooks/use_add_to_timeline'; import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard'; import { useKibana } from '../../lib/kibana'; @@ -20,6 +20,8 @@ import * as i18n from './translations'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; import { TimelineId } from '../../../../common/types/timeline'; import { SELECTOR_TIMELINE_BODY_CLASS_NAME } from '../../../timelines/components/timeline/styles'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererScope } from '../../containers/sourcerer'; interface Props { closePopOver?: () => void; @@ -49,7 +51,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [ kibana.services.data.query.filterManager, ]); - const { getManageTimelineById, getTimelineFilterManager } = useManageTimeline(); + const { getTimelineFilterManager } = useManageTimeline(); const filterManager = useMemo( () => @@ -65,13 +67,16 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ // this component is rendered in the context of the active timeline. This // behavior enables the 'All events' view by appending the alerts index // to the index pattern. - const { indexToAdd } = useMemo( - () => - timelineId === TimelineId.active - ? getManageTimelineById(TimelineId.active) - : { indexToAdd: null }, - [getManageTimelineById, timelineId] - ); + const activeScope: SourcererScopeName = + timelineId === TimelineId.active + ? SourcererScopeName.timeline + : timelineId != null && + [TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage].includes( + timelineId as TimelineId + ) + ? SourcererScopeName.detections + : SourcererScopeName.default; + const { browserFields, indexPattern, selectedPatterns } = useSourcererScope(activeScope); const handleStartDragToTimeline = useCallback(() => { startDragToTimeline(); @@ -121,8 +126,6 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ } }, [goGetTimelineId, timelineId]); - const { browserFields, indexPattern } = useWithSource('default', indexToAdd); - return ( <> {!showTopN && value != null && ( @@ -187,7 +190,7 @@ const DraggableWrapperHoverContentComponent: React.FC = ({ browserFields={browserFields} field={field} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={selectedPatterns} onFilterAdded={onFilterAdded} timelineId={timelineId ?? undefined} toggleTopN={toggleTopN} diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx index bd2f01721290f..14d1c37efb8cf 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.test.tsx @@ -6,9 +6,8 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { mockBrowserFields, mocksSource } from '../../containers/source/mock'; +import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; import { DragDropContextWrapper } from './drag_drop_context_wrapper'; @@ -24,11 +23,9 @@ describe('DroppableWrapper', () => { const wrapper = shallow( - - - {message} - - + + {message} + ); @@ -40,11 +37,9 @@ describe('DroppableWrapper', () => { const wrapper = mount( - - - {message} - - + + {message} + ); @@ -56,13 +51,11 @@ describe('DroppableWrapper', () => { const wrapper = mount( - - - null} droppableId="testing"> -
{message}
-
-
-
+ + null} droppableId="testing"> +
{message}
+
+
); @@ -72,14 +65,12 @@ describe('DroppableWrapper', () => { test('it renders the render prop contents when a render prop is provided', () => { const wrapper = mount( - - -
{`isDraggingOver is: ${isDraggingOver}`}
} - droppableId="testing" - /> -
-
+ +
{`isDraggingOver is: ${isDraggingOver}`}
} + droppableId="testing" + /> +
); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx index 79773630e0dc0..d791ea44f8198 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/link_to_app.test.tsx @@ -44,7 +44,7 @@ describe('LinkToApp component', () => { }); it('should support onClick prop', () => { // Take `_event` (even though it is not used) so that `jest.fn` will have a type that expects to be called with an event - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((_event) => {}); + const spyOnClickHandler: LinkToAppOnClickMock = jest.fn().mockImplementation((_event) => {}); const renderResult = render( {'link'} @@ -98,20 +98,24 @@ describe('LinkToApp component', () => { }); it('should still preventDefault if onClick callback throws', () => { // Take `_event` (even though it is not used) so that `jest.fn` will have a type that expects to be called with an event - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((_event) => { + const spyOnClickHandler = jest.fn().mockImplementation((_event) => { throw new Error('test'); }); - const renderResult = render( - - {'link'} - - ); - expect(() => renderResult.find('EuiLink').simulate('click')).toThrow(); - const clickEventArg = spyOnClickHandler.mock.calls[0][0]; - expect(clickEventArg.isDefaultPrevented()).toBe(true); + // eslint-disable-next-line no-empty + try { + } catch (e) { + const renderResult = render( + + {'link'} + + ); + expect(() => renderResult.find('EuiLink').simulate('click')).toThrowError(); + const clickEventArg = spyOnClickHandler.mock.calls[0][0]; + expect(clickEventArg.isDefaultPrevented()).toBe(true); + } }); it('should not navigate if onClick callback prevents default', () => { - const spyOnClickHandler: LinkToAppOnClickMock = jest.fn((ev) => { + const spyOnClickHandler: LinkToAppOnClickMock = jest.fn().mockImplementation((ev) => { ev.preventDefault(); }); const renderResult = render( diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index 01b0810830dd8..c3c7c864ac99b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -8,16 +8,19 @@ import { shallow } from 'enzyme'; import React from 'react'; import '../../mock/match_media'; -import { mockDetailItemData, mockDetailItemDataId } from '../../mock/mock_detail_item'; -import { TestProviders } from '../../mock/test_providers'; +import '../../mock/react_beautiful_dnd'; +import { + defaultHeaders, + mockDetailItemData, + mockDetailItemDataId, + TestProviders, +} from '../../mock'; import { EventDetails, View } from './event_details'; import { mockBrowserFields } from '../../containers/source/mock'; -import { defaultHeaders } from '../../mock/header'; import { useMountAppended } from '../../utils/use_mount_appended'; jest.mock('../link_to'); - describe('EventDetails', () => { const mount = useMountAppended(); const onEventToggled = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx index 1b8177b2038ae..168fe6e65564d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx @@ -9,11 +9,11 @@ import { set } from '@elastic/safer-lodash-set/fp'; import React from 'react'; import styled from 'styled-components'; -import { DetailItem } from '../../../graphql/types'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { omitTypenameAndEmpty } from '../../../timelines/components/timeline/body/helpers'; interface Props { - data: DetailItem[]; + data: TimelineEventsDetailsItem[]; } const JsonEditor = styled.div` @@ -40,5 +40,5 @@ export const JsonView = React.memo(({ data }) => ( JsonView.displayName = 'JsonView'; -export const buildJsonView = (data: DetailItem[]) => +export const buildJsonView = (data: TimelineEventsDetailsItem[]) => data.reduce((accumulator, item) => set(item.field, item.originalValue, accumulator), {}); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/types.ts b/x-pack/plugins/security_solution/public/common/components/event_details/types.ts index db53f411fa518..ed27a57745787 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/event_details/types.ts @@ -5,6 +5,6 @@ */ import { BrowserField } from '../../containers/source'; -import { DetailItem } from '../../../graphql/types'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; -export type EventFieldsData = BrowserField & DetailItem; +export type EventFieldsData = BrowserField & TimelineEventsDetailsItem; diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 037655f594241..aac1f4f2687eb 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -8,15 +8,13 @@ import React from 'react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; -import { mockIndexPattern, TestProviders } from '../../mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { mockIndexNames, mockIndexPattern, TestProviders } from '../../mock'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; import { EventsViewer } from './events_viewer'; import { defaultHeaders } from './default_headers'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; +import { useSourcererScope } from '../../containers/sourcerer'; import { mockBrowserFields, mockDocValueFields } from '../../containers/source/mock'; import { eventsDefaultModel } from './default_model'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -25,6 +23,7 @@ import { TimelineId } from '../../../../common/types/timeline'; import { KqlMode } from '../../../timelines/store/timeline/model'; import { SortDirection } from '../../../timelines/components/timeline/body/sort'; import { AlertsTableFilterGroup } from '../../../detections/components/alerts_table/alerts_filter_group'; +import { SourcererScopeName } from '../../store/sourcerer/model'; import { useTimelineEvents } from '../../../timelines/containers'; jest.mock('../../../timelines/containers', () => ({ @@ -33,8 +32,8 @@ jest.mock('../../../timelines/containers', () => ({ jest.mock('../../components/url_state/normalize_time_range.ts'); -const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock; -jest.mock('../../../detections/containers/detection_engine/rules/fetch_index_patterns'); +const mockUseSourcererScope: jest.Mock = useSourcererScope as jest.Mock; +jest.mock('../../containers/sourcerer'); const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); @@ -45,9 +44,10 @@ const to = '2019-08-27T22:10:56.794Z'; const defaultMocks = { browserFields: mockBrowserFields, - indexPatterns: mockIndexPattern, docValueFields: mockDocValueFields, - isLoading: false, + indexPattern: mockIndexPattern, + loading: false, + selectedPatterns: mockIndexNames, }; const utilityBar = (refetch: inputsModel.Refetch, totalCount: number) => ( @@ -63,6 +63,7 @@ const eventsViewerDefaultProps = { end: to, filters: [], id: TimelineId.detectionsPage, + indexNames: mockIndexNames, indexPattern: mockIndexPattern, isLive: false, isLoadingIndexPattern: false, @@ -79,6 +80,7 @@ const eventsViewerDefaultProps = { columnId: 'foo', sortDirection: 'none' as SortDirection, }, + scopeId: SourcererScopeName.timeline, toggleColumn: jest.fn(), utilityBar, }; @@ -86,154 +88,57 @@ const eventsViewerDefaultProps = { describe('EventsViewer', () => { const mount = useMountAppended(); + let testProps = { + defaultModel: eventsDefaultModel, + end: to, + id: 'test-stateful-events-viewer', + start: from, + scopeId: SourcererScopeName.timeline, + }; + beforeEach(() => { (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks }]); }); - - test('it renders the "Showing..." subtitle with the expected event count', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().text()).toEqual( - 'Showing: 12 events' - ); - }); + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => defaultMocks); }); - - test('it does NOT render fetch index pattern is loading', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + describe('rendering', () => { + test('it renders the "Showing..." subtitle with the expected event count', () => { + const wrapper = mount( + + + ); - }); - }); - - test('it does NOT render when start is empty', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().text()).toEqual( + 'Showing: 12 events' ); }); - }); - test('it does NOT render when end is empty', async () => { - mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks, isLoading: true }]); - - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false + test('it renders the Fields Browser as a settings gear', () => { + const wrapper = mount( + + + ); - }); - }); - - test('it renders the Fields Browser as a settings gear', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find(`[data-test-subj="show-field-browser"]`).first().exists()).toBe(true); }); - }); - - test('it renders the footer containing the Load More button', async () => { - const wrapper = mount( - - - - ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="timeline-pagination"]`).first().exists()).toBe(true); - }); - }); - - defaultHeaders.forEach((header) => { - test(`it renders the ${header.id} default EventsViewer column header`, async () => { + // TO DO sourcerer @X + test('it renders the footer containing the pagination', () => { const wrapper = mount( - + ); + expect(wrapper.find(`[data-test-subj="timeline-pagination"]`).first().exists()).toBe(true); + }); - await waitFor(() => { - wrapper.update(); + defaultHeaders.forEach((header) => { + test(`it renders the ${header.id} default EventsViewer column header`, () => { + const wrapper = mount( + + + + ); defaultHeaders.forEach((h) => expect(wrapper.find(`[data-test-subj="header-text-${header.id}"]`).first().exists()).toBe( @@ -242,10 +147,58 @@ describe('EventsViewer', () => { ); }); }); + describe('loading', () => { + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); + }); + test('it does NOT render fetch index pattern is loading', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when start is empty', () => { + testProps = { + ...testProps, + start: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when end is empty', () => { + testProps = { + ...testProps, + end: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + }); }); describe('headerFilterGroup', () => { - test('it renders the provided headerFilterGroup', async () => { + test('it renders the provided headerFilterGroup', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); }); - test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).not.toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).not.toHaveStyleRule('visibility', 'hidden'); }); - test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).not.toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).not.toHaveStyleRule('visibility', 'hidden'); }); - test('it does NOT have a visible HeaderFilterGroupWrapper when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT have a visible HeaderFilterGroupWrapper when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect( - wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() - ).toHaveStyleRule('visibility', 'hidden'); - }); + expect( + wrapper.find(`[data-test-subj="header-filter-group-wrapper"]`).first() + ).toHaveStyleRule('visibility', 'hidden'); }); - test('it (still) renders an invisible headerFilterGroup (to maintain state while hidden) when Resolver is showing, because graphEventId is a valid id', async () => { + test('it (still) renders an invisible headerFilterGroup (to maintain state while hidden) when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( { /> ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="alerts-table-filter-group"]`).exists()).toBe(true); }); }); describe('utilityBar', () => { - test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); }); - test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(true); }); - test('it does NOT render the provided utilityBar when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT render the provided utilityBar when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(false); - }); + expect(wrapper.find(`[data-test-subj="mock-utility-bar"]`).exists()).toBe(false); }); }); describe('header inspect button', () => { - test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', async () => { + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); }); - test('it renders the inspect button when Resolver is NOT showing, because graphEventId is an empty string', async () => { + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is an empty string', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(true); }); - test('it does NOT render the inspect button when Resolver is showing, because graphEventId is a valid id', async () => { + test('it does NOT render the inspect button when Resolver is showing, because graphEventId is a valid id', () => { const wrapper = mount( ); - - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(false); - }); + expect(wrapper.find(`[data-test-subj="inspect-icon-button"]`).exists()).toBe(false); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 2998bd031d674..2c8c8136a4733 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -95,6 +95,7 @@ interface Props { headerFilterGroup?: React.ReactNode; height?: number; id: string; + indexNames: string[]; indexPattern: IIndexPattern; isLive: boolean; isLoadingIndexPattern: boolean; @@ -121,6 +122,7 @@ const EventsViewerComponent: React.FC = ({ filters, headerFilterGroup, id, + indexNames, indexPattern, isLive, isLoadingIndexPattern, @@ -213,7 +215,7 @@ const EventsViewerComponent: React.FC = ({ fields, filterQuery: combinedQueries!.filterQuery, id, - indexPattern, + indexNames, limit: itemsPerPage, sort: sortField, startDate: start, diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index 8c61281422c2a..928521c118ba5 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -8,16 +8,14 @@ import React from 'react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; -import { mockIndexPattern, TestProviders } from '../../mock'; +import { waitFor } from '@testing-library/react'; +import { TestProviders } from '../../mock'; import { useMountAppended } from '../../utils/use_mount_appended'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; -import { mockBrowserFields } from '../../containers/source/mock'; import { eventsDefaultModel } from './default_model'; +import { SourcererScopeName } from '../../store/sourcerer/model'; import { useTimelineEvents } from '../../../timelines/containers'; jest.mock('../../../timelines/containers', () => ({ @@ -26,15 +24,6 @@ jest.mock('../../../timelines/containers', () => ({ jest.mock('../../components/url_state/normalize_time_range.ts'); -const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock; -jest.mock('../../../detections/containers/detection_engine/rules/fetch_index_patterns'); -mockUseFetchIndexPatterns.mockImplementation(() => [ - { - browserFields: mockBrowserFields, - indexPatterns: mockIndexPattern, - }, -]); - const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); @@ -42,6 +31,14 @@ mockUseResizeObserver.mockImplementation(() => ({})); const from = '2019-08-27T22:10:56.794Z'; const to = '2019-08-26T22:10:56.791Z'; +const testProps = { + defaultModel: eventsDefaultModel, + end: to, + indexNames: [], + id: 'test-stateful-events-viewer', + scopeId: SourcererScopeName.default, + start: from, +}; describe('StatefulEventsViewer', () => { const mount = useMountAppended(); @@ -50,12 +47,7 @@ describe('StatefulEventsViewer', () => { test('it renders the events viewer', async () => { const wrapper = mount( - + ); @@ -70,12 +62,7 @@ describe('StatefulEventsViewer', () => { test('it renders InspectButtonContainer', async () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index e4520dab4626a..c53d311dc1361 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -9,7 +9,6 @@ import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import styled from 'styled-components'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, inputsSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline'; @@ -20,25 +19,26 @@ import { } from '../../../timelines/store/timeline/model'; import { OnChangeItemsPerPage } from '../../../timelines/components/timeline/events'; import { Filter } from '../../../../../../../src/plugins/data/public'; -import { useUiSetting } from '../../lib/kibana'; import { EventsViewer } from './events_viewer'; -import { useFetchIndexPatterns } from '../../../detections/containers/detection_engine/rules/fetch_index_patterns'; import { InspectButtonContainer } from '../inspect'; import { useFullScreen } from '../../containers/use_full_screen'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { useSourcererScope } from '../../containers/sourcerer'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 652; const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>` height: ${({ $isFullScreen }) => ($isFullScreen ? '100%' : `${DEFAULT_EVENTS_VIEWER_HEIGHT}px`)}; + flex: 1 1 auto; display: flex; width: 100%; `; export interface OwnProps { - defaultIndices?: string[]; defaultModel: SubsetTimelineModel; end: string; id: string; + scopeId: SourcererScopeName; start: string; headerFilterGroup?: React.ReactNode; pageFilters?: Filter[]; @@ -52,7 +52,6 @@ const StatefulEventsViewerComponent: React.FC = ({ columns, dataProviders, deletedEventIds, - defaultIndices, deleteEventQuery, end, excludedRowRendererIds, @@ -67,6 +66,7 @@ const StatefulEventsViewerComponent: React.FC = ({ query, removeColumn, start, + scopeId, showCheckboxes, sort, updateItemsPerPage, @@ -75,13 +75,13 @@ const StatefulEventsViewerComponent: React.FC = ({ // If truthy, the graph viewer (Resolver) is showing graphEventId, }) => { - const [ - { docValueFields, browserFields, indexPatterns, isLoading: isLoadingIndexPattern }, - ] = useFetchIndexPatterns( - defaultIndices ?? useUiSetting(DEFAULT_INDEX_KEY), - 'events_viewer' - ); - + const { + browserFields, + docValueFields, + indexPattern, + selectedPatterns, + loading: isLoadingIndexPattern, + } = useSourcererScope(scopeId); const { globalFullScreen } = useFullScreen(); useEffect(() => { @@ -90,6 +90,7 @@ const StatefulEventsViewerComponent: React.FC = ({ id, columns, excludedRowRendererIds, + indexNames: selectedPatterns, sort, itemsPerPage, showCheckboxes, @@ -144,7 +145,8 @@ const StatefulEventsViewerComponent: React.FC = ({ isLoadingIndexPattern={isLoadingIndexPattern} filters={globalFilters} headerFilterGroup={headerFilterGroup} - indexPattern={indexPatterns} + indexNames={selectedPatterns} + indexPattern={indexPattern} isLive={isLive} itemsPerPage={itemsPerPage!} itemsPerPageOptions={itemsPerPageOptions!} @@ -222,8 +224,8 @@ export const StatefulEventsViewer = connector( StatefulEventsViewerComponent, (prevProps, nextProps) => prevProps.id === nextProps.id && + prevProps.scopeId === nextProps.scopeId && deepEqual(prevProps.columns, nextProps.columns) && - deepEqual(prevProps.defaultIndices, nextProps.defaultIndices) && deepEqual(prevProps.dataProviders, nextProps.dataProviders) && deepEqual(prevProps.excludedRowRendererIds, nextProps.excludedRowRendererIds) && prevProps.deletedEventIds === nextProps.deletedEventIds && diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index 691a7d99d9345..ef2a5770eee8d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -8,18 +8,18 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import { mount, ReactWrapper } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { AddExceptionModal } from './'; import { useCurrentUser } from '../../../../common/lib/kibana'; +import { useAsync } from '../../../../shared_imports'; import { getExceptionListSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_schema.mock'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; +import { useFetchIndex } from '../../../containers/source'; import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; import { useAddOrUpdateException } from '../use_add_exception'; import { useFetchOrCreateRuleExceptionList } from '../use_fetch_or_create_rule_exception_list'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { Ecs } from '../../../../../common/ecs'; -import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline'; import * as builder from '../builder'; import * as helpers from '../helpers'; import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; @@ -28,10 +28,12 @@ import { ExceptionListItemSchema } from '../../../../../../lists/common'; jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../containers/source'); jest.mock('../../../../detections/containers/detection_engine/rules'); jest.mock('../use_add_exception'); jest.mock('../use_fetch_or_create_rule_exception_list'); jest.mock('../builder'); +jest.mock('../../../../shared_imports'); describe('When the add exception modal is opened', () => { const ruleName = 'test rule'; @@ -47,6 +49,11 @@ describe('When the add exception modal is opened', () => { .spyOn(builder, 'ExceptionBuilderComponent') .mockReturnValue(<>); + (useAsync as jest.Mock).mockImplementation(() => ({ + start: jest.fn(), + loading: false, + })); + (useAddOrUpdateException as jest.Mock).mockImplementation(() => [ { isLoading: false }, jest.fn(), @@ -59,9 +66,9 @@ describe('When the add exception modal is opened', () => { loading: false, signalIndexName: 'mock-siem-signals-index', })); - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: stubIndexPattern, }, ]); @@ -77,9 +84,9 @@ describe('When the add exception modal is opened', () => { let wrapper: ReactWrapper; beforeEach(() => { // Mocks one of the hooks as loading - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + true, { - isLoading: true, indexPatterns: stubIndexPattern, }, ]); @@ -103,7 +110,7 @@ describe('When the add exception modal is opened', () => { describe('when there is no alert data passed to an endpoint list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [] })); + await waitFor(() => callProps.onChange({ exceptionItems: [] })); }); it('has the add exception button disabled', () => { expect( @@ -139,11 +146,9 @@ describe('When the add exception modal is opened', () => { describe('when there is alert data passed to an endpoint list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { - const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = { - ecsData: { _id: 'test-id' }, - nonEcsData: [{ field: 'file.path', value: ['test/path'] }], - }; + beforeEach(async () => { + const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } }; + wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -190,11 +197,9 @@ describe('When the add exception modal is opened', () => { describe('when there is alert data passed to a detection list exception', () => { let wrapper: ReactWrapper; - beforeEach(() => { - const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = { - ecsData: { _id: 'test-id' }, - nonEcsData: [{ field: 'file.path', value: ['test/path'] }], - }; + beforeEach(async () => { + const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } }; + wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -242,11 +249,11 @@ describe('When the add exception modal is opened', () => { onChange: (props: { exceptionItems: ExceptionListItemSchema[] }) => void; exceptionListItems: ExceptionListItemSchema[]; }; - beforeEach(() => { + beforeEach(async () => { // Mocks the index patterns to contain the pre-populated endpoint fields so that the exception qualifies as bulk closable - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: { ...stubIndexPattern, fields: [ @@ -259,10 +266,7 @@ describe('When the add exception modal is opened', () => { }, }, ]); - const alertDataMock: { ecsData: Ecs; nonEcsData: TimelineNonEcsData[] } = { - ecsData: { _id: 'test-id' }, - nonEcsData: [{ field: 'file.path', value: ['test/path'] }], - }; + const alertDataMock: Ecs = { _id: 'test-id', file: { path: ['test/path'] } }; wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); }); it('has the add exception button enabled', () => { expect( @@ -306,8 +312,8 @@ describe('When the add exception modal is opened', () => { ).not.toBeDisabled(); }); describe('when a "is in list" entry is added', () => { - it('should have the bulk close checkbox disabled', () => { - act(() => + it('should have the bulk close checkbox disabled', async () => { + await waitFor(() => callProps.onChange({ exceptionItems: [ ...callProps.exceptionListItems, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index 721e53732c093..dee1db6482067 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -30,7 +30,6 @@ import * as i18nCommon from '../../../translations'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; import { Ecs } from '../../../../../common/ecs'; -import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline'; import { useAppToasts } from '../../../hooks/use_app_toasts'; import { useKibana } from '../../../lib/kibana'; import { ExceptionBuilderComponent } from '../builder'; @@ -47,28 +46,21 @@ import { defaultEndpointExceptionItems, entryHasListType, entryHasNonEcsType, - getMappedNonEcsValue, } from '../helpers'; import { ErrorInfo, ErrorCallout } from '../error_callout'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; import { ExceptionsBuilderExceptionItem } from '../types'; +import { useFetchIndex } from '../../../containers/source'; -export interface AddExceptionModalBaseProps { +export interface AddExceptionModalProps { ruleName: string; ruleId: string; exceptionListType: ExceptionListType; ruleIndices: string[]; - alertData?: { - ecsData: Ecs; - nonEcsData: TimelineNonEcsData[]; - }; -} - -export interface AddExceptionModalProps extends AddExceptionModalBaseProps { + alertData?: Ecs; + alertStatus?: Status; onCancel: () => void; onConfirm: (didCloseAlert: boolean, didBulkCloseAlert: boolean) => void; onRuleChange?: () => void; - alertStatus?: Status; } const Modal = styled(EuiModal)` @@ -122,14 +114,13 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const [fetchOrCreateListError, setFetchOrCreateListError] = useState(null); const { addError, addSuccess, addWarning } = useAppToasts(); const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); - const [ - { isLoading: isSignalIndexPatternLoading, indexPatterns: signalIndexPatterns }, - ] = useFetchIndexPatterns(signalIndexName !== null ? [signalIndexName] : [], 'signals'); - - const [{ isLoading: isIndexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - ruleIndices, - 'rules' + const memoSignalIndexName = useMemo(() => (signalIndexName !== null ? [signalIndexName] : []), [ + signalIndexName, + ]); + const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex( + memoSignalIndexName ); + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); const onError = useCallback( (error: Error): void => { @@ -219,12 +210,12 @@ export const AddExceptionModal = memo(function AddExceptionModal({ }); const initialExceptionItems = useMemo((): ExceptionsBuilderExceptionItem[] => { - if (exceptionListType === 'endpoint' && alertData !== undefined && ruleExceptionList) { + if (exceptionListType === 'endpoint' && alertData != null && ruleExceptionList) { return defaultEndpointExceptionItems( exceptionListType, ruleExceptionList.list_id, ruleName, - alertData.nonEcsData + alertData ); } else { return []; @@ -276,15 +267,12 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const retrieveAlertOsTypes = useCallback((): string[] => { const osDefaults = ['windows', 'macos']; - if (alertData) { - const osTypes = getMappedNonEcsValue({ - data: alertData.nonEcsData, - fieldName: 'host.os.family', - }); - if (osTypes.length === 0) { - return osDefaults; + if (alertData != null) { + const osTypes = alertData.host && alertData.host.os && alertData.host.os.family; + if (osTypes != null && osTypes.length > 0) { + return osTypes; } - return osTypes; + return osDefaults; } return osDefaults; }, [alertData]); @@ -305,10 +293,10 @@ export const AddExceptionModal = memo(function AddExceptionModal({ }, [comment, exceptionItemsToAdd, exceptionListType, retrieveAlertOsTypes]); const onAddExceptionConfirm = useCallback((): void => { - if (addOrUpdateExceptionItems !== null) { - const alertIdToClose = shouldCloseAlert && alertData ? alertData.ecsData._id : undefined; + if (addOrUpdateExceptionItems != null) { + const alertIdToClose = shouldCloseAlert && alertData ? alertData._id : undefined; const bulkCloseIndex = - shouldBulkCloseAlert && signalIndexName !== null ? [signalIndexName] : undefined; + shouldBulkCloseAlert && signalIndexName != null ? [signalIndexName] : undefined; addOrUpdateExceptionItems(ruleId, enrichExceptionItems(), alertIdToClose, bulkCloseIndex); } }, [ diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx index 2d389a7dbcee1..e6f57fe666780 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ThemeProvider } from 'styled-components'; import { mount } from 'enzyme'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { fields, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/logic_buttons.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/logic_buttons.stories.tsx index e4508fa2a9a1d..64da43dc5b1c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/logic_buttons.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/logic_buttons.stories.tsx @@ -16,7 +16,7 @@ addDecorator((storyFn) => ( ({ eui: euiLightVars, darkMode: false })}>{storyFn()} )); -storiesOf('Exceptions|BuilderLogicButtons', module) +storiesOf('Exceptions/BuilderLogicButtons', module) .add('and/or buttons', () => { return ( { { isLoading: false }, jest.fn(), ]); - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, { - isLoading: false, indexPatterns: stubIndexPatternWithFields, }, ]); @@ -65,15 +66,14 @@ describe('When the edit exception modal is opened', () => { }); describe('when the modal is loading', () => { - let wrapper: ReactWrapper; - beforeEach(() => { - (useFetchIndexPatterns as jest.Mock).mockImplementation(() => [ + it('renders the loading spinner', async () => { + (useFetchIndex as jest.Mock).mockImplementation(() => [ + true, { - isLoading: true, indexPatterns: stubIndexPattern, }, ]); - wrapper = mount( + const wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { /> ); - }); - it('renders the loading spinner', () => { - expect(wrapper.find('[data-test-subj="loadingEditExceptionModal"]').exists()).toBeTruthy(); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="loadingEditExceptionModal"]').exists()).toBeTruthy(); + }); }); }); describe('when an endpoint exception with exception data is passed', () => { describe('when exception entry fields are included in the index pattern', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const exceptionItemMock = { ...getExceptionListItemSchemaMock(), entries: [ @@ -116,7 +116,9 @@ describe('When the edit exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -144,7 +146,7 @@ describe('When the edit exception modal is opened', () => { describe("when exception entry fields aren't included in the index pattern", () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -188,7 +192,7 @@ describe('When the edit exception modal is opened', () => { describe('when an detection exception with entries is passed', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button enabled', () => { expect( @@ -227,7 +233,7 @@ describe('When the edit exception modal is opened', () => { describe('when an exception with no entries is passed', () => { let wrapper: ReactWrapper; - beforeEach(() => { + beforeEach(async () => { const exceptionItemMock = { ...getExceptionListItemSchemaMock(), entries: [] }; wrapper = mount( ({ eui: euiLightVars, darkMode: false })}> @@ -243,7 +249,9 @@ describe('When the edit exception modal is opened', () => { ); const callProps = ExceptionBuilderComponent.mock.calls[0][0]; - act(() => callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] })); + await waitFor(() => { + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }); + }); }); it('has the edit exception button disabled', () => { expect( diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 5dbf319c3299d..128686428598c 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -21,7 +21,8 @@ import { EuiText, EuiCallOut, } from '@elastic/eui'; -import { useFetchIndexPatterns } from '../../../../detections/containers/detection_engine/rules'; + +import { useFetchIndex } from '../../../containers/source'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import { useRuleAsync } from '../../../../detections/containers/detection_engine/rules/use_rule_async'; import { @@ -108,15 +109,12 @@ export const EditExceptionModal = memo(function EditExceptionModal({ >([]); const { addError, addSuccess } = useAppToasts(); const { loading: isSignalIndexLoading, signalIndexName } = useSignalIndex(); - const [ - { isLoading: isSignalIndexPatternLoading, indexPatterns: signalIndexPatterns }, - ] = useFetchIndexPatterns(signalIndexName !== null ? [signalIndexName] : [], 'signals'); - - const [{ isLoading: isIndexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - ruleIndices, - 'rules' + const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex( + signalIndexName !== null ? [signalIndexName] : [] ); + const [isIndexPatternLoading, { indexPatterns }] = useFetchIndex(ruleIndices); + const handleExceptionUpdateError = useCallback( (error: Error, statusCode: number | null, message: string | null) => { if (error.message.includes('Conflict')) { diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index 4236f347ac7ff..26fb460aee382 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -25,6 +25,9 @@ import { entryHasNonEcsType, prepareExceptionItemsForBulkClose, lowercaseHashValues, + getPrepopulatedItem, + getCodeSignatureValue, + defaultEndpointExceptionItems, } from './helpers'; import { EmptyEntry } from './types'; import { @@ -708,4 +711,207 @@ describe('Exception helpers', () => { ]); }); }); + + describe('getPrepopulatedItem', () => { + test('it returns prepopulated items', () => { + const prepopulatedItem = getPrepopulatedItem({ + listType: 'endpoint', + listId: 'some_id', + ruleName: 'my rule', + codeSignature: { subjectName: '', trusted: '' }, + filePath: '', + sha256Hash: '', + eventCode: '', + }); + + expect(prepopulatedItem.entries).toEqual([ + { + entries: [ + { field: 'subject_name', operator: 'included', type: 'match', value: '' }, + { field: 'trusted', operator: 'included', type: 'match', value: '' }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + }, + { field: 'file.path.text', operator: 'included', type: 'match', value: '' }, + { field: 'file.hash.sha256', operator: 'included', type: 'match', value: '' }, + { field: 'event.code', operator: 'included', type: 'match', value: '' }, + ]); + }); + + test('it returns prepopulated items with values', () => { + const prepopulatedItem = getPrepopulatedItem({ + listType: 'endpoint', + listId: 'some_id', + ruleName: 'my rule', + codeSignature: { subjectName: 'someSubjectName', trusted: 'false' }, + filePath: 'some-file-path', + sha256Hash: 'some-hash', + eventCode: 'some-event-code', + }); + + expect(prepopulatedItem.entries).toEqual([ + { + entries: [ + { + field: 'subject_name', + operator: 'included', + type: 'match', + value: 'someSubjectName', + }, + { field: 'trusted', operator: 'included', type: 'match', value: 'false' }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + }, + { field: 'file.path.text', operator: 'included', type: 'match', value: 'some-file-path' }, + { field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some-hash' }, + { field: 'event.code', operator: 'included', type: 'match', value: 'some-event-code' }, + ]); + }); + }); + + describe('getCodeSignatureValue', () => { + test('it works when file.Ext.code_signature is an object', () => { + const codeSignatures = getCodeSignatureValue({ + _id: '123', + file: { + Ext: { + code_signature: { + subject_name: ['some_subject'], + trusted: ['false'], + }, + }, + }, + }); + + expect(codeSignatures).toEqual([{ subjectName: 'some_subject', trusted: 'false' }]); + }); + + test('it works when file.Ext.code_signature is nested type', () => { + const codeSignatures = getCodeSignatureValue({ + _id: '123', + file: { + Ext: { + code_signature: [ + { subject_name: ['some_subject'], trusted: ['false'] }, + { subject_name: ['some_subject_2'], trusted: ['true'] }, + ], + }, + }, + }); + + expect(codeSignatures).toEqual([ + { subjectName: 'some_subject', trusted: 'false' }, + { + subjectName: 'some_subject_2', + trusted: 'true', + }, + ]); + }); + + test('it returns default when file.Ext.code_signatures values are empty', () => { + const codeSignatures = getCodeSignatureValue({ + _id: '123', + file: { + Ext: { + code_signature: { subject_name: [], trusted: [] }, + }, + }, + }); + + expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]); + }); + + test('it returns default when file.Ext.code_signatures is empty array', () => { + const codeSignatures = getCodeSignatureValue({ + _id: '123', + file: { + Ext: { + code_signature: [], + }, + }, + }); + + expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]); + }); + + test('it returns default when file.Ext.code_signatures does not exist', () => { + const codeSignatures = getCodeSignatureValue({ + _id: '123', + }); + + expect(codeSignatures).toEqual([{ subjectName: '', trusted: '' }]); + }); + }); + + describe('defaultEndpointExceptionItems', () => { + test('it should return pre-populated items', () => { + const defaultItems = defaultEndpointExceptionItems('endpoint', 'list_id', 'my_rule', { + _id: '123', + file: { + Ext: { + code_signature: [ + { subject_name: ['some_subject'], trusted: ['false'] }, + { subject_name: ['some_subject_2'], trusted: ['true'] }, + ], + }, + path: ['some file path'], + hash: { + sha256: ['some hash'], + }, + }, + event: { + code: ['some event code'], + }, + }); + + expect(defaultItems[0].entries).toEqual([ + { + entries: [ + { + field: 'subject_name', + operator: 'included', + type: 'match', + value: 'some_subject', + }, + { field: 'trusted', operator: 'included', type: 'match', value: 'false' }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + }, + { + field: 'file.path.text', + operator: 'included', + type: 'match', + value: 'some file path', + }, + { field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some hash' }, + { field: 'event.code', operator: 'included', type: 'match', value: 'some event code' }, + ]); + expect(defaultItems[1].entries).toEqual([ + { + entries: [ + { + field: 'subject_name', + operator: 'included', + type: 'match', + value: 'some_subject_2', + }, + { field: 'trusted', operator: 'included', type: 'match', value: 'true' }, + ], + field: 'file.Ext.code_signature', + type: 'nested', + }, + { + field: 'file.path.text', + operator: 'included', + type: 'match', + value: 'some file path', + }, + { field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some hash' }, + { field: 'event.code', operator: 'included', type: 'match', value: 'some event code' }, + ]); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index 3c3c71a2b33e7..d4acfa39f995d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -38,7 +38,8 @@ import { } from '../../../shared_imports'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; import { validate } from '../../../../common/validate'; -import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; +import { Ecs } from '../../../../common/ecs'; +import { CodeSignature } from '../../../../common/ecs/file'; import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard'; /** @@ -367,23 +368,6 @@ export const lowercaseHashValues = ( }); }; -/** - * Returns the value for the given fieldname within TimelineNonEcsData if it exists - */ -export const getMappedNonEcsValue = ({ - data, - fieldName, -}: { - data: TimelineNonEcsData[]; - fieldName: string; -}): string[] => { - const item = data.find((d) => d.field === fieldName); - if (item != null && item.value != null) { - return item.value; - } - return []; -}; - export const entryHasListType = ( exceptionItems: Array ) => { @@ -397,6 +381,105 @@ export const entryHasListType = ( return false; }; +/** + * Returns the value for `file.Ext.code_signature` which + * can be an object or array of objects + */ +export const getCodeSignatureValue = ( + alertData: Ecs +): Array<{ subjectName: string; trusted: string }> => { + const { file } = alertData; + const codeSignature = file && file.Ext && file.Ext.code_signature; + + // Pre 7.10 file.Ext.code_signature was mistakenly populated as + // a single object with subject_name and trusted. + if (Array.isArray(codeSignature) && codeSignature.length > 0) { + return codeSignature.map((signature) => ({ + subjectName: (signature.subject_name && signature.subject_name[0]) ?? '', + trusted: (signature.trusted && signature.trusted[0]) ?? '', + })); + } else { + const signature: CodeSignature | undefined = !Array.isArray(codeSignature) + ? codeSignature + : undefined; + const subjectName: string | undefined = + signature && signature.subject_name && signature.subject_name[0]; + const trusted: string | undefined = signature && signature.trusted && signature.trusted[0]; + + return [ + { + subjectName: subjectName ?? '', + trusted: trusted ?? '', + }, + ]; + } +}; + +/** + * Returns the default values from the alert data to autofill new endpoint exceptions + */ +export const getPrepopulatedItem = ({ + listType, + listId, + ruleName, + codeSignature, + filePath, + sha256Hash, + eventCode, + listNamespace = 'agnostic', +}: { + listType: ExceptionListType; + listId: string; + listNamespace?: NamespaceType; + ruleName: string; + codeSignature: { subjectName: string; trusted: string }; + filePath: string; + sha256Hash: string; + eventCode: string; +}): ExceptionsBuilderExceptionItem => { + return { + ...getNewExceptionItem({ listType, listId, namespaceType: listNamespace, ruleName }), + entries: [ + { + field: 'file.Ext.code_signature', + type: 'nested', + entries: [ + { + field: 'subject_name', + operator: 'included', + type: 'match', + value: codeSignature != null ? codeSignature.subjectName : '', + }, + { + field: 'trusted', + operator: 'included', + type: 'match', + value: codeSignature != null ? codeSignature.trusted : '', + }, + ], + }, + { + field: 'file.path.text', + operator: 'included', + type: 'match', + value: filePath ?? '', + }, + { + field: 'file.hash.sha256', + operator: 'included', + type: 'match', + value: sha256Hash ?? '', + }, + { + field: 'event.code', + operator: 'included', + type: 'match', + value: eventCode ?? '', + }, + ], + }; +}; + /** * Determines whether or not any entries within the given exceptionItems contain values not in the specified ECS mapping */ @@ -434,62 +517,19 @@ export const defaultEndpointExceptionItems = ( listType: ExceptionListType, listId: string, ruleName: string, - alertData: TimelineNonEcsData[] + alertEcsData: Ecs ): ExceptionsBuilderExceptionItem[] => { - const [filePath] = getMappedNonEcsValue({ data: alertData, fieldName: 'file.path' }); - const [signatureSigner] = getMappedNonEcsValue({ - data: alertData, - fieldName: 'file.Ext.code_signature.subject_name', - }); - const [signatureTrusted] = getMappedNonEcsValue({ - data: alertData, - fieldName: 'file.Ext.code_signature.trusted', - }); - const [sha256Hash] = getMappedNonEcsValue({ data: alertData, fieldName: 'file.hash.sha256' }); - const [eventCode] = getMappedNonEcsValue({ data: alertData, fieldName: 'event.code' }); - const namespaceType = 'agnostic'; - - return [ - { - ...getNewExceptionItem({ listType, listId, namespaceType, ruleName }), - entries: [ - { - field: 'file.Ext.code_signature', - type: 'nested', - entries: [ - { - field: 'subject_name', - operator: 'included', - type: 'match', - value: signatureSigner ?? '', - }, - { - field: 'trusted', - operator: 'included', - type: 'match', - value: signatureTrusted ?? '', - }, - ], - }, - { - field: 'file.path.text', - operator: 'included', - type: 'match', - value: filePath ?? '', - }, - { - field: 'file.hash.sha256', - operator: 'included', - type: 'match', - value: sha256Hash ?? '', - }, - { - field: 'event.code', - operator: 'included', - type: 'match', - value: eventCode ?? '', - }, - ], - }, - ]; + const { file, event: alertEvent } = alertEcsData; + + return getCodeSignatureValue(alertEcsData).map((codeSignature) => + getPrepopulatedItem({ + listType, + listId, + ruleName, + filePath: file && file.path ? file.path[0] : '', + sha256Hash: file && file.hash && file.hash.sha256 ? file.hash.sha256[0] : '', + eventCode: alertEvent && alertEvent.code ? alertEvent.code[0] : '', + codeSignature, + }) + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx index a540a34b70677..39f34ae8a3cf3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx @@ -17,7 +17,7 @@ addDecorator((storyFn) => ( ({ eui: euiLightVars, darkMode: false })}>{storyFn()} )); -storiesOf('Components|ExceptionItem', module) +storiesOf('Components/ExceptionItem', module) .add('with os', () => { const payload = getExceptionListItemSchemaMock(); payload.description = ''; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx index d79d46817f153..f7e7411db23a4 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exceptions_viewer_header.stories.tsx @@ -16,7 +16,7 @@ addDecorator((storyFn) => ( ({ eui: euiLightVars, darkMode: false })}>{storyFn()} )); -storiesOf('Components|ExceptionsViewerHeader', module) +storiesOf('Components/ExceptionsViewerHeader', module) .add('loading', () => { return ( theme.eui.paddingSizes.s}; +`; + export const ExitFullScreen: React.FC = () => { const { globalFullScreen, setGlobalFullScreen } = useFullScreen(); @@ -36,14 +41,14 @@ export const ExitFullScreen: React.FC = () => { return ( <> - {i18n.EXIT_FULL_SCREEN} - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index e05e3c2e9aeb1..0c6a54d4434d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -6,7 +6,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { pickBy } from 'lodash/fp'; -import React, { useCallback } from 'react'; +import React, { forwardRef, useCallback } from 'react'; import styled from 'styled-components'; import { OutPortal } from 'react-reverse-portal'; @@ -18,37 +18,43 @@ import { getAppOverviewUrl } from '../link_to'; import { MlPopover } from '../ml_popover/ml_popover'; import { SiemNavigation } from '../navigation'; import * as i18n from './translations'; -import { useWithSource } from '../../containers/source'; import { useGetUrlSearch } from '../navigation/use_get_url_search'; import { useKibana } from '../../lib/kibana'; import { APP_ID, ADD_DATA_PATH, APP_DETECTIONS_PATH } from '../../../../common/constants'; import { useGlobalHeaderPortal } from '../../hooks/use_global_header_portal'; import { LinkAnchor } from '../links'; -const Wrapper = styled.header<{ $globalFullScreen: boolean }>` - ${({ $globalFullScreen, theme }) => ` +const Wrapper = styled.header` + ${({ theme }) => ` background: ${theme.eui.euiColorEmptyShade}; border-bottom: ${theme.eui.euiBorderThin}; - padding-top: ${$globalFullScreen ? theme.eui.paddingSizes.s : theme.eui.paddingSizes.m}; width: 100%; z-index: ${theme.eui.euiZNavigation}; + position: fixed; `} `; Wrapper.displayName = 'Wrapper'; +const WrapperContent = styled.div<{ $globalFullScreen: boolean }>` + display: ${({ $globalFullScreen }) => ($globalFullScreen ? 'none' : 'block')}; + padding-top: ${({ $globalFullScreen, theme }) => + $globalFullScreen ? theme.eui.paddingSizes.s : theme.eui.paddingSizes.m}; +`; + +WrapperContent.displayName = 'WrapperContent'; + const FlexItem = styled(EuiFlexItem)` min-width: 0; `; FlexItem.displayName = 'FlexItem'; -const FlexGroup = styled(EuiFlexGroup)<{ $globalFullScreen: boolean; $hasSibling: boolean }>` - ${({ $globalFullScreen, $hasSibling, theme }) => ` +const FlexGroup = styled(EuiFlexGroup)<{ $hasSibling: boolean }>` + ${({ $hasSibling, theme }) => ` border-bottom: ${theme.eui.euiBorderThin}; margin-bottom: 1px; padding-bottom: 4px; padding-left: ${theme.eui.paddingSizes.l}; padding-right: ${gutterTimeline}; - ${$globalFullScreen ? 'display: none;' : ''} ${$hasSibling ? `border-bottom: ${theme.eui.euiBorderThin};` : 'border-bottom-width: 0px;'} `} `; @@ -57,76 +63,74 @@ FlexGroup.displayName = 'FlexGroup'; interface HeaderGlobalProps { hideDetectionEngine?: boolean; } -export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => { - const { indicesExist } = useWithSource(); - const { globalHeaderPortalNode } = useGlobalHeaderPortal(); - const { globalFullScreen } = useFullScreen(); - const search = useGetUrlSearch(navTabs.overview); - const { navigateToApp } = useKibana().services.application; - const goToOverview = useCallback( - (ev) => { - ev.preventDefault(); - navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search }); - }, - [navigateToApp, search] - ); - - return ( - - - <> - - - - - - - +export const HeaderGlobal = React.memo( + forwardRef(({ hideDetectionEngine = false }, ref) => { + const { globalHeaderPortalNode } = useGlobalHeaderPortal(); + const { globalFullScreen } = useFullScreen(); + const search = useGetUrlSearch(navTabs.overview); + const { application, http } = useKibana().services; + const { navigateToApp } = application; + const basePath = http.basePath.get(); + const goToOverview = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search }); + }, + [navigateToApp, search] + ); + return ( + + + + + + + + + + - - key !== SecurityPageName.detections, navTabs) - : navTabs - } - /> - - - + + key !== SecurityPageName.detections, navTabs) + : navTabs + } + /> + + + + + + {window.location.pathname.includes(APP_DETECTIONS_PATH) && ( + + + + )} - - - {indicesExist && window.location.pathname.includes(APP_DETECTIONS_PATH) && ( - + + {i18n.BUTTON_ADD_DATA} + - )} - - - - {i18n.BUTTON_ADD_DATA} - - - - - - -
- -
-
- ); -}); + + + + + + + ); + }) +); HeaderGlobal.displayName = 'HeaderGlobal'; diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap index a100f5e4f93b4..a2a36b3fe1d3b 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/header_page/__snapshots__/index.test.tsx.snap @@ -36,5 +36,8 @@ exports[`HeaderPage it renders 1`] = `

+
`; diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx index 62880e7510cd2..0cb721bb5382f 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx @@ -15,6 +15,8 @@ import { Title } from './title'; import { DraggableArguments, BadgeOptions, TitleProp } from './types'; import { useFormatUrl } from '../link_to'; import { SecurityPageName } from '../../../app/types'; +import { Sourcerer } from '../sourcerer'; +import { SourcererScopeName } from '../../store/sourcerer/model'; interface HeaderProps { border?: boolean; @@ -72,6 +74,7 @@ export interface HeaderPageProps extends HeaderProps { badgeOptions?: BadgeOptions; children?: React.ReactNode; draggableArguments?: DraggableArguments; + hideSourcerer?: boolean; subtitle?: SubtitleProps['items']; subtitle2?: SubtitleProps['items']; title: TitleProp; @@ -84,6 +87,7 @@ const HeaderPageComponent: React.FC = ({ border, children, draggableArguments, + hideSourcerer = false, isLoading, subtitle, subtitle2, @@ -138,6 +142,7 @@ const HeaderPageComponent: React.FC = ({ )} + {!hideSourcerer && } ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx index 9473ba67a1c4f..d9b5c5e10893c 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx @@ -5,9 +5,9 @@ */ import React from 'react'; - +import '../../../common/mock/formatted_relative'; import { getEmptyValue } from '../empty_value'; -import { LastEventIndexKey } from '../../../graphql/types'; +import { LastEventIndexKey } from '../../../../common/search_strategy'; import { mockLastEventTimeQuery } from '../../containers/events/last_event_time/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -37,7 +37,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.html()).toBe( @@ -48,28 +48,28 @@ describe('Last Event Time Stat', () => { (useTimelineLastEventTime as jest.Mock).mockReturnValue([ false, { - lastSeen: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.lastSeen, - errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage, + lastSeen: mockLastEventTimeQuery.lastSeen, + errorMessage: mockLastEventTimeQuery.errorMessage, }, ]); const wrapper = mount( - + ); - expect(wrapper.html()).toBe('Last event: 12 minutes ago'); + expect(wrapper.html()).toBe('Last event: 20 hours ago'); }); test('Bad date time string', async () => { (useTimelineLastEventTime as jest.Mock).mockReturnValue([ false, { lastSeen: 'something-invalid', - errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage, + errorMessage: mockLastEventTimeQuery.errorMessage, }, ]); const wrapper = mount( - + ); @@ -80,12 +80,12 @@ describe('Last Event Time Stat', () => { false, { lastSeen: null, - errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage, + errorMessage: mockLastEventTimeQuery.errorMessage, }, ]); const wrapper = mount( - + ); expect(wrapper.html()).toContain(getEmptyValue()); diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx index e9e8e7a03017c..fe827b3ab324c 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx @@ -8,58 +8,64 @@ import { EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { memo } from 'react'; -import { LastEventIndexKey } from '../../../graphql/types'; +import { DocValueFields, LastEventIndexKey } from '../../../../common/search_strategy'; import { useTimelineLastEventTime } from '../../containers/events/last_event_time'; import { getEmptyTagValue } from '../empty_value'; import { FormattedRelativePreferenceDate } from '../formatted_date'; export interface LastEventTimeProps { + docValueFields: DocValueFields[]; hostName?: string; indexKey: LastEventIndexKey; ip?: string; + indexNames: string[]; } -export const LastEventTime = memo(({ hostName, indexKey, ip }) => { - const [loading, { lastSeen, errorMessage }] = useTimelineLastEventTime({ - indexKey, - details: { - hostName, - ip, - }, - }); +export const LastEventTime = memo( + ({ docValueFields, hostName, indexKey, ip, indexNames }) => { + const [loading, { lastSeen, errorMessage }] = useTimelineLastEventTime({ + docValueFields, + indexKey, + indexNames, + details: { + hostName, + ip, + }, + }); + + if (errorMessage != null) { + return ( + + + + ); + } - if (errorMessage != null) { return ( - - - + <> + {loading && } + {!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date' + ? lastSeen + : !loading && + lastSeen != null && ( + , + }} + /> + )} + {!loading && lastSeen == null && getEmptyTagValue()} + ); } - - return ( - <> - {loading && } - {!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date' - ? lastSeen - : !loading && - lastSeen != null && ( - , - }} - /> - )} - {!loading && lastSeen == null && getEmptyTagValue()} - - ); -}); +); LastEventTime.displayName = 'LastEventTime'; diff --git a/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx b/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx index 19f1d70e6e230..55842342c6677 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_icon/index.tsx @@ -6,16 +6,16 @@ import { EuiIcon, EuiLink, IconSize, IconType } from '@elastic/eui'; import { LinkAnchorProps } from '@elastic/eui/src/components/link/link'; -import React, { ReactNode } from 'react'; +import React, { ReactNode, useCallback, useMemo } from 'react'; import styled, { css } from 'styled-components'; interface LinkProps { + ariaLabel?: string; color?: LinkAnchorProps['color']; disabled?: boolean; href?: string; iconSide?: 'left' | 'right'; onClick?: Function; - ariaLabel?: string; } export const Link = styled(({ iconSide, children, ...rest }) => ( @@ -55,6 +55,7 @@ export interface LinkIconProps extends LinkProps { export const LinkIcon = React.memo( ({ + ariaLabel, children, color, dataTestSubj, @@ -64,21 +65,41 @@ export const LinkIcon = React.memo( iconSize = 's', iconType, onClick, - ariaLabel, - }) => ( - - - {children} - - ) + }) => { + const getChildrenString = useCallback((theChild: string | ReactNode): string => { + if ( + typeof theChild === 'object' && + theChild != null && + 'props' in theChild && + theChild.props && + theChild.props.children + ) { + return getChildrenString(theChild.props.children); + } + return theChild != null && Object.keys(theChild).length > 0 ? (theChild as string) : ''; + }, []); + const aria = useMemo(() => { + if (ariaLabel) { + return ariaLabel; + } + return getChildrenString(children); + }, [ariaLabel, children, getChildrenString]); + + return ( + + + {children} + + ); + } ); LinkIcon.displayName = 'LinkIcon'; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx index 7286c6b743692..99dc8a802b33d 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx @@ -47,6 +47,7 @@ describe('Matrix Histogram Component', () => { errorMessage: 'error', histogramType: MatrixHistogramType.alerts, id: 'mockId', + indexNames: [], isInspected: false, isPtrIncluded: false, setQuery: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 485ca4c93133a..e7d7e60a3c408 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -37,7 +37,6 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps & hideHistogramIfEmpty?: boolean; histogramType: MatrixHistogramType; id: string; - indexToAdd?: string[] | null; legendPosition?: Position; mapping?: MatrixHistogramMappingTypes; showSpacer?: boolean; @@ -72,7 +71,7 @@ export const MatrixHistogramComponent: React.FC = histogramType, hideHistogramIfEmpty = false, id, - indexToAdd, + indexNames, legendPosition, mapping, panelHeight = DEFAULT_PANEL_HEIGHT, @@ -136,7 +135,7 @@ export const MatrixHistogramComponent: React.FC = errorMessage, filterQuery, histogramType, - indexToAdd, + indexNames, startDate, stackByField: selectedStackByOption.value, }); diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts index fc1df4d8ca85f..9a892110bde43 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts @@ -59,6 +59,7 @@ interface MatrixHistogramBasicProps { export interface MatrixHistogramQueryProps { endDate: string; errorMessage: string; + indexNames: string[]; filterQuery?: ESQuery | string | undefined; setAbsoluteRangeDatePicker?: ActionCreator<{ id: InputsModelId; @@ -68,7 +69,6 @@ export interface MatrixHistogramQueryProps { setAbsoluteRangeDatePickerTarget?: InputsModelId; stackByField: string; startDate: string; - indexToAdd?: string[] | null; histogramType: MatrixHistogramType; } diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts index 7a3f44d3ea729..03fa55a3c9fa6 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts @@ -13,7 +13,7 @@ import { } from './utils'; import { UpdateDateRange } from '../charts/common'; import { Position } from '@elastic/charts'; -import { MatrixOverTimeHistogramData } from '../../../graphql/types'; +import { MatrixHistogramData } from '../../../../common/search_strategy'; import { BarchartConfigs } from './types'; describe('utils', () => { @@ -77,7 +77,7 @@ describe('utils', () => { describe('formatToChartDataItem', () => { test('it should format data correctly', () => { - const data: [string, MatrixOverTimeHistogramData[]] = [ + const data: [string, MatrixHistogramData[]] = [ 'g1', [ { x: 1, y: 2, g: 'g1' }, diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts index 9474929d35a51..5b5b56cf0ec45 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts @@ -8,7 +8,7 @@ import { get, groupBy, map, toPairs } from 'lodash/fp'; import { UpdateDateRange, ChartSeriesData } from '../charts/common'; import { MatrixHistogramMappingTypes, BarchartConfigs } from './types'; -import { MatrixOverTimeHistogramData } from '../../../graphql/types'; +import { MatrixHistogramData } from '../../../../common/search_strategy'; import { histogramDateTimeFormatter } from '../utils'; interface GetBarchartConfigsProps { @@ -84,14 +84,14 @@ export const defaultLegendColors = [ export const formatToChartDataItem = ([key, value]: [ string, - MatrixOverTimeHistogramData[] + MatrixHistogramData[] ]): ChartSeriesData => ({ key, value, }); export const getCustomChartData = ( - data: MatrixOverTimeHistogramData[] | null, + data: MatrixHistogramData[] | null, mapping?: MatrixHistogramMappingTypes ): ChartSeriesData[] => { if (!data) return []; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx index e9940d088e606..07d148ff96dfa 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.test.tsx @@ -11,7 +11,6 @@ import '../../mock/match_media'; import { EntityDraggableComponent } from './entity_draggable'; import { TestProviders } from '../../mock/test_providers'; import { useMountAppended } from '../../utils/use_mount_appended'; - describe('entity_draggable', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx index 434cbd8ada88e..cb10c61302d3c 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx @@ -19,7 +19,6 @@ const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; const narrowDateRange = jest.fn(); - describe('anomaly_scores', () => { let anomalies: Anomalies = cloneDeep(mockAnomalies); const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx index a900c3e49f912..52151f217e01a 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx @@ -19,7 +19,6 @@ import { useMountAppended } from '../../../utils/use_mount_appended'; const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; const narrowDateRange = jest.fn(); - describe('anomaly_scores', () => { let anomalies: Anomalies = cloneDeep(mockAnomalies); const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx index d370a901a6262..3092cbf265ea9 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.test.tsx @@ -19,7 +19,6 @@ const startDate = new Date(2001).toISOString(); const endDate = new Date(3000).toISOString(); const interval = 'days'; const narrowDateRange = jest.fn(); - describe('get_anomalies_host_table_columns', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx index 69a4e383413f2..89f94a3819e65 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.test.tsx @@ -16,7 +16,6 @@ import { useMountAppended } from '../../../utils/use_mount_appended'; const startDate = new Date(2001).toISOString(); const endDate = new Date(3000).toISOString(); - describe('get_anomalies_network_table_columns', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx index e58d76bd1dde0..13518acdefdde 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/job_switch.test.tsx @@ -7,6 +7,7 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import { JobSwitchComponent } from './job_switch'; import { cloneDeep } from 'lodash/fp'; import { mockSecurityJobs } from '../api.mock'; @@ -31,7 +32,7 @@ describe('JobSwitch', () => { expect(wrapper).toMatchSnapshot(); }); - test('should call onJobStateChange when the switch is clicked to be true/open', () => { + test('should call onJobStateChange when the switch is clicked to be true/open', async () => { const wrapper = mount( { .simulate('click', { target: { checked: true }, }); - - expect(onJobStateChangeMock.mock.calls[0][0].id).toEqual( - 'linux_anomalous_network_activity_ecs' - ); - expect(onJobStateChangeMock.mock.calls[0][1]).toEqual(1571022859393); - expect(onJobStateChangeMock.mock.calls[0][2]).toEqual(true); + await waitFor(() => { + expect(onJobStateChangeMock.mock.calls[0][0].id).toEqual( + 'linux_anomalous_network_activity_ecs' + ); + expect(onJobStateChangeMock.mock.calls[0][1]).toEqual(1571022859393); + expect(onJobStateChangeMock.mock.calls[0][2]).toEqual(true); + }); }); test('should have a switch when it is not in the loading state', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx index dfcabe7b5aedf..3d7e47a15fc1e 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -6,6 +6,7 @@ import { shallow, mount } from 'enzyme'; import React from 'react'; +import { waitFor } from '@testing-library/react'; import { JobsTableComponent } from './jobs_table'; import { mockSecurityJobs } from '../api.mock'; import { cloneDeep } from 'lodash/fp'; @@ -59,7 +60,7 @@ describe('JobsTableComponent', () => { ); }); - test('should call onJobStateChange when the switch is clicked to be true/open', () => { + test('should call onJobStateChange when the switch is clicked to be true/open', async () => { const wrapper = mount( { .simulate('click', { target: { checked: true }, }); - expect(onJobStateChangeMock.mock.calls[0]).toEqual([securityJobs[0], 1571022859393, true]); + await waitFor(() => { + expect(onJobStateChangeMock.mock.calls[0]).toEqual([securityJobs[0], 1571022859393, true]); + }); }); test('should have a switch when it is not in the loading state', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 89aa77106933e..da5099f61e9b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -105,6 +105,7 @@ const getMockObject = ( }, }, }, + sourcerer: {}, }); const getUrlForAppMock = (appId: string, options?: { path?: string; absolute?: boolean }) => @@ -130,7 +131,7 @@ describe('Navigation Breadcrumbs', () => { }, { href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", text: 'Hosts', }, { @@ -150,7 +151,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Flows', @@ -169,7 +170,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Timelines', href: - "securitySolution:timelines?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:timelines?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -184,12 +185,12 @@ describe('Navigation Breadcrumbs', () => { { text: 'Hosts', href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'siem-kibana', href: - "securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Authentications', href: '' }, ]); @@ -205,11 +206,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: ipv4, - href: `securitySolution:network/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:network/ip/${ipv4}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -225,11 +226,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Network', href: - "securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:network?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: ipv6, - href: `securitySolution:network/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:network/ip/${ipv6Encoded}/source?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -245,7 +246,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Detections', href: - "securitySolution:detections?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:detections?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -259,7 +260,7 @@ describe('Navigation Breadcrumbs', () => { { text: 'Cases', href: - "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, ]); }); @@ -280,11 +281,11 @@ describe('Navigation Breadcrumbs', () => { { text: 'Cases', href: - "securitySolution:case?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:case?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: sampleCase.name, - href: `securitySolution:case/${sampleCase.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + href: `securitySolution:case/${sampleCase.id}?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, }, ]); }); @@ -311,12 +312,12 @@ describe('Navigation Breadcrumbs', () => { { text: 'Hosts', href: - "securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'siem-kibana', href: - "securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "securitySolution:hosts/siem-kibana?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", }, { text: 'Authentications', href: '' }, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts index 8f5a3ac63fa1a..ed71f55fd0161 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts @@ -19,12 +19,19 @@ import { import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { SearchNavTab } from './types'; +import { SourcererScopePatterns } from '../../store/sourcerer/model'; export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => { 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[] | TimelineUrl | string = ''; + let urlStateToReplace: + | Filter[] + | Query + | SourcererScopePatterns + | TimelineUrl + | UrlInputsModel + | string = ''; if (urlKey === CONSTANTS.appQuery && urlState.query != null) { if (urlState.query.query === '') { @@ -40,6 +47,8 @@ export const getSearch = (tab: SearchNavTab, urlState: UrlState): string => { } } else if (urlKey === CONSTANTS.timerange) { urlStateToReplace = urlState[CONSTANTS.timerange]; + } else if (urlKey === CONSTANTS.sourcerer) { + urlStateToReplace = urlState[CONSTANTS.sourcerer]; } else if (urlKey === CONSTANTS.timeline && urlState[CONSTANTS.timeline] != null) { const timeline = urlState[CONSTANTS.timeline]; if (timeline.id === '') { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index 16cb19f5a0c14..102ed7851e57d 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -78,6 +78,7 @@ describe('SIEM Navigation', () => { }, [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, @@ -145,6 +146,7 @@ describe('SIEM Navigation', () => { pageName: 'hosts', pathName: '/', search: '', + sourcerer: {}, state: undefined, tabName: 'authentications', query: { query: '', language: 'kuery' }, @@ -252,6 +254,7 @@ describe('SIEM Navigation', () => { query: { language: 'kuery', query: '' }, savedQuery: undefined, search: '', + sourcerer: {}, state: undefined, tabName: 'authentications', timeline: { id: '', isOpen: false, graphEventId: '' }, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx index 5ee35e7da0f3e..b149488ff38a7 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx @@ -40,19 +40,20 @@ export const SiemNavigationComponent: React.FC< if (pathName || pageName) { setBreadcrumbs( { - query: urlState.query, detailName, filters: urlState.filters, + flowTarget, navTabs, pageName, pathName, + query: urlState.query, savedQuery: urlState.savedQuery, search, + sourcerer: urlState.sourcerer, + state, tabName, - flowTarget, - timerange: urlState.timerange, timeline: urlState.timeline, - state, + timerange: urlState.timerange, }, chrome, getUrlForApp @@ -69,6 +70,7 @@ export const SiemNavigationComponent: React.FC< navTabs={navTabs} pageName={pageName} pathName={pathName} + sourcerer={urlState.sourcerer} savedQuery={urlState.savedQuery} tabName={tabName} timeline={urlState.timeline} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index b25cf3779801b..5c69edbabdc66 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -68,6 +68,7 @@ describe('Tab Navigation', () => { }, [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, @@ -126,6 +127,7 @@ describe('Tab Navigation', () => { }, [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, [CONSTANTS.filters]: [], + [CONSTANTS.sourcerer]: {}, [CONSTANTS.timeline]: { id: '', isOpen: false, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index 217ad0e58570f..3eb66b5591b85 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -94,10 +94,17 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { () => Object.values(navTabs).map((tab) => { const isSelected = selectedTabId === tab.id; - const { query, filters, savedQuery, timerange, timeline } = props; - const search = getSearch(tab, { query, filters, savedQuery, timerange, timeline }); + const { filters, query, savedQuery, sourcerer, timeline, timerange } = props; + const search = getSearch(tab, { + filters, + query, + savedQuery, + sourcerer, + timeline, + timerange, + }); const hrefWithSearch = - tab.href + getSearch(tab, { query, filters, savedQuery, timerange, timeline }); + tab.href + getSearch(tab, { filters, query, savedQuery, sourcerer, timeline, timerange }); return ( { const mapState = makeMapStateToProps(); - const { urlState } = useSelector(mapState, isEqual); + const { urlState } = useDeepEqualSelector(mapState); const urlSearch = useMemo(() => getSearch(tab, urlState), [tab, urlState]); return urlSearch; }; diff --git a/x-pack/plugins/security_solution/public/common/components/page/index.tsx b/x-pack/plugins/security_solution/public/common/components/page/index.tsx index 140429dc4abd7..8a8eda3e20185 100644 --- a/x-pack/plugins/security_solution/public/common/components/page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/page/index.tsx @@ -8,27 +8,36 @@ import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiIcon, EuiPage } from '@e import styled, { createGlobalStyle } from 'styled-components'; import { + GLOBAL_HEADER_HEIGHT, FULL_SCREEN_TOGGLED_CLASS_NAME, SCROLLING_DISABLED_CLASS_NAME, } from '../../../../common/constants'; +export const SecuritySolutionAppWrapper = styled.div` + display: flex; + flex-direction: column; + flex: 1 1 auto; + width: 100%; +`; +SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper'; + /* SIDE EFFECT: the following `createGlobalStyle` overrides default styling in angular code that was not theme-friendly and `EuiPopover`, `EuiToolTip` global styles */ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimary: string } } }>` - /* dirty hack to fix draggables with tooltip on FF */ - body#siem-app { - position: static; - } - /* end of dirty hack to fix draggables with tooltip on FF */ - div.app-wrapper { background-color: rgba(0,0,0,0); } div.application { background-color: rgba(0,0,0,0); + + // Security App wrapper + > div { + display: flex; + flex: 1 1 auto; + } } .euiPopover__panel.euiPopover__panel-isOpen { @@ -67,37 +76,8 @@ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimar ${({ theme }) => `background-color: ${theme.eui.euiColorPrimary} !important`}; } - body { - overflow-y: hidden; - } - - #kibana-body { - height: 100%; - overflow-y: hidden; - - > .content { - height: 100%; - - > .app-wrapper { - height: 100%; - - > .app-wrapper-panel { - height: 100%; - - > .application { - height: 100%; - - > div { - height: 100%; - } - } - } - } - } - } - - .${SCROLLING_DISABLED_CLASS_NAME} #kibana-body { - overflow-y: hidden; + .${SCROLLING_DISABLED_CLASS_NAME} ${SecuritySolutionAppWrapper} { + max-height: calc(100vh - ${GLOBAL_HEADER_HEIGHT}px); } `; diff --git a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx index aa61688f1f986..12199ce5c1b66 100644 --- a/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/query_bar/index.test.tsx @@ -6,7 +6,7 @@ import { mount } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { DEFAULT_FROM, DEFAULT_TO } from '../../../../common/constants'; import { TestProviders, mockIndexPattern } from '../../mock'; @@ -16,22 +16,6 @@ import { QueryBar, QueryBarComponentProps } from '.'; const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; describe('QueryBar ', () => { - // We are doing that because we need to wrapped this component with redux - // and redux does not like to be updated and since we need to update our - // child component (BODY) and we do not want to scare anyone with this error - // we are hiding it!!! - // eslint-disable-next-line no-console - const originalError = console.error; - beforeAll(() => { - // eslint-disable-next-line no-console - console.error = (...args: string[]) => { - if (/ does not support changing `store` on the fly/.test(args[0])) { - return; - } - originalError.call(console, ...args); - }; - }); - const mockOnChangeQuery = jest.fn(); const mockOnSubmitQuery = jest.fn(); const mockOnSavedQuery = jest.fn(); @@ -372,7 +356,7 @@ describe('QueryBar ', () => { }); describe('SavedQueryManagementComponent state', () => { - test('popover should hidden when "Save current query" button was clicked', () => { + test('popover should hidden when "Save current query" button was clicked', async () => { const Proxy = (props: QueryBarComponentProps) => ( @@ -397,21 +381,24 @@ describe('QueryBar ', () => { onSavedQuery={mockOnSavedQuery} /> ); + await waitFor(() => { + const isSavedQueryPopoverOpen = () => + wrapper.find('EuiPopover[id="savedQueryPopover"]').prop('isOpen'); - const isSavedQueryPopoverOpen = () => - wrapper.find('EuiPopover[id="savedQueryPopover"]').prop('isOpen'); - - expect(isSavedQueryPopoverOpen()).toBeFalsy(); + expect(isSavedQueryPopoverOpen()).toBeFalsy(); - wrapper - .find('button[data-test-subj="saved-query-management-popover-button"]') - .simulate('click'); + wrapper + .find('button[data-test-subj="saved-query-management-popover-button"]') + .simulate('click'); - expect(isSavedQueryPopoverOpen()).toBeTruthy(); + expect(isSavedQueryPopoverOpen()).toBeTruthy(); - wrapper.find('button[data-test-subj="saved-query-management-save-button"]').simulate('click'); + wrapper + .find('button[data-test-subj="saved-query-management-save-button"]') + .simulate('click'); - expect(isSavedQueryPopoverOpen()).toBeFalsy(); + expect(isSavedQueryPopoverOpen()).toBeFalsy(); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx index c330bb073b146..2696b115cdc18 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx @@ -6,107 +6,138 @@ import React from 'react'; import { mount } from 'enzyme'; -import { SecurityPageName } from '../../containers/sourcerer/constants'; -import { mockPatterns, mockSourceGroup } from '../../containers/sourcerer/mocks'; -import { MaybeSourcerer } from './index'; -import * as i18n from './translations'; -import { ADD_INDEX_PATH } from '../../../../common/constants'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { SourcererComponent } from './index'; +import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; +import { sourcererActions, sourcererModel } from '../../store/sourcerer'; +import { + apolloClientObservable, + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../mock'; +import { createStore, State } from '../../store'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { waitFor } from '@testing-library/react'; -const updateSourceGroupIndicies = jest.fn(); -const mockManageSource = { - activeSourceGroupId: SecurityPageName.default, - availableIndexPatterns: mockPatterns, - availableSourceGroupIds: [SecurityPageName.default], - getManageSourceGroupById: jest.fn().mockReturnValue(mockSourceGroup(SecurityPageName.default)), - initializeSourceGroup: jest.fn(), - isIndexPatternsLoading: false, - setActiveSourceGroupId: jest.fn(), - updateSourceGroupIndicies, -}; -jest.mock('../../containers/sourcerer', () => { - const original = jest.requireActual('../../containers/sourcerer'); +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); return { ...original, - useManageSource: () => mockManageSource, + useDispatch: () => mockDispatch, }; }); const mockOptions = [ - { label: 'auditbeat-*', key: 'auditbeat-*-0', value: 'auditbeat-*', checked: 'on' }, - { label: 'endgame-*', key: 'endgame-*-1', value: 'endgame-*', checked: 'on' }, - { label: 'filebeat-*', key: 'filebeat-*-2', value: 'filebeat-*', checked: 'on' }, - { label: 'logs-*', key: 'logs-*-3', value: 'logs-*', checked: 'on' }, - { label: 'packetbeat-*', key: 'packetbeat-*-4', value: 'packetbeat-*', checked: undefined }, - { label: 'winlogbeat-*', key: 'winlogbeat-*-5', value: 'winlogbeat-*', checked: 'on' }, - { - label: 'apm-*-transaction*', - key: 'apm-*-transaction*-0', - value: 'apm-*-transaction*', - disabled: true, - checked: undefined, - }, - { - label: 'blobbeat-*', - key: 'blobbeat-*-1', - value: 'blobbeat-*', - disabled: true, - checked: undefined, - }, + { label: 'apm-*-transaction*', value: 'apm-*-transaction*' }, + { label: 'auditbeat-*', value: 'auditbeat-*' }, + { label: 'endgame-*', value: 'endgame-*' }, + { label: 'filebeat-*', value: 'filebeat-*' }, + { label: 'logs-*', value: 'logs-*' }, + { label: 'packetbeat-*', value: 'packetbeat-*' }, + { label: 'winlogbeat-*', value: 'winlogbeat-*' }, ]; +const defaultProps = { + scope: sourcererModel.SourcererScopeName.default, +}; describe('Sourcerer component', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + const state: State = mockGlobalState; + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + + beforeEach(() => { + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + }); + // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects - it('Mounts with correct options selected and disabled', () => { - const wrapper = mount(); + it('Mounts with all options selected', () => { + const wrapper = mount( + + + + ); wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - expect( - wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('options') + wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('selectedOptions') ).toEqual(mockOptions); }); - it('onChange calls updateSourceGroupIndicies', () => { - const wrapper = mount(); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - - const switcherOnChange = wrapper - .find(`[data-test-subj="indexPattern-switcher"]`) - .first() - .prop('onChange'); - // @ts-ignore - switcherOnChange([mockOptions[0], mockOptions[1]]); - expect(updateSourceGroupIndicies).toHaveBeenCalledWith(SecurityPageName.default, [ - mockOptions[0].value, - mockOptions[1].value, - ]); - }); - it('Disabled options have icon tooltip', () => { - const wrapper = mount(); - wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - // @ts-ignore - const Rendered = wrapper - .find(`[data-test-subj="indexPattern-switcher"]`) - .first() - .prop('renderOption')( - { - label: 'blobbeat-*', - key: 'blobbeat-*-1', - value: 'blobbeat-*', - disabled: true, - checked: undefined, + it('Mounts with some options selected', () => { + const state2 = { + ...mockGlobalState, + sourcerer: { + ...mockGlobalState.sourcerer, + sourcererScopes: { + ...mockGlobalState.sourcerer.sourcererScopes, + [SourcererScopeName.default]: { + ...mockGlobalState.sourcerer.sourcererScopes[SourcererScopeName.default], + loading: false, + selectedPatterns: [DEFAULT_INDEX_PATTERN[0]], + }, + }, }, - '' + }; + + store = createStore( + state2, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + const wrapper = mount( + + + ); - expect(Rendered.props.children[1].props.content).toEqual(i18n.DISABLED_INDEX_PATTERNS); + wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); + expect( + wrapper.find(`[data-test-subj="indexPattern-switcher"]`).first().prop('selectedOptions') + ).toEqual([mockOptions[0]]); }); - - it('Button links to index path', () => { - const wrapper = mount(); + it('onChange calls updateSourcererScopeIndices', async () => { + const wrapper = mount( + + + + ); + expect(true).toBeTruthy(); wrapper.find(`[data-test-subj="sourcerer-trigger"]`).first().simulate('click'); - expect(wrapper.find(`[data-test-subj="add-index"]`).first().prop('href')).toEqual( - ADD_INDEX_PATH + await waitFor(() => { + ((wrapper.find(EuiComboBox).props() as unknown) as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + }).onChange([mockOptions[0], mockOptions[1]]); + wrapper.update(); + }); + wrapper.find(`[data-test-subj="add-index"]`).first().simulate('click'); + + expect(mockDispatch).toHaveBeenCalledWith( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.default, + selectedPatterns: [mockOptions[0].value, mockOptions[1].value], + }) ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx index 6275ce19c3608..7a74f5bf2247f 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx @@ -4,50 +4,122 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useMemo, useState } from 'react'; import { EuiButton, EuiButtonEmpty, - EuiHighlight, - EuiIconTip, + EuiComboBox, + EuiComboBoxOptionOption, + EuiIcon, + EuiFlexGroup, + EuiFlexItem, EuiPopover, - EuiPopoverFooter, EuiPopoverTitle, - EuiSelectable, + EuiSpacer, + EuiText, + EuiToolTip, } from '@elastic/eui'; -import { EuiSelectableOption } from '@elastic/eui/src/components/selectable/selectable_option'; -import { useManageSource } from '../../containers/sourcerer'; +import deepEqual from 'fast-deep-equal'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import styled from 'styled-components'; + import * as i18n from './translations'; import { SOURCERER_FEATURE_FLAG_ON } from '../../containers/sourcerer/constants'; -import { ADD_INDEX_PATH } from '../../../../common/constants'; - -export const MaybeSourcerer = React.memo(() => { - const { - activeSourceGroupId, - availableIndexPatterns, - getManageSourceGroupById, - isIndexPatternsLoading, - updateSourceGroupIndicies, - } = useManageSource(); - const { defaultPatterns, indexPatterns: selectedOptions, loading: loadingIndices } = useMemo( - () => getManageSourceGroupById(activeSourceGroupId), - [getManageSourceGroupById, activeSourceGroupId] +import { sourcererActions, sourcererModel } from '../../store/sourcerer'; +import { State } from '../../store'; +import { getSourcererScopeSelector, SourcererScopeSelector } from './selectors'; + +const PopoverContent = styled.div` + width: 600px; +`; + +const ResetButton = styled(EuiButtonEmpty)` + width: fit-content; +`; +interface SourcererComponentProps { + scope: sourcererModel.SourcererScopeName; +} + +export const SourcererComponent = React.memo(({ scope: scopeId }) => { + const dispatch = useDispatch(); + const sourcererScopeSelector = useMemo(getSourcererScopeSelector, []); + const { configIndexPatterns, kibanaIndexPatterns, sourcererScope } = useSelector< + State, + SourcererScopeSelector + >((state) => sourcererScopeSelector(state, scopeId), deepEqual); + const { selectedPatterns, loading } = sourcererScope; + const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const [selectedOptions, setSelectedOptions] = useState>>( + selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) ); - const loading = useMemo(() => loadingIndices || isIndexPatternsLoading, [ - isIndexPatternsLoading, - loadingIndices, - ]); + const setPopoverIsOpenCb = useCallback(() => setPopoverIsOpen((prevState) => !prevState), []); const onChangeIndexPattern = useCallback( - (newIndexPatterns: string[]) => { - updateSourceGroupIndicies(activeSourceGroupId, newIndexPatterns); + (newSelectedPatterns: string[]) => { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scopeId, + selectedPatterns: newSelectedPatterns, + }) + ); }, - [activeSourceGroupId, updateSourceGroupIndicies] + [dispatch, scopeId] + ); + + const renderOption = useCallback( + (option) => { + const { value } = option; + if (kibanaIndexPatterns.some((kip) => kip.title === value)) { + return ( + <> + {value} + + ); + } + return <>{value}; + }, + [kibanaIndexPatterns] + ); + + const onChangeCombo = useCallback((newSelectedOptions) => { + setSelectedOptions(newSelectedOptions); + }, []); + + const resetDataSources = useCallback(() => { + setSelectedOptions( + configIndexPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })) + ); + }, [configIndexPatterns]); + + const handleSaveIndices = useCallback(() => { + onChangeIndexPattern(selectedOptions.map((so) => so.label)); + setPopoverIsOpen(false); + }, [onChangeIndexPattern, selectedOptions]); + + const handleClosePopOver = useCallback(() => { + setPopoverIsOpen(false); + }, []); + + const indexesPatternOptions = useMemo( + () => + [...configIndexPatterns, ...kibanaIndexPatterns.map((kip) => kip.title)].reduce< + Array> + >((acc, index) => { + if (index != null && !acc.some((o) => o.label.includes(index))) { + return [...acc, { label: index, value: index }]; + } + return acc; + }, []), + [configIndexPatterns, kibanaIndexPatterns] ); - const [isPopoverOpen, setPopoverIsOpen] = useState(false); - const setPopoverIsOpenCb = useCallback(() => setPopoverIsOpen((prevState) => !prevState), []); const trigger = useMemo( () => ( { data-test-subj="sourcerer-trigger" flush="left" iconSide="right" - iconType="indexSettings" + iconType="arrowDown" + isLoading={loading} onClick={setPopoverIsOpenCb} size="l" title={i18n.SOURCERER} @@ -63,99 +136,91 @@ export const MaybeSourcerer = React.memo(() => { {i18n.SOURCERER} ), - [setPopoverIsOpenCb] - ); - const options: EuiSelectableOption[] = useMemo( - () => - availableIndexPatterns.map((title, id) => ({ - label: title, - key: `${title}-${id}`, - value: title, - checked: selectedOptions.includes(title) ? 'on' : undefined, - })), - [availableIndexPatterns, selectedOptions] + [setPopoverIsOpenCb, loading] ); - const unSelectableOptions: EuiSelectableOption[] = useMemo( - () => - defaultPatterns - .filter((title) => !availableIndexPatterns.includes(title)) - .map((title, id) => ({ - label: title, - key: `${title}-${id}`, - value: title, - disabled: true, - checked: undefined, - })), - [availableIndexPatterns, defaultPatterns] - ); - const renderOption = useCallback( - (option, searchValue) => ( - <> - {option.label} - {option.disabled ? ( - - ) : null} - + + const comboBox = useMemo( + () => ( + ), - [] + [indexesPatternOptions, onChangeCombo, renderOption, selectedOptions] ); - const onChange = useCallback( - (choices: EuiSelectableOption[]) => { - const choice = choices.reduce( - (acc, { checked, label }) => (checked === 'on' ? [...acc, label] : acc), - [] - ); - onChangeIndexPattern(choice); - }, - [onChangeIndexPattern] + + useEffect(() => { + const newSelecteOptions = selectedPatterns.map((indexSelected) => ({ + label: indexSelected, + value: indexSelected, + })); + setSelectedOptions((prevSelectedOptions) => { + if (!deepEqual(newSelecteOptions, prevSelectedOptions)) { + return newSelecteOptions; + } + return prevSelectedOptions; + }); + }, [selectedPatterns]); + + const tooltipContent = useMemo( + () => (isPopoverOpen ? null : sourcererScope.selectedPatterns.sort().join(', ')), + [isPopoverOpen, sourcererScope.selectedPatterns] ); - const allOptions = useMemo(() => [...options, ...unSelectableOptions], [ - options, - unSelectableOptions, - ]); + return ( - setPopoverIsOpen(false)} - display="block" - panelPaddingSize="s" - ownFocus - > -
- - <> - {i18n.CHANGE_INDEX_PATTERNS} - - - - - {(list, search) => ( - <> - {search} - {list} - - )} - - - - {i18n.ADD_INDEX_PATTERNS} - - -
-
+ + + + + <>{i18n.SELECT_INDEX_PATTERNS} + + + {i18n.INDEX_PATTERNS_SELECTION_LABEL} + + {comboBox} + + + + + {i18n.INDEX_PATTERNS_RESET} + + + + + {i18n.SAVE_INDEX_PATTERNS} + + + + + + ); }); -MaybeSourcerer.displayName = 'Sourcerer'; +SourcererComponent.displayName = 'Sourcerer'; -export const Sourcerer = SOURCERER_FEATURE_FLAG_ON ? MaybeSourcerer : () => null; +export const Sourcerer = SOURCERER_FEATURE_FLAG_ON ? SourcererComponent : () => null; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.tsx new file mode 100644 index 0000000000000..6bbe24e921880 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/selectors.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 { State } from '../../store'; +import { sourcererSelectors } from '../../store/sourcerer'; +import { KibanaIndexPatterns, ManageScope, SourcererScopeName } from '../../store/sourcerer/model'; + +export interface SourcererScopeSelector { + configIndexPatterns: string[]; + kibanaIndexPatterns: KibanaIndexPatterns; + sourcererScope: ManageScope; +} + +export const getSourcererScopeSelector = () => { + const getKibanaIndexPatternsSelector = sourcererSelectors.kibanaIndexPatternsSelector(); + const getScopesSelector = sourcererSelectors.scopesSelector(); + const getConfigIndexPatternsSelector = sourcererSelectors.configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): SourcererScopeSelector => { + const kibanaIndexPatterns = getKibanaIndexPatternsSelector(state); + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return { + kibanaIndexPatterns, + configIndexPatterns, + sourcererScope: scope, + }; + }; + + return mapStateToProps; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts b/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts index 71b1734dad6a6..473eb43d5c4fe 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/translations.ts @@ -6,23 +6,26 @@ import { i18n } from '@kbn/i18n'; -export const SOURCERER = i18n.translate('xpack.securitySolution.indexPatterns.sourcerer', { - defaultMessage: 'Sourcerer', +export const SOURCERER = i18n.translate('xpack.securitySolution.indexPatterns.dataSourcesLabel', { + defaultMessage: 'Data sources', }); -export const CHANGE_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.help', { - defaultMessage: 'Change index patterns', +export const ALL_DEFAULT = i18n.translate('xpack.securitySolution.indexPatterns.allDefault', { + defaultMessage: 'All default', }); -export const ADD_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.add', { - defaultMessage: 'Configure Kibana index patterns', +export const SELECT_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.help', { + defaultMessage: 'Data sources selection', }); -export const CONFIGURE_INDEX_PATTERNS = i18n.translate( - 'xpack.securitySolution.indexPatterns.configure', +export const SAVE_INDEX_PATTERNS = i18n.translate('xpack.securitySolution.indexPatterns.save', { + defaultMessage: 'Save', +}); + +export const INDEX_PATTERNS_SELECTION_LABEL = i18n.translate( + 'xpack.securitySolution.indexPatterns.selectionLabel', { - defaultMessage: - 'Configure additional Kibana index patterns to see them become available in the Security Solution', + defaultMessage: 'Choose the source of the data on this page', } ); @@ -33,3 +36,17 @@ export const DISABLED_INDEX_PATTERNS = i18n.translate( 'Disabled index patterns are recommended on this page, but first need to be configured in your Kibana index pattern settings', } ); + +export const INDEX_PATTERNS_RESET = i18n.translate( + 'xpack.securitySolution.indexPatterns.resetButton', + { + defaultMessage: 'Reset', + } +); + +export const PICK_INDEX_PATTERNS = i18n.translate( + 'xpack.securitySolution.indexPatterns.pickIndexPatternsCombo', + { + defaultMessage: 'Pick index patterns', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx index 664d8b2ff5598..310d4c52ec5bc 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx @@ -39,8 +39,10 @@ import { } from '../../mock'; import { State, createStore } from '../../store'; import { Provider as ReduxStoreProvider } from 'react-redux'; -import { KpiHostsData } from '../../../graphql/types'; -import { NetworkKpiStrategyResponse } from '../../../../common/search_strategy'; +import { + HostsKpiStrategyResponse, + NetworkKpiStrategyResponse, +} from '../../../../common/search_strategy'; const from = '2019-06-15T06:00:00.000Z'; const to = '2019-06-18T06:00:00.000Z'; @@ -242,7 +244,7 @@ describe('useKpiMatrixStatus', () => { data, }: { fieldsMapping: Readonly; - data: NetworkKpiStrategyResponse | KpiHostsData; + data: NetworkKpiStrategyResponse | HostsKpiStrategyResponse; }) => { const statItemsProps: StatItemsProps[] = useKpiMatrixStatus( fieldsMapping, diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx index 13a93a784a2c9..34fb344eed3c4 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.tsx @@ -18,8 +18,10 @@ import { get, getOr } from 'lodash/fp'; import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; -import { NetworkKpiStrategyResponse } from '../../../../common/search_strategy'; -import { KpiHostsData } from '../../../graphql/types'; +import { + HostsKpiStrategyResponse, + NetworkKpiStrategyResponse, +} from '../../../../common/search_strategy'; import { AreaChart } from '../charts/areachart'; import { BarChart } from '../charts/barchart'; import { ChartSeriesData, ChartData, ChartSeriesConfigs, UpdateDateRange } from '../charts/common'; @@ -113,12 +115,12 @@ export const barchartConfigs = (config?: { onElementClick?: ElementClickListener export const addValueToFields = ( fields: StatItem[], - data: KpiHostsData | NetworkKpiStrategyResponse + data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse ): StatItem[] => fields.map((field) => ({ ...field, value: get(field.key, data) })); export const addValueToAreaChart = ( fields: StatItem[], - data: KpiHostsData | NetworkKpiStrategyResponse + data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse ): ChartSeriesData[] => fields .filter((field) => get(`${field.key}Histogram`, data) != null) @@ -130,7 +132,7 @@ export const addValueToAreaChart = ( export const addValueToBarChart = ( fields: StatItem[], - data: KpiHostsData | NetworkKpiStrategyResponse + data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse ): ChartSeriesData[] => { if (fields.length === 0) return []; return fields.reduce((acc: ChartSeriesData[], field: StatItem, idx: number) => { @@ -159,7 +161,7 @@ export const addValueToBarChart = ( export const useKpiMatrixStatus = ( mappings: Readonly, - data: KpiHostsData | NetworkKpiStrategyResponse, + data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse, id: string, from: string, to: string, diff --git a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx index b28c7e70b8ae8..f091f22abcb94 100644 --- a/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/tables/helpers.test.tsx @@ -17,7 +17,6 @@ import { import { TestProviders } from '../../mock'; import { getEmptyValue } from '../empty_value'; import { useMountAppended } from '../../utils/use_mount_appended'; - describe('Table Helpers', () => { const items = ['item1', 'item2', 'item3']; const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts b/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts index b654eaf17b47b..79cbd87cda201 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/top_n/helpers.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EventType } from '../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../common/types/timeline'; import * as i18n from './translations'; export interface TopNOption { inputDisplay: string; - value: EventType; + value: TimelineEventsType; 'data-test-subj': string; } @@ -52,8 +52,8 @@ export const defaultOptions = [...rawEvents, ...alertEvents]; * is always in sync with the `EventType` chosen by the user in * the active timeline. */ -export const getOptions = (activeTimelineEventType?: EventType): TopNOption[] => { - switch (activeTimelineEventType) { +export const getOptions = (activeTimelineEventsType?: TimelineEventsType): TopNOption[] => { + switch (activeTimelineEventsType) { case 'all': return allEvents; case 'raw': diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index 31318122eb564..fd1fa1c29a807 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -6,7 +6,7 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; import { mockBrowserFields } from '../../containers/source/mock'; import { @@ -168,20 +168,18 @@ const store = createStore( storage ); -describe('StatefulTopN', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); +let testProps = { + browserFields: mockBrowserFields, + field, + indexNames: [], + indexPattern: mockIndexPattern, + timelineId: TimelineId.hostsPageExternalAlerts, + toggleTopN: jest.fn(), + onFilterAdded: jest.fn(), + value, +}; +describe('StatefulTopN', () => { describe('rendering in a global NON-timeline context', () => { let wrapper: ReactWrapper; @@ -189,16 +187,7 @@ describe('StatefulTopN', () => { wrapper = mount( - + ); @@ -277,19 +266,14 @@ describe('StatefulTopN', () => { filterManager, }, }; + testProps = { + ...testProps, + timelineId: TimelineId.active, + }; wrapper = mount( - + ); @@ -345,37 +329,34 @@ describe('StatefulTopN', () => { expect(props.to).toEqual('2020-04-15T03:46:09.047Z'); }); }); + describe('rendering in a NON-active timeline context', () => { + test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, async () => { + const filterManager = new FilterManager(mockUiSettingsForFilterManager); - test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, () => { - const filterManager = new FilterManager(mockUiSettingsForFilterManager); + const manageTimelineForTesting = { + [TimelineId.active]: { + ...getTimelineDefaults(TimelineId.active), + filterManager, + documentType: 'alerts', + }, + }; - const manageTimelineForTesting = { - [TimelineId.active]: { - ...getTimelineDefaults(TimelineId.active), - filterManager, - documentType: 'alerts', - }, - }; - - const wrapper = mount( - - - - - - ); - - const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; - - expect(props.defaultView).toEqual('alert'); + testProps = { + ...testProps, + timelineId: TimelineId.detectionsPage, + }; + const wrapper = mount( + + + + + + ); + await waitFor(() => { + const props = wrapper.find('[data-test-subj="top-n"]').first().props() as Props; + + expect(props.defaultView).toEqual('alert'); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx index d71242329bcda..9c81cb57335a5 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx @@ -74,7 +74,7 @@ interface OwnProps { browserFields: BrowserFields; field: string; indexPattern: IIndexPattern; - indexToAdd: string[] | null; + indexNames: string[]; timelineId?: string; toggleTopN: () => void; onFilterAdded?: () => void; @@ -93,7 +93,7 @@ const StatefulTopNComponent: React.FC = ({ dataProviders, field, indexPattern, - indexToAdd, + indexNames, globalFilters = EMPTY_FILTERS, globalQuery = EMPTY_QUERY, kqlMode, @@ -109,7 +109,6 @@ const StatefulTopNComponent: React.FC = ({ const options = getOptions( timelineId === TimelineId.active ? activeTimelineEventType : undefined ); - return ( = ({ filters={timelineId === TimelineId.active ? EMPTY_FILTERS : globalFilters} from={timelineId === TimelineId.active ? activeTimelineFrom : from} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={indexNames} options={options} query={timelineId === TimelineId.active ? EMPTY_QUERY : globalQuery} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx index 667d1816e8f07..f7ad35f2c5a37 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx @@ -6,13 +6,13 @@ import { mount, ReactWrapper } from 'enzyme'; import React from 'react'; - +import { waitFor } from '@testing-library/react'; import '../../mock/match_media'; import { TestProviders, mockIndexPattern } from '../../mock'; import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { allEvents, defaultOptions } from './helpers'; -import { TopN } from './top_n'; +import { TopN, Props as TopNProps } from './top_n'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -88,44 +88,36 @@ const combinedQueries = { }; describe('TopN', () => { - // Suppress warnings about "react-beautiful-dnd" - /* eslint-disable no-console */ - const originalError = console.error; - const originalWarn = console.warn; - beforeAll(() => { - console.warn = jest.fn(); - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - console.warn = originalWarn; - }); - const query = { query: '', language: 'kuery' }; + const toggleTopN = jest.fn(); + const eventTypes: { [id: string]: TopNProps['defaultView'] } = { + raw: 'raw', + alert: 'alert', + all: 'all', + }; + let testProps: TopNProps = { + defaultView: eventTypes.raw, + field, + filters: [], + from: '2020-04-14T00:31:47.695Z', + indexNames: [], + indexPattern: mockIndexPattern, + options: defaultOptions, + query, + setAbsoluteRangeDatePicker, + setAbsoluteRangeDatePickerTarget: 'global', + setQuery: jest.fn(), + to: '2020-04-15T00:31:47.695Z', + toggleTopN, + value, + }; describe('common functionality', () => { - let toggleTopN: () => void; let wrapper: ReactWrapper; - beforeEach(() => { - toggleTopN = jest.fn(); wrapper = mount( - + ); }); @@ -143,28 +135,12 @@ describe('TopN', () => { }); describe('events view', () => { - let toggleTopN: () => void; let wrapper: ReactWrapper; beforeEach(() => { - toggleTopN = jest.fn(); wrapper = mount( - + ); }); @@ -181,40 +157,35 @@ describe('TopN', () => { }); describe('alerts view', () => { - let toggleTopN: () => void; - let wrapper: ReactWrapper; + beforeAll(() => { + testProps = { + ...testProps, + defaultView: eventTypes.alert, + }; + }); - beforeEach(() => { - toggleTopN = jest.fn(); - wrapper = mount( + test(`it renders SignalsByCategory when defaultView is 'alert'`, async () => { + const wrapper = mount( - + ); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBe(true); + }); }); - test(`it renders SignalsByCategory when defaultView is 'signal'`, () => { - expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBe(true); - }); - - test(`it does NOT render EventsByDataset when defaultView is 'signal'`, () => { - expect( - wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').exists() - ).toBe(false); + test(`it does NOT render EventsByDataset when defaultView is 'alert'`, async () => { + const wrapper = mount( + + + + ); + await waitFor(() => { + expect( + wrapper.find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]').exists() + ).toBe(false); + }); }); }); @@ -222,24 +193,14 @@ describe('TopN', () => { let wrapper: ReactWrapper; beforeEach(() => { + testProps = { + ...testProps, + defaultView: eventTypes.all, + options: allEvents, + }; wrapper = mount( - + ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx index 064241a7216f4..4f0a71dcc3ebb 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx @@ -14,7 +14,7 @@ import { EventsByDataset } from '../../../overview/components/events_by_dataset' import { SignalsByCategory } from '../../../overview/components/signals_by_category'; import { Filter, IIndexPattern, Query } from '../../../../../../../src/plugins/data/public'; import { InputsModelId } from '../../store/inputs/constants'; -import { EventType } from '../../../timelines/store/timeline/model'; +import { TimelineEventsType } from '../../../../common/types/timeline'; import { TopNOption } from './helpers'; import * as i18n from './translations'; @@ -45,11 +45,11 @@ const TopNContent = styled.div` export interface Props extends Pick { combinedQueries?: string; - defaultView: EventType; + defaultView: TimelineEventsType; field: string; filters: Filter[]; indexPattern: IIndexPattern; - indexToAdd?: string[] | null; + indexNames: string[]; options: TopNOption[]; query: Query; setAbsoluteRangeDatePicker: ActionCreator<{ @@ -75,7 +75,7 @@ const TopNComponent: React.FC = ({ field, from, indexPattern, - indexToAdd, + indexNames, options, query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, @@ -85,8 +85,10 @@ const TopNComponent: React.FC = ({ to, toggleTopN, }) => { - const [view, setView] = useState(defaultView); - const onViewSelected = useCallback((value: string) => setView(value as EventType), [setView]); + const [view, setView] = useState(defaultView); + const onViewSelected = useCallback((value: string) => setView(value as TimelineEventsType), [ + setView, + ]); useEffect(() => { setView(defaultView); @@ -123,7 +125,7 @@ const TopNComponent: React.FC = ({ from={from} headerChildren={headerChildren} indexPattern={indexPattern} - indexToAdd={indexToAdd} + indexNames={indexNames} onlyField={field} query={query} setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget} diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/translations.ts b/x-pack/plugins/security_solution/public/common/components/top_n/translations.ts index b149a5eb1458f..76bb3dcfa6d37 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/top_n/translations.ts @@ -19,5 +19,5 @@ export const RAW_EVENTS = i18n.translate('xpack.securitySolution.topN.rawEventsS }); export const ALERT_EVENTS = i18n.translate('xpack.securitySolution.topN.alertEventsSelectLabel', { - defaultMessage: 'Alert events', + defaultMessage: 'Detection Alerts', }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts index 5a4aec93dd9aa..e5c09d229808b 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts @@ -17,6 +17,7 @@ export enum CONSTANTS { networkPage = 'network.page', overviewPage = 'overview.page', savedQuery = 'savedQuery', + sourcerer = 'sourcerer', timeline = 'timeline', timelinePage = 'timeline.page', timerange = 'timerange', diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 6052913b4183b..05000f91f094c 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -22,6 +22,8 @@ import { formatDate } from '../super_date_picker'; import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; import { ReplaceStateInLocation, UpdateUrlStateString } from './types'; +import { sourcererSelectors } from '../../store/sourcerer'; +import { SourcererScopeName, SourcererScopePatterns } from '../../store/sourcerer/model'; export const decodeRisonUrlState = (value: string | undefined): T | null => { try { @@ -58,16 +60,14 @@ export const replaceStateKeyInQueryString = (stateKey: string, urlState: T) = // ಠ_ಠ Code was copied from x-pack/legacy/plugins/infra/public/utils/url_state.tsx ಠ_ಠ // Remove this if these utilities are promoted to kibana core - const encodedUrlState = - typeof urlState !== 'undefined' ? encodeRisonUrlState(urlState) : undefined; - - return stringify( - url.encodeQuery({ - ...previousQueryValues, - [stateKey]: encodedUrlState, - }), - { sort: false, encode: false } - ); + const newValue = + typeof urlState === 'undefined' + ? previousQueryValues + : { + ...previousQueryValues, + [stateKey]: encodeRisonUrlState(urlState), + }; + return stringify(url.encodeQuery(newValue), { sort: false, encode: false }); }; export const replaceQueryStringInLocation = ( @@ -118,6 +118,7 @@ export const makeMapStateToProps = () => { const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector(); const getTimeline = timelineSelectors.getTimelineByIdSelector(); + const getSourcererScopes = sourcererSelectors.scopesSelector(); const mapStateToProps = (state: State) => { const inputState = getInputsSelector(state); const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; @@ -147,10 +148,16 @@ export const makeMapStateToProps = () => { [CONSTANTS.savedQuery]: savedQuery.id, }; } + const sourcerer = getSourcererScopes(state); + const activeScopes: SourcererScopeName[] = Object.keys(sourcerer) as SourcererScopeName[]; + const selectedPatterns: SourcererScopePatterns = activeScopes + .filter((scope) => scope === SourcererScopeName.default) + .reduce((acc, scope) => ({ ...acc, [scope]: sourcerer[scope]?.selectedPatterns }), {}); return { urlState: { ...searchAttr, + [CONSTANTS.sourcerer]: selectedPatterns, [CONSTANTS.timerange]: { global: { [CONSTANTS.timerange]: globalTimerange, @@ -217,6 +224,17 @@ export const updateUrlStateString = ({ urlStateKey: urlKey, }); } + } else if (urlKey === CONSTANTS.sourcerer) { + const sourcererState = decodeRisonUrlState(newUrlStateString); + if (sourcererState != null && Object.keys(sourcererState).length > 0) { + return replaceStateInLocation({ + history, + pathName, + search, + urlStateToReplace: sourcererState, + urlStateKey: urlKey, + }); + } } else if (urlKey === CONSTANTS.filters) { const queryState = decodeRisonUrlState(newUrlStateString); if (isEmpty(queryState)) { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx index 72df9d613abac..f4a48eaea69c2 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx @@ -21,8 +21,7 @@ import { } from './test_dependencies'; import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; let mockProps: UrlStateContainerPropTypes; @@ -161,7 +160,7 @@ describe('UrlStateContainer', () => { ).toEqual({ hash: '', pathname: examplePath, - search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, + search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))`, state: '', }); } diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx index 723f2d235864f..9e845ec538aa0 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx @@ -83,7 +83,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))", state: '', }); }); @@ -114,7 +114,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); }); @@ -147,7 +147,40 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))&timeline=(id:hello_timeline_id,isOpen:!t)", + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))&timeline=(id:hello_timeline_id,isOpen:!t)", + state: '', + }); + }); + + test('sourcerer redux state updates the url', () => { + mockProps = getMockPropsObj({ + page: CONSTANTS.networkPage, + examplePath: '/network', + namespaceLower: 'network', + pageName: SecurityPageName.network, + detailName: undefined, + }).noSearch.undefinedQuery; + + const wrapper = mount( + useUrlStateHooks(args)} /> + ); + const newUrlState = { + ...mockProps.urlState, + sourcerer: ['cool', 'patterns'], + }; + + wrapper.setProps({ + hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false }, + }); + wrapper.update(); + + expect( + mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] + ).toStrictEqual({ + hash: '', + pathname: '/network', + search: + "?sourcerer=!(cool,patterns)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); }); @@ -176,7 +209,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: examplePath, search: - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))", state: '', }); } @@ -204,7 +237,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" + "?sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" ); wrapper.setProps({ hookProps: updatedProps }); @@ -213,7 +246,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" + "?query=(language:kuery,query:'host.name:%22siem-es%22')&sourcerer=()&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2019-05-16T23:10:43.696Z',fromStr:now-24h,kind:relative,to:'2019-05-17T23:10:43.697Z',toStr:now)))" ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index 6eccf52ec72da..1e77ae7766630 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -8,7 +8,7 @@ import { get, isEmpty } from 'lodash/fp'; import { Dispatch } from 'redux'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; -import { inputsActions } from '../../store/actions'; +import { inputsActions, sourcererActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; import { UrlInputsModel, @@ -22,6 +22,8 @@ import { decodeRisonUrlState } from './helpers'; import { normalizeTimeRange } from './normalize_time_range'; import { DispatchSetInitialStateFromUrl, SetInitialStateFromUrl } from './types'; import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { SourcererScopeName, SourcererScopePatterns } from '../../store/sourcerer/model'; +import { SecurityPageName } from '../../../../common/constants'; export const dispatchSetInitialStateFromUrl = ( dispatch: Dispatch @@ -40,6 +42,22 @@ export const dispatchSetInitialStateFromUrl = ( if (urlKey === CONSTANTS.timerange) { updateTimerange(newUrlStateString, dispatch); } + if (urlKey === CONSTANTS.sourcerer) { + const sourcererState = decodeRisonUrlState(newUrlStateString); + if (sourcererState != null) { + const activeScopes: SourcererScopeName[] = Object.keys(sourcererState).filter( + (key) => !(key === SourcererScopeName.default && pageName === SecurityPageName.detections) + ) as SourcererScopeName[]; + activeScopes.forEach((scope) => + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scope, + selectedPatterns: sourcererState[scope] ?? [], + }) + ) + ); + } + } if (urlKey === CONSTANTS.appQuery && indexPattern != null) { const appQuery = decodeRisonUrlState(newUrlStateString); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts index 8d471e843320c..6f04226fa3a19 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts @@ -117,6 +117,7 @@ export const defaultProps: UrlStateContainerPropTypes = { id: '', isOpen: false, }, + [CONSTANTS.sourcerer]: {}, }, setInitialStateFromUrl: dispatchSetInitialStateFromUrl(mockDispatch), updateTimeline: (jest.fn() as unknown) as DispatchUpdateTimeline, diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts index f383e18132385..301771a4db6b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts @@ -22,11 +22,13 @@ import { DispatchUpdateTimeline } from '../../../timelines/components/open_timel import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; +import { SourcererScopePatterns } from '../../store/sourcerer/model'; export const ALL_URL_STATE_KEYS: KeyUrlState[] = [ CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ]; @@ -36,6 +38,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -43,6 +46,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -51,6 +55,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -58,6 +63,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -65,6 +71,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -72,6 +79,7 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, + CONSTANTS.sourcerer, CONSTANTS.timerange, CONSTANTS.timeline, ], @@ -93,6 +101,7 @@ export interface UrlState { [CONSTANTS.appQuery]?: Query; [CONSTANTS.filters]?: Filter[]; [CONSTANTS.savedQuery]?: string; + [CONSTANTS.sourcerer]: SourcererScopePatterns; [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: TimelineUrl; } diff --git a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx index f3136b0a40b3e..0908c887d25f6 100644 --- a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx @@ -28,6 +28,9 @@ const Wrapper = styled.div` &.siemWrapperPage--fullHeight { height: 100%; + display: flex; + flex-direction: column; + flex: 1 1 auto; } &.siemWrapperPage--withTimeline { @@ -36,6 +39,9 @@ const Wrapper = styled.div` &.siemWrapperPage--noPadding { padding: 0; + display: flex; + flex-direction: column; + flex: 1 1 auto; } `; diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx index f6ebbb990f223..489ccb23c9b2c 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx @@ -29,6 +29,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ AnomaliesTableComponent, flowTarget, ip, + indexNames, }) => { const { jobs } = useInstalledSecurityJobs(); const [anomalyScore] = useUiSetting$(DEFAULT_ANOMALY_SCORE); @@ -57,6 +58,7 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({ endDate={endDate} filterQuery={mergedFilterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...histogramConfigs} diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts index d716df70246f7..3ce4b8b6d4494 100644 --- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts +++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/types.ts @@ -24,6 +24,7 @@ export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & { deleteQuery?: ({ id }: { id: string }) => void; endDate: GlobalTimeArgs['to']; flowTarget?: FlowTarget; + indexNames: string[]; narrowDateRange: NarrowDateRange; setQuery: GlobalTimeArgs['setQuery']; startDate: GlobalTimeArgs['from']; diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index d70762615818b..dc2d6605bc292 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -8,7 +8,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; import { @@ -18,11 +17,15 @@ import { LastTimeDetails, LastEventIndexKey, } from '../../../../../common/search_strategy/timeline'; -import { AbortError } from '../../../../../../../../src/plugins/data/common'; -import { useWithSource } from '../../source'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../../src/plugins/data/common'; import * as i18n from './translations'; +import { DocValueFields } from '../../../../../common/search_strategy'; -// const ID = 'timelineEventsLastEventTimeQuery'; +const ID = 'timelineEventsLastEventTimeQuery'; export interface UseTimelineLastEventTimeArgs { lastSeen: string | null; @@ -31,26 +34,29 @@ export interface UseTimelineLastEventTimeArgs { } interface UseTimelineLastEventTimeProps { + docValueFields: DocValueFields[]; indexKey: LastEventIndexKey; + indexNames: string[]; details: LastTimeDetails; } export const useTimelineLastEventTime = ({ + docValueFields, indexKey, + indexNames, details, }: UseTimelineLastEventTimeProps): [boolean, UseTimelineLastEventTimeArgs] => { - const { data, notifications, uiSettings } = useKibana().services; - const { docValueFields } = useWithSource('default'); + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [TimelineLastEventTimeRequest, setTimelineLastEventTimeRequest] = useState< TimelineEventsLastEventTimeRequestOptions >({ - defaultIndex, - factoryQueryType: TimelineEventsQueries.lastEventTime, + defaultIndex: indexNames, docValueFields, + factoryQueryType: TimelineEventsQueries.lastEventTime, + id: ID, indexKey, details, }); @@ -80,7 +86,7 @@ export const useTimelineLastEventTime = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setTimelineLastEventTimeResponse((prevResponse) => ({ @@ -91,7 +97,7 @@ export const useTimelineLastEventTime = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -129,7 +135,8 @@ export const useTimelineLastEventTime = ({ setTimelineLastEventTimeRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, + docValueFields, indexKey, details, }; @@ -138,7 +145,7 @@ export const useTimelineLastEventTime = ({ } return prevRequest; }); - }, [defaultIndex, details, indexKey]); + }, [indexNames, details, docValueFields, indexKey]); useEffect(() => { timelineLastEventTimeSearch(TimelineLastEventTimeRequest); diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/last_event_time.gql_query.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/last_event_time.gql_query.ts deleted file mode 100644 index 36305ef0dc882..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/last_event_time.gql_query.ts +++ /dev/null @@ -1,29 +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 gql from 'graphql-tag'; - -export const LastEventTimeGqlQuery = gql` - query GetLastEventTimeQuery( - $sourceId: ID! - $indexKey: LastEventIndexKey! - $details: LastTimeDetails! - $defaultIndex: [String!]! - $docValueFields: [docValueFieldsInput!]! - ) { - source(id: $sourceId) { - id - LastEventTime( - indexKey: $indexKey - details: $details - defaultIndex: $defaultIndex - docValueFields: $docValueFields - ) { - lastSeen - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/mock.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/mock.ts index bdeb1db4e1b28..208c03b453e04 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/mock.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/mock.ts @@ -4,28 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; -import { GetLastEventTimeQuery, LastEventIndexKey } from '../../../../graphql/types'; - -import { LastEventTimeGqlQuery } from './last_event_time.gql_query'; - interface MockLastEventTimeQuery { - request: { - query: GetLastEventTimeQuery.Query; - variables: GetLastEventTimeQuery.Variables; - }; - result: { - data?: { - source: { - id: string; - LastEventTime: { - lastSeen: string | null; - errorMessage: string | null; - }; - }; - }; - errors?: [{ message: string }]; - }; + lastSeen: string | null; + errorMessage: string | null; } const getTimeTwelveMinutesAgo = () => { @@ -35,28 +16,7 @@ const getTimeTwelveMinutesAgo = () => { return new Date(twelveMinutes).toISOString(); }; -export const mockLastEventTimeQuery: MockLastEventTimeQuery[] = [ - { - request: { - query: LastEventTimeGqlQuery, - variables: { - sourceId: 'default', - indexKey: LastEventIndexKey.hosts, - details: {}, - defaultIndex: DEFAULT_INDEX_PATTERN, - docValueFields: [], - }, - }, - result: { - data: { - source: { - id: 'default', - LastEventTime: { - lastSeen: getTimeTwelveMinutesAgo(), - errorMessage: null, - }, - }, - }, - }, - }, -]; +export const mockLastEventTimeQuery: MockLastEventTimeQuery = { + lastSeen: getTimeTwelveMinutesAgo(), + errorMessage: null, +}; diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.gql_query.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.gql_query.ts deleted file mode 100644 index 6fb729ca7e9a0..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.gql_query.ts +++ /dev/null @@ -1,41 +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 gql from 'graphql-tag'; - -export const MatrixHistogramGqlQuery = gql` - query GetMatrixHistogramQuery( - $defaultIndex: [String!]! - $filterQuery: String - $histogramType: HistogramType! - $inspect: Boolean! - $sourceId: ID! - $stackByField: String! - $timerange: TimerangeInput! - ) { - source(id: $sourceId) { - id - MatrixHistogram( - timerange: $timerange - filterQuery: $filterQuery - defaultIndex: $defaultIndex - stackByField: $stackByField - histogramType: $histogramType - ) { - matrixHistogramData { - x - y - g - } - totalCount - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index 65ad3cc994c67..ca8bcc637717b 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -5,21 +5,24 @@ */ import deepEqual from 'fast-deep-equal'; -import { isEmpty, noop } from 'lodash/fp'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { noop } from 'lodash/fp'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; -import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; +import { useKibana } from '../../../common/lib/kibana'; import { MatrixHistogramQuery, MatrixHistogramRequestOptions, MatrixHistogramStrategyResponse, MatrixHistogramData, } from '../../../../common/search_strategy/security_solution'; -import { AbortError } from '../../../../../../../src/plugins/data/common'; +import { + AbortError, + isErrorResponse, + isCompleteResponse, +} from '../../../../../../../src/plugins/data/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import * as i18n from './translations'; @@ -36,25 +39,18 @@ export const useMatrixHistogram = ({ errorMessage, filterQuery, histogramType, - indexToAdd, + indexNames, stackByField, startDate, }: MatrixHistogramQueryProps): [boolean, UseMatrixHistogramArgs] => { const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY); - const defaultIndex = useMemo(() => { - if (indexToAdd != null && !isEmpty(indexToAdd)) { - return [...configIndex, ...indexToAdd]; - } - return configIndex; - }, [configIndex, indexToAdd]); const [loading, setLoading] = useState(false); const [matrixHistogramRequest, setMatrixHistogramRequest] = useState< MatrixHistogramRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, factoryQueryType: MatrixHistogramQuery, filterQuery: createFilter(filterQuery), histogramType, @@ -90,7 +86,7 @@ export const useMatrixHistogram = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setMatrixHistogramResponse((prevResponse) => ({ @@ -102,7 +98,7 @@ export const useMatrixHistogram = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -136,7 +132,7 @@ export const useMatrixHistogram = ({ setMatrixHistogramRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, filterQuery: createFilter(filterQuery), timerange: { interval: '12h', @@ -149,7 +145,7 @@ export const useMatrixHistogram = ({ } return prevRequest; }); - }, [defaultIndex, endDate, filterQuery, startDate]); + }, [indexNames, endDate, filterQuery, startDate]); useEffect(() => { hostsSearch(matrixHistogramRequest); diff --git a/x-pack/plugins/security_solution/public/common/containers/query_template.tsx b/x-pack/plugins/security_solution/public/common/containers/query_template.tsx index eaa43c255a944..80791d91481a8 100644 --- a/x-pack/plugins/security_solution/public/common/containers/query_template.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/query_template.tsx @@ -14,6 +14,7 @@ import { DocValueFields } from './source'; export { DocValueFields }; export interface QueryTemplateProps { + indexNames: string[]; docValueFields?: DocValueFields[]; id?: string; endDate?: string; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts b/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts deleted file mode 100644 index 630515c5cbed4..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.gql_query.ts +++ /dev/null @@ -1,31 +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 gql from 'graphql-tag'; - -export const sourceQuery = gql` - query SourceQuery($sourceId: ID = "default", $defaultIndex: [String!]!) { - source(id: $sourceId) { - id - status { - indicesExist(defaultIndex: $defaultIndex) - indexFields(defaultIndex: $defaultIndex) { - category - description - example - indexes - name - searchable - type - aggregatable - format - esTypes - subType - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx deleted file mode 100644 index 8ba7f7da7b8e3..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.test.tsx +++ /dev/null @@ -1,109 +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 { act, renderHook } from '@testing-library/react-hooks'; - -import { useWithSource, indicesExistOrDataTemporarilyUnavailable } from '.'; -import { NO_ALERT_INDEX } from '../../../../common/constants'; -import { mockBrowserFields, mockIndexFields, mocksSource } from './mock'; - -jest.mock('../../lib/kibana'); -jest.mock('../../utils/apollo_context', () => ({ - useApolloClient: jest.fn().mockReturnValue({ - query: jest.fn().mockImplementation(() => Promise.resolve(mocksSource[0].result)), - }), -})); - -describe('Index Fields & Browser Fields', () => { - test('At initialization the value of indicesExists should be true', async () => { - const { result, waitForNextUpdate } = renderHook(() => useWithSource()); - const initialResult = result.current; - - await waitForNextUpdate(); - - return expect(initialResult).toEqual({ - browserFields: {}, - docValueFields: [], - errorMessage: null, - indexPattern: { - fields: [], - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - indicesExist: true, - loading: true, - }); - }); - - test('returns memoized value', async () => { - const { result, waitForNextUpdate, rerender } = renderHook(() => useWithSource()); - await waitForNextUpdate(); - - const result1 = result.current; - act(() => rerender()); - const result2 = result.current; - - return expect(result1).toBe(result2); - }); - - test('Index Fields', async () => { - const { result, waitForNextUpdate } = renderHook(() => useWithSource()); - - await waitForNextUpdate(); - - return expect(result).toEqual({ - current: { - indicesExist: true, - browserFields: mockBrowserFields, - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, - ], - indexPattern: { - fields: mockIndexFields, - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - loading: false, - errorMessage: null, - }, - error: undefined, - }); - }); - - test('Make sure we are not querying for NO_ALERT_INDEX and it is not includes in the index pattern', async () => { - const { result, waitForNextUpdate } = renderHook(() => - useWithSource('default', [NO_ALERT_INDEX]) - ); - - await waitForNextUpdate(); - return expect(result.current.indexPattern.title).toEqual( - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*' - ); - }); - - describe('indicesExistOrDataTemporarilyUnavailable', () => { - test('it returns true when undefined', () => { - let undefVar; - const result = indicesExistOrDataTemporarilyUnavailable(undefVar); - expect(result).toBeTruthy(); - }); - test('it returns true when true', () => { - const result = indicesExistOrDataTemporarilyUnavailable(true); - expect(result).toBeTruthy(); - }); - test('it returns false when false', () => { - const result = indicesExistOrDataTemporarilyUnavailable(false); - expect(result).toBeFalsy(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx index ffbecf9e3d433..c36e2de61fcbf 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/source/index.tsx @@ -4,42 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isUndefined } from 'lodash'; import { set } from '@elastic/safer-lodash-set/fp'; -import { get, keyBy, pick, isEmpty } from 'lodash/fp'; -import { useEffect, useMemo, useState } from 'react'; +import { keyBy, pick, isEmpty, isEqual, isUndefined } from 'lodash/fp'; import memoizeOne from 'memoize-one'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useDispatch } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; -import { DEFAULT_INDEX_KEY, NO_ALERT_INDEX } from '../../../../common/constants'; -import { useUiSetting$ } from '../../lib/kibana'; +import { useKibana } from '../../lib/kibana'; +import { + IndexField, + IndexFieldsStrategyResponse, + IndexFieldsStrategyRequest, + BrowserField, + BrowserFields, +} from '../../../../common/search_strategy/index_fields'; +import { AbortError } from '../../../../../../../src/plugins/data/common'; +import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; +import * as i18n from './translations'; +import { SourcererScopeName } from '../../store/sourcerer/model'; +import { sourcererActions, sourcererSelectors } from '../../store/sourcerer'; +import { DocValueFields } from '../../../../common/search_strategy/common'; -import { IndexField, SourceQuery } from '../../../graphql/types'; - -import { sourceQuery } from './index.gql_query'; -import { useApolloClient } from '../../utils/apollo_context'; - -export { sourceQuery }; - -export interface BrowserField { - aggregatable: boolean; - category: string; - description: string | null; - example: string | number | null; - fields: Readonly>>; - format: string; - indexes: string[]; - name: string; - searchable: boolean; - type: string; -} - -export interface DocValueFields { - field: string; - format: string; -} - -export type BrowserFields = Readonly>>; +export { BrowserField, BrowserFields, DocValueFields }; export const getAllBrowserFields = (browserFields: BrowserFields): Array> => Object.values(browserFields).reduce>>( @@ -85,14 +72,12 @@ export const getDocValueFields = memoizeOne( (_title: string, fields: IndexField[]): DocValueFields[] => fields && fields.length > 0 ? fields.reduce((accumulator: DocValueFields[], field: IndexField) => { - if (field.type === 'date' && accumulator.length < 100) { - const format: string = - field.format != null && !isEmpty(field.format) ? field.format : 'date_time'; + if (field.readFromDocValues && accumulator.length < 100) { return [ ...accumulator, { field: field.name, - format, + format: field.format, }, ]; } @@ -107,115 +92,195 @@ export const indicesExistOrDataTemporarilyUnavailable = ( indicesExist: boolean | null | undefined ) => indicesExist || isUndefined(indicesExist); -const EMPTY_BROWSER_FIELDS = {}; -const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; +const DEFAULT_BROWSER_FIELDS = {}; +const DEFAULT_INDEX_PATTERNS = { fields: [], title: '' }; +const DEFAULT_DOC_VALUE_FIELDS: DocValueFields[] = []; -interface UseWithSourceState { +interface FetchIndexReturn { browserFields: BrowserFields; docValueFields: DocValueFields[]; - errorMessage: string | null; - indexPattern: IIndexPattern; - indicesExist: boolean | undefined | null; - loading: boolean; + indexes: string[]; + indexExists: boolean; + indexPatterns: IIndexPattern; } -export const useWithSource = ( - sourceId = 'default', - indexToAdd?: string[] | null, - onlyCheckIndexToAdd?: boolean, - // Fun fact: When using this hook multiple times within a component (e.g. add_exception_modal & edit_exception_modal), - // the apolloClient will perform queryDeduplication and prevent the first query from executing. A deep compare is not - // performed on `indices`, so another field must be passed to circumvent this. - // For details, see https://github.com/apollographql/react-apollo/issues/2202 - queryDeduplication = 'default' -) => { - const [configIndex] = useUiSetting$(DEFAULT_INDEX_KEY); - const defaultIndex = useMemo(() => { - const filterIndexAdd = (indexToAdd ?? []).filter((item) => item !== NO_ALERT_INDEX); - if (!isEmpty(filterIndexAdd)) { - return onlyCheckIndexToAdd ? filterIndexAdd : [...configIndex, ...filterIndexAdd]; - } - return configIndex; - }, [configIndex, indexToAdd, onlyCheckIndexToAdd]); - - const [state, setState] = useState({ - browserFields: EMPTY_BROWSER_FIELDS, - docValueFields: EMPTY_DOCVALUE_FIELD, - errorMessage: null, - indexPattern: getIndexFields(defaultIndex.join(), []), - indicesExist: indicesExistOrDataTemporarilyUnavailable(undefined), - loading: true, +export const useFetchIndex = ( + indexNames: string[], + onlyCheckIfIndicesExist: boolean = false +): [boolean, FetchIndexReturn] => { + const { data, notifications } = useKibana().services; + const abortCtrl = useRef(new AbortController()); + const previousIndexesName = useRef([]); + const [isLoading, setLoading] = useState(true); + + const [state, setState] = useState({ + browserFields: DEFAULT_BROWSER_FIELDS, + docValueFields: DEFAULT_DOC_VALUE_FIELDS, + indexes: indexNames, + indexExists: true, + indexPatterns: DEFAULT_INDEX_PATTERNS, }); - const apolloClient = useApolloClient(); + const indexFieldsSearch = useCallback( + (iNames) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + const searchSubscription$ = data.search + .search( + { indices: iNames, onlyCheckIfIndicesExist }, + { + abortSignal: abortCtrl.current.signal, + strategy: 'securitySolutionIndexFields', + } + ) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + const stringifyIndices = response.indicesExist.sort().join(); + previousIndexesName.current = response.indicesExist; + setLoading(false); + setState({ + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + indexes: response.indicesExist, + indexExists: response.indicesExist.length > 0, + indexPatterns: getIndexFields(stringifyIndices, response.indexFields), + }); + } + searchSubscription$.unsubscribe(); + } else if (!didCancel && response.isPartial && !response.isRunning) { + setLoading(false); + notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!didCancel) { + setLoading(false); + } - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - async function fetchSource() { - if (!apolloClient) return; - - setState((prevState) => ({ ...prevState, loading: true })); - - try { - const result = await apolloClient.query< - SourceQuery.Query, - SourceQuery.Variables & { queryDeduplication: string } - >({ - query: sourceQuery, - fetchPolicy: 'cache-first', - variables: { - sourceId, - defaultIndex, - queryDeduplication, - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + } }, - }, - }); - - if (isSubscribed) { - setState({ - loading: false, - indicesExist: indicesExistOrDataTemporarilyUnavailable( - get('data.source.status.indicesExist', result) - ), - browserFields: getBrowserFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - indexPattern: getIndexFields( - defaultIndex.join(), - get('data.source.status.indexFields', result) - ), - errorMessage: null, }); - } - } catch (error) { - if (isSubscribed) { - setState((prevState) => ({ - ...prevState, - loading: false, - errorMessage: error.message, - })); - } - } + }; + abortCtrl.current.abort(); + asyncSearch(); + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, notifications.toasts, onlyCheckIfIndicesExist] + ); + + useEffect(() => { + if (!isEmpty(indexNames) && !isEqual(previousIndexesName.current, indexNames)) { + indexFieldsSearch(indexNames); } + }, [indexNames, indexFieldsSearch, previousIndexesName]); + + return [isLoading, state]; +}; + +export const useIndexFields = (sourcererScopeName: SourcererScopeName) => { + const { data, notifications } = useKibana().services; + const abortCtrl = useRef(new AbortController()); + const dispatch = useDispatch(); + const previousIndexesName = useRef([]); + + const indexNamesSelectedSelector = useMemo( + () => sourcererSelectors.getIndexNamesSelectedSelector(), + [] + ); + const indexNames = useShallowEqualSelector((state) => + indexNamesSelectedSelector(state, sourcererScopeName) + ); - fetchSource(); + const setLoading = useCallback( + (loading: boolean) => { + dispatch(sourcererActions.setSourcererScopeLoading({ id: sourcererScopeName, loading })); + }, + [dispatch, sourcererScopeName] + ); + + const indexFieldsSearch = useCallback( + (indicesName) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + const searchSubscription$ = data.search + .search( + { indices: indicesName, onlyCheckIfIndicesExist: false }, + { + abortSignal: abortCtrl.current.signal, + strategy: 'securitySolutionIndexFields', + } + ) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + const stringifyIndices = response.indicesExist.sort().join(); + previousIndexesName.current = response.indicesExist; + dispatch( + sourcererActions.setSource({ + id: sourcererScopeName, + payload: { + browserFields: getBrowserFields(stringifyIndices, response.indexFields), + docValueFields: getDocValueFields(stringifyIndices, response.indexFields), + errorMessage: null, + id: sourcererScopeName, + indexPattern: getIndexFields(stringifyIndices, response.indexFields), + indicesExist: response.indicesExist.length > 0, + loading: false, + }, + }) + ); + } + searchSubscription$.unsubscribe(); + } else if (!didCancel && response.isPartial && !response.isRunning) { + // TODO: Make response error status clearer + setLoading(false); + notifications.toasts.addWarning(i18n.ERROR_BEAT_FIELDS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!didCancel) { + setLoading(false); + } - return () => { - isSubscribed = false; - return abortCtrl.abort(); - }; - }, [apolloClient, sourceId, defaultIndex, queryDeduplication]); + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + text: msg.message, + title: i18n.FAIL_BEAT_FIELDS, + }); + } + }, + }); + }; + abortCtrl.current.abort(); + asyncSearch(); + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, dispatch, notifications.toasts, setLoading, sourcererScopeName] + ); - return state; + useEffect(() => { + if (!isEmpty(indexNames) && !isEqual(previousIndexesName.current, indexNames)) { + indexFieldsSearch(indexNames); + } + }, [indexNames, indexFieldsSearch, previousIndexesName]); }; diff --git a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts index bba6a15d73970..7fcd11f71f081 100644 --- a/x-pack/plugins/security_solution/public/common/containers/source/mock.ts +++ b/x-pack/plugins/security_solution/public/common/containers/source/mock.ts @@ -5,347 +5,296 @@ */ import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; +import { DocValueFields } from '../../../../common/search_strategy'; +import { BrowserFields } from '../../../../common/search_strategy/index_fields'; -import { BrowserFields, DocValueFields } from '.'; -import { sourceQuery } from './index.gql_query'; - -export const mocksSource = [ - { - request: { - query: sourceQuery, - variables: { - sourceId: 'default', - defaultIndex: DEFAULT_INDEX_PATTERN, - }, +export const mocksSource = { + indexFields: [ + { + category: 'base', + description: + 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', + example: '2016-05-23T08:05:34.853Z', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: '@timestamp', + searchable: true, + type: 'date', + aggregatable: true, }, - result: { - data: { - source: { - id: 'default', - configuration: {}, - status: { - indicesExist: true, - winlogbeatIndices: [ - 'winlogbeat-7.0.0-2019.02.17', - 'winlogbeat-7.0.0-2019.02.18', - 'winlogbeat-7.0.0-2019.02.19', - 'winlogbeat-7.0.0-2019.02.20', - 'winlogbeat-7.0.0-2019.02.21', - 'winlogbeat-7.0.0-2019.02.21-000001', - 'winlogbeat-7.0.0-2019.02.22', - 'winlogbeat-8.0.0-2019.02.19-000001', - ], - auditbeatIndices: [ - 'auditbeat-7.0.0-2019.02.17', - 'auditbeat-7.0.0-2019.02.18', - 'auditbeat-7.0.0-2019.02.19', - 'auditbeat-7.0.0-2019.02.20', - 'auditbeat-7.0.0-2019.02.21', - 'auditbeat-7.0.0-2019.02.21-000001', - 'auditbeat-7.0.0-2019.02.22', - 'auditbeat-8.0.0-2019.02.19-000001', - ], - filebeatIndices: [ - 'filebeat-7.0.0-iot-2019.06', - 'filebeat-7.0.0-iot-2019.07', - 'filebeat-7.0.0-iot-2019.08', - 'filebeat-7.0.0-iot-2019.09', - 'filebeat-7.0.0-iot-2019.10', - 'filebeat-8.0.0-2019.02.19-000001', - ], - indexFields: [ - { - category: 'base', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: '@timestamp', - searchable: true, - type: 'date', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.ephemeral_id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: null, - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.hostname', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'agent', - description: - 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', - example: 'foo', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a0', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a1', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a2', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: - 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: 'Bytes sent from the client to the server.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - { - category: 'client', - description: 'Client domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'client', - description: 'Country ISO code.', - example: 'CA', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'cloud', - description: - 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: '666777888999', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.account.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'cloud', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Unique container id.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Name of the image the container was built on.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'container', - description: 'Container image tag.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.tag', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'destination', - description: - 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - category: 'destination', - description: 'Bytes sent from the destination to the source.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - { - category: 'destination', - description: 'Destination domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - { - aggregatable: true, - category: 'destination', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.ip', - searchable: true, - type: 'ip', - }, - { - aggregatable: true, - category: 'destination', - description: 'Port of the destination.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.port', - searchable: true, - type: 'long', - }, - { - aggregatable: true, - category: 'source', - description: - 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.ip', - searchable: true, - type: 'ip', - }, - { - aggregatable: true, - category: 'source', - description: 'Port of the source.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.port', - searchable: true, - type: 'long', - }, - { - aggregatable: true, - category: 'event', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: null, - format: '', - indexes: DEFAULT_INDEX_PATTERN, - name: 'event.end', - searchable: true, - type: 'date', - }, - ], - }, - }, - }, + { + category: 'agent', + description: + 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', + example: '8a4f500f', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.ephemeral_id', + searchable: true, + type: 'string', + aggregatable: true, }, - }, -]; + { + category: 'agent', + description: null, + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.hostname', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'agent', + description: + 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', + example: '8a4f500d', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'agent', + description: + 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', + example: 'foo', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'agent.name', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a0', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a1', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'auditd', + description: null, + example: null, + format: '', + indexes: ['auditbeat'], + name: 'auditd.data.a2', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: + 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.address', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: 'Bytes sent from the client to the server.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.bytes', + searchable: true, + type: 'number', + aggregatable: true, + }, + { + category: 'client', + description: 'Client domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.domain', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'client', + description: 'Country ISO code.', + example: 'CA', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'client.geo.country_iso_code', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'cloud', + description: + 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', + example: '666777888999', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.account.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'cloud', + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'cloud.availability_zone', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Unique container id.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.id', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Name of the image the container was built on.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.name', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'container', + description: 'Container image tag.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'container.image.tag', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'destination', + description: + 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.address', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + category: 'destination', + description: 'Bytes sent from the destination to the source.', + example: '184', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.bytes', + searchable: true, + type: 'number', + aggregatable: true, + }, + { + category: 'destination', + description: 'Destination domain.', + example: null, + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.domain', + searchable: true, + type: 'string', + aggregatable: true, + }, + { + aggregatable: true, + category: 'destination', + description: 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.ip', + searchable: true, + type: 'ip', + }, + { + aggregatable: true, + category: 'destination', + description: 'Port of the destination.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'destination.port', + searchable: true, + type: 'long', + }, + { + aggregatable: true, + category: 'source', + description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.ip', + searchable: true, + type: 'ip', + }, + { + aggregatable: true, + category: 'source', + description: 'Port of the source.', + example: '', + format: '', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + name: 'source.port', + searchable: true, + type: 'long', + }, + { + aggregatable: true, + category: 'event', + description: + 'event.end contains the date when the event ended or when the activity was last observed.', + example: null, + format: '', + indexes: DEFAULT_INDEX_PATTERN, + name: 'event.end', + searchable: true, + type: 'date', + }, + ], +}; export const mockIndexFields = [ { aggregatable: true, name: '@timestamp', searchable: true, type: 'date' }, diff --git a/x-pack/plugins/security_solution/public/common/containers/source/translations.ts b/x-pack/plugins/security_solution/public/common/containers/source/translations.ts new file mode 100644 index 0000000000000..f12a9a0b41a7b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/containers/source/translations.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ERROR_BEAT_FIELDS = i18n.translate( + 'xpack.securitySolution.beatFields.errorSearchDescription', + { + defaultMessage: `An error has occurred on getting beat fields`, + } +); + +export const FAIL_BEAT_FIELDS = i18n.translate( + 'xpack.securitySolution.beatFields.failSearchDescription', + { + defaultMessage: `Failed to run search on beat fields`, + } +); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts index 106294ba54f5a..be3d074811032 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/constants.ts @@ -4,26 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const SOURCERER_FEATURE_FLAG_ON = false; - -export enum SecurityPageName { - default = 'default', - host = 'host', - detections = 'detections', - timeline = 'timeline', - network = 'network', -} - -export type SourceGroupsType = keyof typeof SecurityPageName; - -export const sourceGroups = { - [SecurityPageName.default]: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'winlogbeat-*', - 'blobbeat-*', - ], -}; +export const SOURCERER_FEATURE_FLAG_ON = true; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx deleted file mode 100644 index b8017df09b738..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { indicesExistOrDataTemporarilyUnavailable } from './format'; - -describe('indicesExistOrDataTemporarilyUnavailable', () => { - it('it returns true when undefined', () => { - let undefVar; - const result = indicesExistOrDataTemporarilyUnavailable(undefVar); - expect(result).toBeTruthy(); - }); - it('it returns true when true', () => { - const result = indicesExistOrDataTemporarilyUnavailable(true); - expect(result).toBeTruthy(); - }); - it('it returns false when false', () => { - const result = indicesExistOrDataTemporarilyUnavailable(false); - expect(result).toBeFalsy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts deleted file mode 100644 index 8c9a16ed705ef..0000000000000 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/format.ts +++ /dev/null @@ -1,96 +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 { isEmpty, pick } from 'lodash/fp'; -import memoizeOne from 'memoize-one'; -import { set } from '@elastic/safer-lodash-set/fp'; -import { isUndefined } from 'lodash'; -import { IndexField } from '../../../graphql/types'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; - -export interface BrowserField { - aggregatable: boolean; - category: string; - description: string | null; - example: string | number | null; - fields: Readonly>>; - format: string; - indexes: string[]; - name: string; - searchable: boolean; - type: string; -} - -export interface DocValueFields { - field: string; - format: string; -} - -export type BrowserFields = Readonly>>; - -export const getAllBrowserFields = (browserFields: BrowserFields): Array> => - Object.values(browserFields).reduce>>( - (acc, namespace) => [ - ...acc, - ...Object.values(namespace.fields != null ? namespace.fields : {}), - ], - [] - ); - -export const getIndexFields = memoizeOne( - (title: string, fields: IndexField[]): IIndexPattern => - fields && fields.length > 0 - ? { - fields: fields.map((field) => - pick(['name', 'searchable', 'type', 'aggregatable', 'esTypes', 'subType'], field) - ), - title, - } - : { fields: [], title }, - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] && newArgs[1].length === lastArgs[1].length -); - -export const getBrowserFields = memoizeOne( - (_title: string, fields: IndexField[]): BrowserFields => - fields && fields.length > 0 - ? fields.reduce( - (accumulator: BrowserFields, field: IndexField) => - set([field.category, 'fields', field.name], field, accumulator), - {} - ) - : {}, - // Update the value only if _title has changed - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] -); - -export const getDocValueFields = memoizeOne( - (_title: string, fields: IndexField[]): DocValueFields[] => - fields && fields.length > 0 - ? fields.reduce((accumulator: DocValueFields[], field: IndexField) => { - if (field.type === 'date' && accumulator.length < 100) { - const format: string = - field.format != null && !isEmpty(field.format) ? field.format : 'date_time'; - return [ - ...accumulator, - { - field: field.name, - format, - }, - ]; - } - return accumulator; - }, []) - : [], - // Update the value only if _title has changed - (newArgs, lastArgs) => newArgs[0] === lastArgs[0] -); - -export const indicesExistOrDataTemporarilyUnavailable = ( - indicesExist: boolean | null | undefined -) => indicesExist || isUndefined(indicesExist); - -export const EMPTY_BROWSER_FIELDS = {}; -export const EMPTY_DOCVALUE_FIELD: DocValueFields[] = []; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx index 38af84e0968f8..673db7af2b5e6 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.test.tsx @@ -4,28 +4,73 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable react/display-name */ + +import React from 'react'; import { act, renderHook } from '@testing-library/react-hooks'; +import { Provider } from 'react-redux'; -import { getSourceDefaults, useSourceManager, UseSourceManager } from '.'; +import { useInitSourcerer } from '.'; +import { mockPatterns, mockSource } from './mocks'; +// import { SourcererScopeName } from '../../store/sourcerer/model'; +import { RouteSpyState } from '../../utils/route/types'; +import { SecurityPageName } from '../../../../common/constants'; +import { createStore, State } from '../../store'; import { - mockSourceSelections, - mockSourceGroup, - mockSourceGroups, - mockPatterns, - mockSource, -} from './mocks'; -import { SecurityPageName } from './constants'; -const mockSourceDefaults = mockSource(SecurityPageName.default); + apolloClientObservable, + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, +} from '../../mock'; +const mockSourceDefaults = mockSource; + +const mockRouteSpy: RouteSpyState = { + pageName: SecurityPageName.overview, + detailName: undefined, + tabName: undefined, + search: '', + pathName: '/', +}; +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); +jest.mock('../../utils/route/use_route_spy', () => ({ + useRouteSpy: () => [mockRouteSpy], +})); jest.mock('../../lib/kibana', () => ({ useKibana: jest.fn().mockReturnValue({ services: { + application: { + capabilities: { + siem: { + crud: true, + }, + }, + }, data: { indexPatterns: { getTitles: jest.fn().mockImplementation(() => Promise.resolve(mockPatterns)), }, + search: { + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, }, + notifications: {}, }, }), + useUiSetting$: jest.fn().mockImplementation(() => [mockPatterns]), })); jest.mock('../../utils/apollo_context', () => ({ useApolloClient: jest.fn().mockReturnValue({ @@ -34,148 +79,193 @@ jest.mock('../../utils/apollo_context', () => ({ })); describe('Sourcerer Hooks', () => { - const testId = SecurityPageName.default; - const uninitializedId = SecurityPageName.host; + // const testId = SourcererScopeName.default; + // const uninitializedId = SourcererScopeName.detections; beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); + const state: State = mockGlobalState; + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + + beforeEach(() => { + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + kibanaObservable, + storage + ); + }); describe('Initialization', () => { - it('initializes loading default index patterns', async () => { + it('initializes loading default and timeline index patterns', async () => { await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: 'default', - availableIndexPatterns: [], - availableSourceGroupIds: [], - isIndexPatternsLoading: true, - sourceGroups: {}, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + const { waitForNextUpdate } = renderHook(() => useInitSourcerer(), { + wrapper: ({ children }) => {children}, }); - }); - }); - it('initializes loading default source group', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); await waitForNextUpdate(); await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: 'default', - availableIndexPatterns: mockPatterns, - availableSourceGroupIds: [], - isIndexPatternsLoading: false, - sourceGroups: {}, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + expect(mockDispatch).toBeCalledTimes(2); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + type: 'x-pack/security_solution/local/sourcerer/SET_SOURCERER_SCOPE_LOADING', + payload: { id: 'default', loading: true }, }); - }); - }); - it('initialize completes with formatted source group data', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - activeSourceGroupId: testId, - availableIndexPatterns: mockPatterns, - availableSourceGroupIds: [testId], - isIndexPatternsLoading: false, - sourceGroups: { - default: mockSourceGroup(testId), - }, - getManageSourceGroupById: result.current.getManageSourceGroupById, - initializeSourceGroup: result.current.initializeSourceGroup, - setActiveSourceGroupId: result.current.setActiveSourceGroupId, - updateSourceGroupIndicies: result.current.updateSourceGroupIndicies, + expect(mockDispatch.mock.calls[1][0]).toEqual({ + type: 'x-pack/security_solution/local/sourcerer/SET_SOURCERER_SCOPE_LOADING', + payload: { id: 'timeline', loading: true }, }); + // expect(mockDispatch.mock.calls[1][0]).toEqual({ + // type: 'x-pack/security_solution/local/sourcerer/SET_INDEX_PATTERNS_LIST', + // payload: { allIndexPatterns: mockPatterns, kibanaIndexPatterns: [] }, + // }); }); }); + // TO DO sourcerer @S + // it('initializes loading default source group', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // expect(result.current).toEqual({ + // activeSourcererScopeId: 'default', + // kibanaIndexPatterns: mockPatterns, + // isIndexPatternsLoading: false, + // getSourcererScopeById: result.current.getSourcererScopeById, + // setActiveSourcererScopeId: result.current.setActiveSourcererScopeId, + // updateSourcererScopeIndices: result.current.updateSourcererScopeIndices, + // }); + // }); + // }); + // it('initialize completes with formatted source group data', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // expect(result.current).toEqual({ + // activeSourcererScopeId: testId, + // kibanaIndexPatterns: mockPatterns, + // isIndexPatternsLoading: false, + // getSourcererScopeById: result.current.getSourcererScopeById, + // setActiveSourcererScopeId: result.current.setActiveSourcererScopeId, + // updateSourcererScopeIndices: result.current.updateSourcererScopeIndices, + // }); + // }); + // }); }); - describe('Methods', () => { - it('getManageSourceGroupById: initialized source group returns defaults', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - const initializedSourceGroup = result.current.getManageSourceGroupById(testId); - expect(initializedSourceGroup).toEqual(mockSourceGroup(testId)); - }); - }); - it('getManageSourceGroupById: uninitialized source group returns defaults', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - const uninitializedSourceGroup = result.current.getManageSourceGroupById(uninitializedId); - expect(uninitializedSourceGroup).toEqual(getSourceDefaults(uninitializedId, mockPatterns)); - }); - }); - it('initializeSourceGroup: initializes source group', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - result.current.initializeSourceGroup( - uninitializedId, - mockSourceGroups[uninitializedId], - true - ); - await waitForNextUpdate(); - const initializedSourceGroup = result.current.getManageSourceGroupById(uninitializedId); - expect(initializedSourceGroup.indexPatterns).toEqual(mockSourceSelections[uninitializedId]); - }); - }); - it('setActiveSourceGroupId: active source group id gets set only if it gets initialized first', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - expect(result.current.activeSourceGroupId).toEqual(testId); - result.current.setActiveSourceGroupId(uninitializedId); - expect(result.current.activeSourceGroupId).toEqual(testId); - result.current.initializeSourceGroup(uninitializedId); - result.current.setActiveSourceGroupId(uninitializedId); - expect(result.current.activeSourceGroupId).toEqual(uninitializedId); - }); - }); - it('updateSourceGroupIndicies: updates source group indicies', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useSourceManager() - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - await waitForNextUpdate(); - let sourceGroup = result.current.getManageSourceGroupById(testId); - expect(sourceGroup.indexPatterns).toEqual(mockSourceSelections[testId]); - result.current.updateSourceGroupIndicies(testId, ['endgame-*', 'filebeat-*']); - await waitForNextUpdate(); - sourceGroup = result.current.getManageSourceGroupById(testId); - expect(sourceGroup.indexPatterns).toEqual(['endgame-*', 'filebeat-*']); - }); - }); - }); + // describe('Methods', () => { + // it('getSourcererScopeById: initialized source group returns defaults', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // const initializedSourcererScope = result.current.getSourcererScopeById(testId); + // expect(initializedSourcererScope).toEqual(mockSourcererScope(testId)); + // }); + // }); + // it('getSourcererScopeById: uninitialized source group returns defaults', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // const uninitializedSourcererScope = result.current.getSourcererScopeById(uninitializedId); + // expect(uninitializedSourcererScope).toEqual( + // getSourceDefaults(uninitializedId, mockPatterns) + // ); + // }); + // }); + // // it('initializeSourcererScope: initializes source group', async () => { + // // await act(async () => { + // // const { result, waitForNextUpdate } = renderHook( + // // () => useSourcerer(), + // // { + // // wrapper: ({ children }) => {children}, + // // } + // // ); + // // await waitForNextUpdate(); + // // await waitForNextUpdate(); + // // await waitForNextUpdate(); + // // result.current.initializeSourcererScope( + // // uninitializedId, + // // mockSourcererScopes[uninitializedId], + // // true + // // ); + // // await waitForNextUpdate(); + // // const initializedSourcererScope = result.current.getSourcererScopeById(uninitializedId); + // // expect(initializedSourcererScope.selectedPatterns).toEqual( + // // mockSourcererScopes[uninitializedId] + // // ); + // // }); + // // }); + // it('setActiveSourcererScopeId: active source group id gets set only if it gets initialized first', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // expect(result.current.activeSourcererScopeId).toEqual(testId); + // result.current.setActiveSourcererScopeId(uninitializedId); + // expect(result.current.activeSourcererScopeId).toEqual(testId); + // // result.current.initializeSourcererScope(uninitializedId); + // result.current.setActiveSourcererScopeId(uninitializedId); + // expect(result.current.activeSourcererScopeId).toEqual(uninitializedId); + // }); + // }); + // it('updateSourcererScopeIndices: updates source group indices', async () => { + // await act(async () => { + // const { result, waitForNextUpdate } = renderHook( + // () => useInitSourcerer(), + // { + // wrapper: ({ children }) => {children}, + // } + // ); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // await waitForNextUpdate(); + // let sourceGroup = result.current.getSourcererScopeById(testId); + // expect(sourceGroup.selectedPatterns).toEqual(mockSourcererScopes[testId]); + // expect(sourceGroup.scopePatterns).toEqual(mockSourcererScopes[testId]); + // result.current.updateSourcererScopeIndices({ + // id: testId, + // selectedPatterns: ['endgame-*', 'filebeat-*'], + // }); + // await waitForNextUpdate(); + // sourceGroup = result.current.getSourcererScopeById(testId); + // expect(sourceGroup.scopePatterns).toEqual(mockSourcererScopes[testId]); + // expect(sourceGroup.selectedPatterns).toEqual(['endgame-*', 'filebeat-*']); + // }); + // }); + // }); }); diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index 91907b45aa449..b02a09625ccf3 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -4,412 +4,74 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, noop, isEmpty } from 'lodash/fp'; -import React, { createContext, useCallback, useContext, useEffect, useReducer } from 'react'; -import { IIndexPattern } from 'src/plugins/data/public'; - -import { NO_ALERT_INDEX } from '../../../../common/constants'; -import { useKibana } from '../../lib/kibana'; - -import { SourceQuery } from '../../../graphql/types'; - -import { sourceQuery } from '../source/index.gql_query'; -import { useApolloClient } from '../../utils/apollo_context'; -import { - sourceGroups, - SecurityPageName, - SourceGroupsType, - SOURCERER_FEATURE_FLAG_ON, -} from './constants'; -import { - BrowserFields, - DocValueFields, - EMPTY_BROWSER_FIELDS, - EMPTY_DOCVALUE_FIELD, - getBrowserFields, - getDocValueFields, - getIndexFields, - indicesExistOrDataTemporarilyUnavailable, -} from './format'; - -// TYPES -interface ManageSource { - browserFields: BrowserFields; - defaultPatterns: string[]; - docValueFields: DocValueFields[]; - errorMessage: string | null; - id: SourceGroupsType; - indexPattern: IIndexPattern; - indexPatterns: string[]; - indicesExist: boolean | undefined | null; - loading: boolean; -} - -interface ManageSourceInit extends Partial { - id: SourceGroupsType; -} - -type ManageSourceGroupById = { - [id in SourceGroupsType]?: ManageSource; -}; - -type ActionManageSource = - | { - type: 'SET_SOURCE'; - id: SourceGroupsType; - defaultIndex: string[]; - payload: ManageSourceInit; - } - | { - type: 'SET_IS_SOURCE_LOADING'; - id: SourceGroupsType; - payload: boolean; - } - | { - type: 'SET_ACTIVE_SOURCE_GROUP_ID'; - payload: SourceGroupsType; - } - | { - type: 'SET_AVAILABLE_INDEX_PATTERNS'; - payload: string[]; - } - | { - type: 'SET_IS_INDEX_PATTERNS_LOADING'; - payload: boolean; - }; - -interface ManageSourcerer { - activeSourceGroupId: SourceGroupsType; - availableIndexPatterns: string[]; - availableSourceGroupIds: SourceGroupsType[]; - isIndexPatternsLoading: boolean; - sourceGroups: ManageSourceGroupById; -} - -export interface UseSourceManager extends ManageSourcerer { - getManageSourceGroupById: (id: SourceGroupsType) => ManageSource; - initializeSourceGroup: ( - id: SourceGroupsType, - indexToAdd?: string[] | null, - onlyCheckIndexToAdd?: boolean - ) => void; - setActiveSourceGroupId: (id: SourceGroupsType) => void; - updateSourceGroupIndicies: (id: SourceGroupsType, updatedIndicies: string[]) => void; -} - -// DEFAULTS/INIT -export const getSourceDefaults = (id: SourceGroupsType, defaultIndex: string[]) => ({ - browserFields: EMPTY_BROWSER_FIELDS, - defaultPatterns: defaultIndex, - docValueFields: EMPTY_DOCVALUE_FIELD, - errorMessage: null, - id, - indexPattern: getIndexFields(defaultIndex.join(), []), - indexPatterns: defaultIndex, - indicesExist: indicesExistOrDataTemporarilyUnavailable(undefined), - loading: true, -}); - -const initManageSource: ManageSourcerer = { - activeSourceGroupId: SecurityPageName.default, - availableIndexPatterns: [], - availableSourceGroupIds: [], - isIndexPatternsLoading: true, - sourceGroups: {}, -}; -const init: UseSourceManager = { - ...initManageSource, - getManageSourceGroupById: (id: SourceGroupsType) => getSourceDefaults(id, []), - initializeSourceGroup: () => noop, - setActiveSourceGroupId: () => noop, - updateSourceGroupIndicies: () => noop, -}; - -const reducerManageSource = (state: ManageSourcerer, action: ActionManageSource) => { - switch (action.type) { - case 'SET_SOURCE': - return { - ...state, - sourceGroups: { - ...state.sourceGroups, - [action.id]: { - ...getSourceDefaults(action.id, action.defaultIndex), - ...state.sourceGroups[action.id], - ...action.payload, - }, - }, - availableSourceGroupIds: state.availableSourceGroupIds.includes(action.id) - ? state.availableSourceGroupIds - : [...state.availableSourceGroupIds, action.id], - }; - case 'SET_IS_SOURCE_LOADING': - return { - ...state, - sourceGroups: { - ...state.sourceGroups, - [action.id]: { - ...state.sourceGroups[action.id], - id: action.id, - loading: action.payload, - }, - }, - }; - case 'SET_ACTIVE_SOURCE_GROUP_ID': - return { - ...state, - activeSourceGroupId: action.payload, - }; - case 'SET_AVAILABLE_INDEX_PATTERNS': - return { - ...state, - availableIndexPatterns: action.payload, - }; - case 'SET_IS_INDEX_PATTERNS_LOADING': - return { - ...state, - isIndexPatternsLoading: action.payload, - }; - default: - return state; - } -}; - -// HOOKS -export const useSourceManager = (): UseSourceManager => { - const { - services: { - data: { indexPatterns }, - }, - } = useKibana(); - const apolloClient = useApolloClient(); - const [state, dispatch] = useReducer(reducerManageSource, initManageSource); - - // Kibana Index Patterns - const setIsIndexPatternsLoading = useCallback((loading: boolean) => { - dispatch({ - type: 'SET_IS_INDEX_PATTERNS_LOADING', - payload: loading, - }); - }, []); - const getDefaultIndex = useCallback( - (indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => { - const filterIndexAdd = (indexToAdd ?? []).filter((item) => item !== NO_ALERT_INDEX); - if (!isEmpty(filterIndexAdd)) { - return onlyCheckIndexToAdd - ? filterIndexAdd.sort() - : [ - ...state.availableIndexPatterns, - ...filterIndexAdd.filter((index) => !state.availableIndexPatterns.includes(index)), - ].sort(); - } - return state.availableIndexPatterns.sort(); - }, - [state.availableIndexPatterns] - ); - const setAvailableIndexPatterns = useCallback((availableIndexPatterns: string[]) => { - dispatch({ - type: 'SET_AVAILABLE_INDEX_PATTERNS', - payload: availableIndexPatterns, - }); - }, []); - const fetchKibanaIndexPatterns = useCallback(() => { - setIsIndexPatternsLoading(true); - const abortCtrl = new AbortController(); - - async function fetchTitles() { - try { - const result = await indexPatterns.getTitles(); - setAvailableIndexPatterns(result); - setIsIndexPatternsLoading(false); - } catch (error) { - setIsIndexPatternsLoading(false); - } - } - - fetchTitles(); - - return () => { - return abortCtrl.abort(); - }; - }, [indexPatterns, setAvailableIndexPatterns, setIsIndexPatternsLoading]); - - // Security Solution Source Groups - const setActiveSourceGroupId = useCallback( - (sourceGroupId: SourceGroupsType) => { - if (state.availableSourceGroupIds.includes(sourceGroupId)) { - dispatch({ - type: 'SET_ACTIVE_SOURCE_GROUP_ID', - payload: sourceGroupId, - }); - } - }, - [state.availableSourceGroupIds] - ); - const setIsSourceLoading = useCallback( - ({ id, loading }: { id: SourceGroupsType; loading: boolean }) => { - dispatch({ - type: 'SET_IS_SOURCE_LOADING', - id, - payload: loading, - }); - }, +import deepEqual from 'fast-deep-equal'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports +import isEqual from 'lodash/isEqual'; +import { useEffect, useMemo } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; + +import { sourcererActions, sourcererSelectors } from '../../store/sourcerer'; +import { ManageScope, SourcererScopeName } from '../../store/sourcerer/model'; +import { useIndexFields } from '../source'; +import { State } from '../../store'; +import { useUserInfo } from '../../../detections/components/user_info'; + +export const useInitSourcerer = ( + scopeId: SourcererScopeName.default | SourcererScopeName.detections = SourcererScopeName.default +) => { + const dispatch = useDispatch(); + + const { loading: loadingSignalIndex, isSignalIndexExists, signalIndexName } = useUserInfo(); + const getConfigIndexPatternsSelector = useMemo( + () => sourcererSelectors.configIndexPatternsSelector(), [] ); - const enrichSource = useCallback( - (id: SourceGroupsType, indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - const defaultIndex = getDefaultIndex(indexToAdd, onlyCheckIndexToAdd); - const selectedPatterns = defaultIndex.filter((pattern) => - state.availableIndexPatterns.includes(pattern) - ); - if (state.sourceGroups[id] == null) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { defaultPatterns: defaultIndex, id }, - }); - } - - async function fetchSource() { - if (!apolloClient) return; - setIsSourceLoading({ id, loading: true }); - try { - const result = await apolloClient.query({ - query: sourceQuery, - fetchPolicy: 'network-only', - variables: { - sourceId: 'default', // always - defaultIndex: selectedPatterns, - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, - }, - }, - }); - if (isSubscribed) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { - browserFields: getBrowserFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - errorMessage: null, - id, - indexPattern: getIndexFields( - selectedPatterns.join(), - get('data.source.status.indexFields', result) - ), - indexPatterns: selectedPatterns, - indicesExist: indicesExistOrDataTemporarilyUnavailable( - get('data.source.status.indicesExist', result) - ), - loading: false, - }, - }); - } - } catch (error) { - if (isSubscribed) { - dispatch({ - type: 'SET_SOURCE', - id, - defaultIndex: selectedPatterns, - payload: { - errorMessage: error.message, - id, - loading: false, - }, - }); - } - } - } - - fetchSource(); - - return () => { - isSubscribed = false; - return abortCtrl.abort(); - }; - }, - [ - apolloClient, - getDefaultIndex, - setIsSourceLoading, - state.availableIndexPatterns, - state.sourceGroups, - ] - ); + const ConfigIndexPatterns = useSelector(getConfigIndexPatternsSelector, isEqual); - const initializeSourceGroup = useCallback( - (id: SourceGroupsType, indexToAdd?: string[] | null, onlyCheckIndexToAdd?: boolean) => - enrichSource(id, indexToAdd, onlyCheckIndexToAdd), - [enrichSource] - ); - - const updateSourceGroupIndicies = useCallback( - (id: SourceGroupsType, updatedIndicies: string[]) => enrichSource(id, updatedIndicies, true), - [enrichSource] - ); - const getManageSourceGroupById = useCallback( - (id: SourceGroupsType) => { - const sourceById = state.sourceGroups[id]; - if (sourceById != null) { - return sourceById; - } - return getSourceDefaults(id, getDefaultIndex()); - }, - [getDefaultIndex, state.sourceGroups] - ); + useIndexFields(scopeId); + useIndexFields(SourcererScopeName.timeline); - // load initial default index useEffect(() => { - fetchKibanaIndexPatterns(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + if (!loadingSignalIndex && signalIndexName != null) { + dispatch(sourcererActions.setSignalIndexName({ signalIndexName })); + } + }, [dispatch, loadingSignalIndex, signalIndexName]); + // Related to timeline useEffect(() => { - if (!state.isIndexPatternsLoading) { - Object.entries(sourceGroups).forEach(([key, value]) => - initializeSourceGroup(key as SourceGroupsType, value, true) + if (!loadingSignalIndex && signalIndexName != null) { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: SourcererScopeName.timeline, + selectedPatterns: [...ConfigIndexPatterns, signalIndexName], + }) ); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [state.isIndexPatternsLoading]); + }, [ConfigIndexPatterns, dispatch, loadingSignalIndex, signalIndexName]); - return { - ...state, - getManageSourceGroupById, - initializeSourceGroup, - setActiveSourceGroupId, - updateSourceGroupIndicies, - }; + // Related to the detection page + useEffect(() => { + if ( + scopeId === SourcererScopeName.detections && + isSignalIndexExists && + signalIndexName != null + ) { + dispatch( + sourcererActions.setSelectedIndexPatterns({ + id: scopeId, + selectedPatterns: [signalIndexName], + }) + ); + } + }, [dispatch, isSignalIndexExists, scopeId, signalIndexName]); }; -const ManageSourceContext = createContext(init); - -export const useManageSource = () => useContext(ManageSourceContext); - -interface ManageSourceProps { - children: React.ReactNode; -} - -export const MaybeManageSource = ({ children }: ManageSourceProps) => { - const indexPatternManager = useSourceManager(); - return ( - - {children} - +export const useSourcererScope = (scope: SourcererScopeName = SourcererScopeName.default) => { + const sourcererScopeSelector = useMemo(() => sourcererSelectors.getSourcererScopeSelector(), []); + const SourcererScope = useSelector( + (state) => sourcererScopeSelector(state, scope), + deepEqual ); + return SourcererScope; }; -export const ManageSource = SOURCERER_FEATURE_FLAG_ON - ? MaybeManageSource - : ({ children }: ManageSourceProps) => <>{children}; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts b/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts index cde14e54694f0..c34a6917f300e 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/mocks.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SecurityPageName } from './constants'; -import { getSourceDefaults } from './index'; +import { initSourcererScope } from '../../store/sourcerer/model'; export const mockPatterns = [ 'auditbeat-*', @@ -14,32 +13,10 @@ export const mockPatterns = [ 'logs-*', 'packetbeat-*', 'winlogbeat-*', + 'journalbeat-*', ]; -export const mockSourceGroups = { - [SecurityPageName.default]: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'blobbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'winlogbeat-*', - ], - [SecurityPageName.host]: [ - 'apm-*-transaction*', - 'endgame-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], -}; - -export const mockSourceSelections = { - [SecurityPageName.default]: ['auditbeat-*', 'endgame-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], - [SecurityPageName.host]: ['endgame-*', 'logs-*', 'packetbeat-*', 'winlogbeat-*'], -}; -export const mockSource = (testId: SecurityPageName.default | SecurityPageName.host) => ({ +export const mockSource = { data: { source: { id: 'default', @@ -50,7 +27,7 @@ export const mockSource = (testId: SecurityPageName.default | SecurityPageName.h category: '_id', description: 'Each document has an _id that uniquely identifies it', example: 'Y-6TfmcB0WOhS6qyMv3s', - indexes: mockSourceSelections[testId], + indexes: mockPatterns, name: '_id', searchable: true, type: 'string', @@ -67,48 +44,45 @@ export const mockSource = (testId: SecurityPageName.default | SecurityPageName.h loading: false, networkStatus: 7, stale: false, -}); +}; -export const mockSourceGroup = (testId: SecurityPageName.default | SecurityPageName.host) => { - const indexes = mockSourceSelections[testId]; - return { - ...getSourceDefaults(testId, mockPatterns), - defaultPatterns: mockSourceGroups[testId], - browserFields: { - _id: { - fields: { - _id: { - __typename: 'IndexField', - aggregatable: false, - category: '_id', - description: 'Each document has an _id that uniquely identifies it', - esTypes: null, - example: 'Y-6TfmcB0WOhS6qyMv3s', - format: null, - indexes, - name: '_id', - searchable: true, - subType: null, - type: 'string', - }, - }, - }, - }, - indexPattern: { - fields: [ - { +export const mockSourcererScope = { + ...initSourcererScope, + scopePatterns: mockPatterns, + browserFields: { + _id: { + fields: { + _id: { + __typename: 'IndexField', aggregatable: false, + category: '_id', + description: 'Each document has an _id that uniquely identifies it', esTypes: null, + example: 'Y-6TfmcB0WOhS6qyMv3s', + format: null, + indexes: mockPatterns, name: '_id', searchable: true, subType: null, type: 'string', }, - ], - title: indexes.join(), + }, }, - indexPatterns: indexes, - indicesExist: true, - loading: false, - }; + }, + indexPattern: { + fields: [ + { + aggregatable: false, + esTypes: null, + name: '_id', + searchable: true, + subType: null, + type: 'string', + }, + ], + title: mockPatterns.join(), + }, + selectedPatterns: mockPatterns, + indicesExist: true, + loading: false, }; diff --git a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx index 32591fb032436..8357a9d22739e 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_full_screen/index.tsx @@ -5,9 +5,10 @@ */ import { useCallback, useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { SCROLLING_DISABLED_CLASS_NAME } from '../../../../common/constants'; +import { useDispatch } from 'react-redux'; +import { SCROLLING_DISABLED_CLASS_NAME } from '../../../../common/constants'; +import { useShallowEqualSelector } from '../../hooks/use_selector'; import { inputsSelectors } from '../../store'; import { inputsActions } from '../../store/actions'; @@ -29,8 +30,10 @@ export const resetScroll = () => { export const useFullScreen = () => { const dispatch = useDispatch(); - const globalFullScreen = useSelector(inputsSelectors.globalFullScreenSelector) ?? false; - const timelineFullScreen = useSelector(inputsSelectors.timelineFullScreenSelector) ?? false; + const globalFullScreen = + useShallowEqualSelector(inputsSelectors.globalFullScreenSelector) ?? false; + const timelineFullScreen = + useShallowEqualSelector(inputsSelectors.timelineFullScreenSelector) ?? false; const setGlobalFullScreen = useCallback( (fullScreen: boolean) => { diff --git a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx index 52825caf9ce74..e6c47c697c0b2 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_global_time/index.tsx @@ -5,15 +5,16 @@ */ import { useCallback, useState, useEffect, useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; +import { useShallowEqualSelector } from '../../hooks/use_selector'; import { inputsSelectors } from '../../store'; import { inputsActions } from '../../store/actions'; import { SetQuery, DeleteQuery } from './types'; export const useGlobalTime = (clearAllQuery: boolean = true) => { const dispatch = useDispatch(); - const { from, to } = useSelector(inputsSelectors.globalTimeRangeSelector); + const { from, to } = useShallowEqualSelector(inputsSelectors.globalTimeRangeSelector); const [isInitializing, setIsInitializing] = useState(true); const setQuery = useCallback( diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/upgrade.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/upgrade.ts new file mode 100644 index 0000000000000..48f826d1c3a91 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/upgrade.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { useEffect } from 'react'; +import { HttpFetchOptions, HttpStart } from 'src/core/public'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { + epmRouteService, + appRoutesService, + CheckPermissionsResponse, + BulkInstallPackagesResponse, +} from '../../../../../ingest_manager/common'; +import { StartServices } from '../../../types'; +import { useIngestEnabledCheck } from './ingest_enabled'; + +/** + * Requests that the endpoint package be upgraded to the latest version + * + * @param http an http client for sending the request + * @param options an object containing options for the request + */ +const sendUpgradeEndpointPackage = async ( + http: HttpStart, + options: HttpFetchOptions = {} +): Promise => { + return http.post(epmRouteService.getBulkInstallPath(), { + ...options, + body: JSON.stringify({ + packages: ['endpoint'], + }), + }); +}; + +/** + * Checks with the ingest manager if the current user making these requests has the right permissions + * to install the endpoint package. + * + * @param http an http client for sending the request + * @param options an object containing options for the request + */ +const sendCheckPermissions = async ( + http: HttpStart, + options: HttpFetchOptions = {} +): Promise => { + return http.get(appRoutesService.getCheckPermissionsPath(), { + ...options, + }); +}; + +export const useUpgradeEndpointPackage = () => { + const context = useKibana(); + const { allEnabled: ingestEnabled } = useIngestEnabledCheck(); + + useEffect(() => { + const abortController = new AbortController(); + + // cancel any ongoing requests + const abortRequests = () => { + abortController.abort(); + }; + + if (ingestEnabled) { + const signal = abortController.signal; + + (async () => { + try { + // make sure we're a privileged user before trying to install the package + const { success: hasPermissions } = await sendCheckPermissions(context.services.http, { + signal, + }); + + // if we're not a privileged user then return and don't try to check the status of the endpoint package + if (!hasPermissions) { + return abortRequests; + } + + // ignore the response for now since we aren't notifying the user + await sendUpgradeEndpointPackage(context.services.http, { signal }); + } catch (error) { + // Ignore Errors, since this should not hinder the user's ability to use the UI + + // ignore the error that occurs from aborting a request + if (!abortController.signal.aborted) { + // eslint-disable-next-line no-console + console.error(error); + } + } + + return abortRequests; + })(); + } + }, [ingestEnabled, context.services.http]); +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_selector.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_selector.tsx new file mode 100644 index 0000000000000..d5416acb69d14 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_selector.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowEqual, useSelector } from 'react-redux'; +import deepEqual from 'fast-deep-equal'; +import { State } from '../store'; + +export type TypedUseSelectorHook = ( + selector: (state: TState) => TSelected, + equalityFn?: (left: TSelected, right: TSelected) => boolean +) => TSelected; + +export const useShallowEqualSelector: TypedUseSelectorHook = (selector) => + useSelector(selector, shallowEqual); + +export const useDeepEqualSelector: TypedUseSelectorHook = (selector) => + useSelector(selector, deepEqual); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts index 573ef92f7e069..3051459d5de0c 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/__mocks__/index.ts @@ -12,9 +12,25 @@ import { createStartServicesMock, createWithKibanaMock, } from '../kibana_react.mock'; - +const mockStartServicesMock = createStartServicesMock(); export const KibanaServices = { get: jest.fn(), getKibanaVersion: jest.fn(() => '8.0.0') }; -export const useKibana = jest.fn().mockReturnValue({ services: createStartServicesMock() }); +export const useKibana = jest.fn().mockReturnValue({ + services: { + ...mockStartServicesMock, + data: { + ...mockStartServicesMock.data, + search: { + ...mockStartServicesMock.data.search, + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, + }, + }, +}); export const useUiSetting = jest.fn(createUseUiSettingMock()); export const useUiSetting$ = jest.fn(createUseUiSetting$Mock()); export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http); diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index c026b65853a4c..6f8ff2e1bb21a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -13,20 +13,20 @@ import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_r import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; import { securityMock } from '../../../../../../plugins/security/public/mocks'; import { - DEFAULT_APP_TIME_RANGE, DEFAULT_APP_REFRESH_INTERVAL, - DEFAULT_INDEX_KEY, + DEFAULT_APP_TIME_RANGE, + DEFAULT_BYTES_FORMAT, + DEFAULT_DARK_MODE, DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ, - DEFAULT_DARK_MODE, - DEFAULT_TIME_RANGE, - DEFAULT_REFRESH_RATE_INTERVAL, DEFAULT_FROM, - DEFAULT_TO, + DEFAULT_INDEX_KEY, + DEFAULT_INDEX_PATTERN, DEFAULT_INTERVAL_PAUSE, DEFAULT_INTERVAL_VALUE, - DEFAULT_BYTES_FORMAT, - DEFAULT_INDEX_PATTERN, + DEFAULT_REFRESH_RATE_INTERVAL, + DEFAULT_TIME_RANGE, + DEFAULT_TO, } from '../../../../common/constants'; import { StartServices } from '../../../types'; import { createSecuritySolutionStorageMock } from '../../mock/mock_local_storage'; @@ -77,14 +77,43 @@ export const createStartServicesMock = (): StartServices => { const data = dataPluginMock.createStartContract(); const security = securityMock.createSetup(); - const services = ({ + return ({ ...core, - data, + data: { + ...data, + query: { + ...data.query, + savedQueries: { + ...data.query.savedQueries, + getAllSavedQueries: jest.fn(() => + Promise.resolve({ + id: '123', + attributes: { + total: 123, + }, + }) + ), + findSavedQueries: jest.fn(() => + Promise.resolve({ + total: 123, + queries: [], + }) + ), + }, + }, + search: { + ...data.search, + search: jest.fn().mockImplementation(() => ({ + subscribe: jest.fn().mockImplementation(() => ({ + error: jest.fn(), + next: jest.fn(), + })), + })), + }, + }, security, storage, } as unknown) as StartServices; - - return services; }; export const createWithKibanaMock = () => { diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx index 73c0f00573911..3b7262e8a8d7e 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_root_provider.tsx @@ -11,7 +11,7 @@ import { Router } from 'react-router-dom'; import { History } from 'history'; import { useObservable } from 'react-use'; import { Store } from 'redux'; -import { EuiThemeProvider } from '../../../../../../legacy/common/eui_styled_components'; +import { EuiThemeProvider } from '../../../../../xpack_legacy/common'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { RouteCapture } from '../../components/endpoint/route_capture'; import { StartPlugins } from '../../../types'; diff --git a/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts b/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts new file mode 100644 index 0000000000000..0eb1c9a478ca0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/mock/formatted_relative.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('@kbn/i18n/react', () => { + const originalModule = jest.requireActual('@kbn/i18n/react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index a74c9a6d2009d..0944b6aa27f67 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -22,11 +22,15 @@ import { DEFAULT_TO, DEFAULT_INTERVAL_TYPE, DEFAULT_INTERVAL_VALUE, + DEFAULT_INDEX_PATTERN, } from '../../../common/constants'; import { networkModel } from '../../network/store'; import { TimelineType, TimelineStatus } from '../../../common/types/timeline'; import { mockManagementState } from '../../management/store/reducer'; import { ManagementState } from '../../management/types'; +import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model'; +import { mockBrowserFields, mockDocValueFields } from '../containers/source/mock'; +import { mockIndexPattern } from './index_pattern'; export const mockGlobalState: State = { app: { @@ -203,6 +207,7 @@ export const mockGlobalState: State = { id: 'test', savedObjectId: null, columns: defaultHeaders, + indexNames: DEFAULT_INDEX_PATTERN, itemsPerPage: 5, dataProviders: [], description: '', @@ -241,6 +246,28 @@ export const mockGlobalState: State = { }, insertTimeline: null, }, + sourcerer: { + ...initialSourcererState, + sourcererScopes: { + ...initialSourcererState.sourcererScopes, + [SourcererScopeName.default]: { + ...initialSourcererState.sourcererScopes[SourcererScopeName.default], + selectedPatterns: DEFAULT_INDEX_PATTERN, + browserFields: mockBrowserFields, + indexPattern: mockIndexPattern, + docValueFields: mockDocValueFields, + loading: false, + }, + [SourcererScopeName.timeline]: { + ...initialSourcererState.sourcererScopes[SourcererScopeName.timeline], + selectedPatterns: DEFAULT_INDEX_PATTERN, + browserFields: mockBrowserFields, + indexPattern: mockIndexPattern, + docValueFields: mockDocValueFields, + loading: false, + }, + }, + }, /** * These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture, * they are cast to mutable versions here. diff --git a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts index 826057560f942..e4abc17e9034c 100644 --- a/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts +++ b/x-pack/plugins/security_solution/public/common/mock/index_pattern.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export const mockIndexPattern = { +import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; + +export const mockIndexPattern: IIndexPattern = { fields: [ { name: '@timestamp', @@ -93,3 +95,5 @@ export const mockIndexPattern = { ], title: 'filebeat-*,auditbeat-*,packetbeat-*', }; + +export const mockIndexNames = ['filebeat-*', 'auditbeat-*', 'packetbeat-*']; diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts index 2395010a0ba2e..c5d881c540eec 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts +++ b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DetailItem } from '../../graphql/types'; +import { TimelineEventsDetailsItem } from '../../../common/search_strategy'; export const mockDetailItemDataId = 'Y-6TfmcB0WOhS6qyMv3s'; -export const mockDetailItemData: DetailItem[] = [ +export const mockDetailItemData: TimelineEventsDetailsItem[] = [ { field: '_id', originalValue: 'pEMaMmkBUV60JmNWmWVi', diff --git a/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts b/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.ts new file mode 100644 index 0000000000000..e077d28925912 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/mock/react_beautiful_dnd.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 { + DraggableProvided, + DraggableStateSnapshot, + DroppableProvided, + DroppableStateSnapshot, +} from 'react-beautiful-dnd'; +import React from 'react'; + +jest.mock('react-beautiful-dnd', () => ({ + Droppable: ({ + children, + }: { + children: (a: DroppableProvided, b: DroppableStateSnapshot) => void; + }) => + children( + { + droppableProps: { + 'data-rbd-droppable-context-id': '123', + 'data-rbd-droppable-id': '123', + }, + innerRef: jest.fn(), + }, + { + isDraggingOver: false, + isUsingPlaceholder: false, + } + ), + Draggable: ({ + children, + }: { + children: (a: DraggableProvided, b: DraggableStateSnapshot) => void; + }) => + children( + { + draggableProps: { + 'data-rbd-draggable-context-id': '123', + 'data-rbd-draggable-id': '123', + }, + innerRef: jest.fn(), + }, + { + isDragging: false, + isDropAnimating: false, + } + ), + DragDropContext: ({ children }: { children: React.ReactNode }) => children, +})); diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 8c186addf783d..e84f80655fbe4 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -46,7 +46,7 @@ export const kibanaObservable = new BehaviorSubject(createStartServicesMock()); Object.defineProperty(window, 'localStorage', { value: localStorageMock(), }); - +window.scrollTo = jest.fn(); const MockKibanaContextProvider = createKibanaContextProviderMock(); const { storage } = createSecuritySolutionStorageMock(); diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 26013915315af..9f26fc22ede53 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -8,13 +8,8 @@ import { FilterStateStore } from '../../../../../../src/plugins/data/common/es_q import { TimelineId, TimelineType, TimelineStatus } from '../../../common/types/timeline'; import { OpenTimelineResult } from '../../timelines/components/open_timeline/types'; -import { - GetAllTimeline, - SortFieldTimeline, - TimelineResult, - Direction, - DetailItem, -} from '../../graphql/types'; +import { GetAllTimeline, SortFieldTimeline, TimelineResult, Direction } from '../../graphql/types'; +import { TimelineEventsDetailsItem } from '../../../common/search_strategy'; import { allTimelinesQuery } from '../../timelines/containers/all/index.gql_query'; import { CreateTimelineProps } from '../../detections/components/alerts_table/types'; import { TimelineModel } from '../../timelines/store/timeline/model'; @@ -2124,6 +2119,7 @@ export const mockTimelineModel: TimelineModel = { highlightedDropAndProviderId: '', historyIds: [], id: 'ef579e40-jibber-jabber', + indexNames: [], isFavorite: false, isLive: false, isLoading: false, @@ -2228,6 +2224,7 @@ export const defaultTimelineProps: CreateTimelineProps = { highlightedDropAndProviderId: '', historyIds: [], id: TimelineId.active, + indexNames: [], isFavorite: false, isLive: false, isLoading: false, @@ -2262,7 +2259,7 @@ export const defaultTimelineProps: CreateTimelineProps = { ruleNote: '# this is some markdown documentation', }; -export const mockTimelineDetails: DetailItem[] = [ +export const mockTimelineDetails: TimelineEventsDetailsItem[] = [ { field: 'host.name', values: ['apache'], diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index 6b446ab6692d9..f4134b5c47c2c 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -12,6 +12,7 @@ import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store export { appActions } from './app'; export { dragAndDropActions } from './drag_and_drop'; export { inputsActions } from './inputs'; +export { sourcererActions } from './sourcerer'; import { RoutingAction } from './routing'; export type AppAction = diff --git a/x-pack/plugins/security_solution/public/common/store/model.ts b/x-pack/plugins/security_solution/public/common/store/model.ts index 0032a95cce321..04603d0607583 100644 --- a/x-pack/plugins/security_solution/public/common/store/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/model.ts @@ -7,4 +7,5 @@ export { appModel } from './app'; export { dragAndDropModel } from './drag_and_drop'; export { inputsModel } from './inputs'; +export { sourcererModel } from './sourcerer'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.ts b/x-pack/plugins/security_solution/public/common/store/reducer.ts index a0977cea71da7..60cb6a4e960bd 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.ts @@ -9,6 +9,7 @@ import { combineReducers, PreloadedState, AnyAction, Reducer } from 'redux'; import { appReducer, initialAppState } from './app'; import { dragAndDropReducer, initialDragAndDropState } from './drag_and_drop'; import { createInitialInputsState, inputsReducer } from './inputs'; +import { sourcererReducer, sourcererModel } from './sourcerer'; import { HostsPluginReducer } from '../../hosts/store'; import { NetworkPluginReducer } from '../../network/store'; @@ -18,6 +19,7 @@ import { SecuritySubPlugins } from '../../app/types'; import { ManagementPluginReducer } from '../../management'; import { State } from './types'; import { AppAction } from './actions'; +import { KibanaIndexPatterns } from './sourcerer/model'; export type SubPluginsInitReducer = HostsPluginReducer & NetworkPluginReducer & @@ -28,13 +30,22 @@ export type SubPluginsInitReducer = HostsPluginReducer & * Factory for the 'initialState' that is used to preload state into the Security App's redux store. */ export const createInitialState = ( - pluginsInitState: SecuritySubPlugins['store']['initialState'] + pluginsInitState: SecuritySubPlugins['store']['initialState'], + { + kibanaIndexPatterns, + configIndexPatterns, + }: { kibanaIndexPatterns: KibanaIndexPatterns; configIndexPatterns: string[] } ): PreloadedState => { const preloadedState: PreloadedState = { app: initialAppState, dragAndDrop: initialDragAndDropState, ...pluginsInitState, inputs: createInitialInputsState(), + sourcerer: { + ...sourcererModel.initialSourcererState, + kibanaIndexPatterns, + configIndexPatterns, + }, }; return preloadedState; }; @@ -49,5 +60,6 @@ export const createReducer: ( app: appReducer, dragAndDrop: dragAndDropReducer, inputs: inputsReducer, + sourcerer: sourcererReducer, ...pluginsReducer, }); diff --git a/x-pack/plugins/security_solution/public/common/store/selectors.ts b/x-pack/plugins/security_solution/public/common/store/selectors.ts index b938bae39b634..3cefd92bf9e60 100644 --- a/x-pack/plugins/security_solution/public/common/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/common/store/selectors.ts @@ -7,3 +7,4 @@ export { appSelectors } from './app'; export { dragAndDropSelectors } from './drag_and_drop'; export { inputsSelectors } from './inputs'; +export { sourcererSelectors } from './sourcerer'; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts new file mode 100644 index 0000000000000..0b40586798f09 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/actions.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import actionCreatorFactory from 'typescript-fsa'; +import { TimelineEventsType } from '../../../../common/types/timeline'; + +import { KibanaIndexPatterns, ManageScopeInit, SourcererScopeName } from './model'; + +const actionCreator = actionCreatorFactory('x-pack/security_solution/local/sourcerer'); + +export const setSource = actionCreator<{ + id: SourcererScopeName; + payload: ManageScopeInit; +}>('SET_SOURCE'); + +export const setIndexPatternsList = actionCreator<{ + kibanaIndexPatterns: KibanaIndexPatterns; + configIndexPatterns: string[]; +}>('SET_INDEX_PATTERNS_LIST'); + +export const setSignalIndexName = actionCreator<{ signalIndexName: string }>( + 'SET_SIGNAL_INDEX_NAME' +); + +export const setSourcererScopeLoading = actionCreator<{ id: SourcererScopeName; loading: boolean }>( + 'SET_SOURCERER_SCOPE_LOADING' +); + +export const setSelectedIndexPatterns = actionCreator<{ + id: SourcererScopeName; + selectedPatterns: string[]; + eventType?: TimelineEventsType; +}>('SET_SELECTED_INDEX_PATTERNS'); diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/index.ts new file mode 100644 index 0000000000000..551c7d8e3efbc --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/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 * as sourcererActions from './actions'; +import * as sourcererModel from './model'; +import * as sourcererSelectors from './selectors'; + +export { sourcererActions, sourcererModel, sourcererSelectors }; +export * from './reducer'; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts new file mode 100644 index 0000000000000..93f7ff95dfb00 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; +import { DocValueFields } from '../../../../common/search_strategy/common'; +import { + BrowserFields, + EMPTY_BROWSER_FIELDS, + EMPTY_DOCVALUE_FIELD, + EMPTY_INDEX_PATTERN, +} from '../../../../common/search_strategy/index_fields'; + +export type ErrorModel = Error[]; + +export enum SourcererScopeName { + default = 'default', + detections = 'detections', + timeline = 'timeline', +} + +export interface ManageScope { + browserFields: BrowserFields; + docValueFields: DocValueFields[]; + errorMessage: string | null; + id: SourcererScopeName; + indexPattern: IIndexPattern; + indicesExist: boolean | undefined | null; + loading: boolean; + selectedPatterns: string[]; +} + +export interface ManageScopeInit extends Partial { + id: SourcererScopeName; +} + +export type SourcererScopeById = { + [id in SourcererScopeName]: ManageScope; +}; + +export type KibanaIndexPatterns = Array<{ id: string; title: string }>; + +// ManageSourcerer +export interface SourcererModel { + kibanaIndexPatterns: KibanaIndexPatterns; + configIndexPatterns: string[]; + signalIndexName: string | null; + sourcererScopes: SourcererScopeById; +} + +export const initSourcererScope = { + browserFields: EMPTY_BROWSER_FIELDS, + docValueFields: EMPTY_DOCVALUE_FIELD, + errorMessage: null, + indexPattern: EMPTY_INDEX_PATTERN, + indicesExist: true, + loading: true, + selectedPatterns: [], +}; + +export const initialSourcererState: SourcererModel = { + kibanaIndexPatterns: [], + configIndexPatterns: [], + signalIndexName: null, + sourcererScopes: { + [SourcererScopeName.default]: { + ...initSourcererScope, + id: SourcererScopeName.default, + }, + [SourcererScopeName.detections]: { + ...initSourcererScope, + id: SourcererScopeName.detections, + }, + [SourcererScopeName.timeline]: { + ...initSourcererScope, + id: SourcererScopeName.timeline, + }, + }, +}; + +export type FSourcererScopePatterns = { + [id in SourcererScopeName]: string[]; +}; +export type SourcererScopePatterns = Partial; diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts new file mode 100644 index 0000000000000..221244aaf9200 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/reducer.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports +import isEmpty from 'lodash/isEmpty'; +import { reducerWithInitialState } from 'typescript-fsa-reducers'; + +import { + setIndexPatternsList, + setSourcererScopeLoading, + setSelectedIndexPatterns, + setSignalIndexName, + setSource, +} from './actions'; +import { initialSourcererState, SourcererModel, SourcererScopeName } from './model'; + +export type SourcererState = SourcererModel; + +export const sourcererReducer = reducerWithInitialState(initialSourcererState) + .case(setIndexPatternsList, (state, { kibanaIndexPatterns, configIndexPatterns }) => ({ + ...state, + kibanaIndexPatterns, + configIndexPatterns, + })) + .case(setSignalIndexName, (state, { signalIndexName }) => ({ + ...state, + signalIndexName, + })) + .case(setSourcererScopeLoading, (state, { id, loading }) => ({ + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + loading, + }, + }, + })) + .case(setSelectedIndexPatterns, (state, { id, selectedPatterns, eventType }) => { + const kibanaIndexPatterns = state.kibanaIndexPatterns.map((kip) => kip.title); + const newSelectedPatterns = selectedPatterns.filter( + (sp) => + state.configIndexPatterns.includes(sp) || + kibanaIndexPatterns.includes(sp) || + (!isEmpty(state.signalIndexName) && state.signalIndexName === sp) + ); + let defaultIndexPatterns = state.configIndexPatterns; + if (id === SourcererScopeName.timeline && isEmpty(newSelectedPatterns)) { + if (eventType === 'all' && !isEmpty(state.signalIndexName)) { + defaultIndexPatterns = [...state.configIndexPatterns, state.signalIndexName ?? '']; + } else if (eventType === 'raw') { + defaultIndexPatterns = state.configIndexPatterns; + } else if ( + !isEmpty(state.signalIndexName) && + (eventType === 'signal' || eventType === 'alert') + ) { + defaultIndexPatterns = [state.signalIndexName ?? '']; + } + } else if (id === SourcererScopeName.detections && isEmpty(newSelectedPatterns)) { + defaultIndexPatterns = [state.signalIndexName ?? '']; + } + return { + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + selectedPatterns: isEmpty(newSelectedPatterns) + ? defaultIndexPatterns + : newSelectedPatterns, + }, + }, + }; + }) + .case(setSource, (state, { id, payload }) => { + const { ...sourcererScopes } = payload; + return { + ...state, + sourcererScopes: { + ...state.sourcererScopes, + [id]: { + ...state.sourcererScopes[id], + ...sourcererScopes, + ...(state.sourcererScopes[id].selectedPatterns.length === 0 + ? { selectedPatterns: state.configIndexPatterns } + : {}), + }, + }, + }; + }) + .build(); diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts new file mode 100644 index 0000000000000..ca9ea26ba5bac --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/selectors.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { createSelector } from 'reselect'; +import { State } from '../types'; +import { SourcererScopeById, KibanaIndexPatterns, SourcererScopeName, ManageScope } from './model'; + +export const sourcererKibanaIndexPatternsSelector = ({ sourcerer }: State): KibanaIndexPatterns => + sourcerer.kibanaIndexPatterns; + +export const sourcererSignalIndexNameSelector = ({ sourcerer }: State): string | null => + sourcerer.signalIndexName; + +export const sourcererConfigIndexPatternsSelector = ({ sourcerer }: State): string[] => + sourcerer.configIndexPatterns; + +export const sourcererScopesSelector = ({ sourcerer }: State): SourcererScopeById => + sourcerer.sourcererScopes; + +export const scopesSelector = () => createSelector(sourcererScopesSelector, (scopes) => scopes); + +export const kibanaIndexPatternsSelector = () => + createSelector( + sourcererKibanaIndexPatternsSelector, + (kibanaIndexPatterns) => kibanaIndexPatterns + ); + +export const signalIndexNameSelector = () => + createSelector(sourcererSignalIndexNameSelector, (signalIndexName) => signalIndexName); + +export const configIndexPatternsSelector = () => + createSelector( + sourcererConfigIndexPatternsSelector, + (configIndexPatterns) => configIndexPatterns + ); + +export const getIndexNamesSelectedSelector = () => { + const getScopesSelector = scopesSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): string[] => { + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return scope.selectedPatterns.length === 0 ? configIndexPatterns : scope.selectedPatterns; + }; + + return mapStateToProps; +}; + +export const getAllExistingIndexNamesSelector = () => { + const getSignalIndexNameSelector = signalIndexNameSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State): string[] => { + const signalIndexName = getSignalIndexNameSelector(state); + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return signalIndexName != null + ? [...configIndexPatterns, signalIndexName] + : configIndexPatterns; + }; + + return mapStateToProps; +}; + +export const defaultIndexNamesSelector = () => { + const getScopesSelector = scopesSelector(); + const getConfigIndexPatternsSelector = configIndexPatternsSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): string[] => { + const scope = getScopesSelector(state)[scopeId]; + const configIndexPatterns = getConfigIndexPatternsSelector(state); + + return scope.selectedPatterns.length === 0 ? configIndexPatterns : scope.selectedPatterns; + }; + + return mapStateToProps; +}; + +export const getSourcererScopeSelector = () => { + const getScopesSelector = scopesSelector(); + + const mapStateToProps = (state: State, scopeId: SourcererScopeName): ManageScope => + getScopesSelector(state)[scopeId]; + + return mapStateToProps; +}; diff --git a/x-pack/plugins/security_solution/public/common/store/types.ts b/x-pack/plugins/security_solution/public/common/store/types.ts index 91d92e4758c4a..6903567c752bc 100644 --- a/x-pack/plugins/security_solution/public/common/store/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/types.ts @@ -12,6 +12,7 @@ import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; import { AppState } from './app/reducer'; import { InputsState } from './inputs/reducer'; +import { SourcererState } from './sourcerer/reducer'; import { HostsPluginState } from '../../hosts/store'; import { DragAndDropState } from './drag_and_drop/reducer'; import { TimelinePluginState } from '../../timelines/store/timeline'; @@ -25,6 +26,7 @@ export type StoreState = HostsPluginState & app: AppState; dragAndDrop: DragAndDropState; inputs: InputsState; + sourcerer: SourcererState; }; /** * The redux `State` type for the Security App. diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 678aaf06e50e4..47da1e93cf004 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -15,10 +15,12 @@ import { apolloClient, mockTimelineApolloResult, mockTimelineDetailsApollo, + mockTimelineDetails, } from '../../../common/mock/'; import { CreateTimeline, UpdateTimelineLoading } from './types'; import { Ecs } from '../../../../common/ecs'; import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { ISearchStart } from '../../../../../../../src/plugins/data/public'; jest.mock('apollo-client'); @@ -27,6 +29,7 @@ describe('alert actions', () => { const unix = moment(anchor).valueOf(); let createTimeline: CreateTimeline; let updateTimelineIsLoading: UpdateTimelineLoading; + let searchStrategyClient: ISearchStart; let clock: sinon.SinonFakeTimers; beforeEach(() => { @@ -39,6 +42,12 @@ describe('alert actions', () => { createTimeline = jest.fn() as jest.Mocked; updateTimelineIsLoading = jest.fn() as jest.Mocked; + searchStrategyClient = { + aggs: {} as ISearchStart['aggs'], + showError: jest.fn(), + search: jest.fn().mockResolvedValue({ data: mockTimelineDetails }), + searchSource: {} as ISearchStart['searchSource'], + }; jest.spyOn(apolloClient, 'query').mockImplementation((obj) => { const id = get('variables.id', obj); @@ -64,6 +73,7 @@ describe('alert actions', () => { ecsData: mockEcsDataWithAlert, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); expect(updateTimelineIsLoading).toHaveBeenCalledTimes(1); @@ -80,6 +90,7 @@ describe('alert actions', () => { ecsData: mockEcsDataWithAlert, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); const expected = { from: '2018-11-05T18:58:25.937Z', @@ -197,6 +208,7 @@ describe('alert actions', () => { highlightedDropAndProviderId: '', historyIds: [], id: '', + indexNames: [], isFavorite: false, isLive: false, isLoading: false, @@ -267,6 +279,7 @@ describe('alert actions', () => { ecsData: mockEcsDataWithAlert, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); const createTimelineArg = (createTimeline as jest.Mock).mock.calls[0][0]; @@ -296,6 +309,7 @@ describe('alert actions', () => { ecsData: mockEcsDataWithAlert, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); const createTimelineArg = (createTimeline as jest.Mock).mock.calls[0][0]; @@ -314,6 +328,7 @@ describe('alert actions', () => { ecsData: mockEcsDataWithAlert, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); expect(updateTimelineIsLoading).toHaveBeenCalledWith({ @@ -348,6 +363,7 @@ describe('alert actions', () => { ecsData: ecsDataMock, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); expect(updateTimelineIsLoading).not.toHaveBeenCalled(); @@ -373,6 +389,7 @@ describe('alert actions', () => { ecsData: ecsDataMock, nonEcsData: [], updateTimelineIsLoading, + searchStrategyClient, }); expect(updateTimelineIsLoading).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 640726bb2e7c8..0e2aee5abd42e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -15,10 +15,13 @@ import { TimelineId, TimelineStatus, TimelineType } from '../../../../common/typ import { updateAlertStatus } from '../../containers/detection_engine/alerts/api'; import { SendAlertToTimelineActionProps, UpdateAlertStatusActionProps } from './types'; import { Ecs } from '../../../../common/ecs'; -import { GetOneTimeline, TimelineResult, GetTimelineDetailsQuery } from '../../../graphql/types'; +import { GetOneTimeline, TimelineResult } from '../../../graphql/types'; import { TimelineNonEcsData, TimelineEventsDetailsItem, + TimelineEventsDetailsRequestOptions, + TimelineEventsDetailsStrategyResponse, + TimelineEventsQueries, } from '../../../../common/search_strategy/timeline'; import { oneTimelineQuery } from '../../../timelines/containers/one/index.gql_query'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; @@ -34,7 +37,6 @@ import { } from './helpers'; import { KueryFilterQueryKind } from '../../../common/store'; import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; -import { timelineDetailsQuery } from '../../../timelines/containers/details/index.gql_query'; export const getUpdateAlertsQuery = (eventIds: Readonly) => { return { @@ -154,6 +156,7 @@ export const sendAlertToTimelineAction = async ({ ecsData, nonEcsData, updateTimelineIsLoading, + searchStrategyClient, }: SendAlertToTimelineActionProps) => { let openAlertInBasicTimeline = true; const noteContent = ecsData.signal?.rule?.note != null ? ecsData.signal?.rule?.note[0] : ''; @@ -172,24 +175,24 @@ export const sendAlertToTimelineAction = async ({ id: timelineId, }, }), - apolloClient.query({ - query: timelineDetailsQuery, - fetchPolicy: 'no-cache', - variables: { + searchStrategyClient.search< + TimelineEventsDetailsRequestOptions, + TimelineEventsDetailsStrategyResponse + >( + { defaultIndex: [], docValueFields: [], - eventId: ecsData._id, indexName: ecsData._index ?? '', - sourceId: 'default', + eventId: ecsData._id, + factoryQueryType: TimelineEventsQueries.details, }, - }), + { + strategy: 'securitySolutionTimelineSearchStrategy', + } + ), ]); const resultingTimeline: TimelineResult = getOr({}, 'data.getOneTimeline', responseTimeline); - const eventData: TimelineEventsDetailsItem[] = getOr( - [], - 'data.source.TimelineDetails.data', - eventDataResp - ); + const eventData: TimelineEventsDetailsItem[] = getOr([], 'data', eventDataResp); if (!isEmpty(resultingTimeline)) { const timelineTemplate: TimelineResult = omitTypenameInTimeline(resultingTimeline); openAlertInBasicTimeline = false; @@ -279,6 +282,7 @@ export const sendAlertToTimelineAction = async ({ ...getThresholdAggregationDataProvider(ecsData, nonEcsData), ], id: TimelineId.active, + indexNames: [], dateRange: { start: from, end: to, @@ -329,6 +333,7 @@ export const sendAlertToTimelineAction = async ({ }, ], id: TimelineId.active, + indexNames: [], dateRange: { start: from, end: to, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts index 20c233a03a8cf..b386ce0c9631b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/helpers.ts @@ -11,7 +11,8 @@ import { DataProviderType, DataProvidersAnd, } from '../../../timelines/components/timeline/data_providers/data_provider'; -import { DetailItem, TimelineType } from '../../../graphql/types'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; +import { TimelineType } from '../../../graphql/types'; interface FindValueToChangeInQuery { field: string; @@ -49,7 +50,7 @@ const templateFields = [ */ export const getStringArray = ( field: string, - data: DetailItem[], + data: TimelineEventsDetailsItem[], localConsole = console ): string[] => { const value: unknown | undefined = data.find((d) => d.field === field)?.values ?? null; @@ -108,7 +109,7 @@ export const findValueToChangeInQuery = ( export const replaceTemplateFieldFromQuery = ( query: string, - eventData: DetailItem[], + eventData: TimelineEventsDetailsItem[], timelineType: TimelineType = TimelineType.default ): string => { if (timelineType === TimelineType.default) { @@ -132,7 +133,7 @@ export const replaceTemplateFieldFromQuery = ( export const replaceTemplateFieldFromMatchFilters = ( filters: Filter[], - eventData: DetailItem[] + eventData: TimelineEventsDetailsItem[] ): Filter[] => filters.map((filter) => { if ( @@ -151,7 +152,7 @@ export const replaceTemplateFieldFromMatchFilters = ( export const reformatDataProviderWithNewValue = ( dataProvider: T, - eventData: DetailItem[], + eventData: TimelineEventsDetailsItem[], timelineType: TimelineType = TimelineType.default ): T => { // Support for legacy "template-like" timeline behavior that is using hardcoded list of templateFields @@ -201,7 +202,7 @@ export const reformatDataProviderWithNewValue = dataProviders.map((dataProvider) => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx index be24957602037..6724d3a83d617 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.test.tsx @@ -22,7 +22,6 @@ describe('AlertsTableComponent', () => { hasIndexWrite from={'2020-07-07T08:20:18.966Z'} loading - signalsIndex="index" to={'2020-07-08T08:20:18.966Z'} globalQuery={{ query: 'query', diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 0416b3d2a459f..d66d37a020040 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -13,7 +13,6 @@ import { Status } from '../../../../common/detection_engine/schemas/common/schem import { Filter, esQuery } from '../../../../../../../src/plugins/data/public'; import { TimelineIdLiteral } from '../../../../common/types/timeline'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; -import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns'; import { StatefulEventsViewer } from '../../../common/components/events_viewer'; import { HeaderSection } from '../../../common/components/header_section'; import { combineQueries } from '../../../timelines/components/timeline/helpers'; @@ -45,6 +44,8 @@ import { displaySuccessToast, displayErrorToast, } from '../../../common/components/toasters'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; interface OwnProps { timelineId: TimelineIdLiteral; @@ -55,7 +56,6 @@ interface OwnProps { loading: boolean; showBuildingBlockAlerts: boolean; onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void; - signalsIndex: string; to: string; } @@ -80,19 +80,20 @@ export const AlertsTableComponent: React.FC = ({ setEventsLoading, showBuildingBlockAlerts, onShowBuildingBlockAlertsChanged, - signalsIndex, to, }) => { const [showClearSelectionAction, setShowClearSelectionAction] = useState(false); const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); - const [{ browserFields, indexPatterns, isLoading: indexPatternsLoading }] = useFetchIndexPatterns( - signalsIndex !== '' ? [signalsIndex] : [], - 'alerts_table' - ); + const { + browserFields, + indexPattern: indexPatterns, + loading: indexPatternsLoading, + selectedPatterns, + } = useSourcererScope(SourcererScopeName.detections); const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); const { addWarning } = useAppToasts(); - const { initializeTimeline, setSelectAll, setIndexToAdd } = useManageTimeline(); + const { initializeTimeline, setSelectAll } = useManageTimeline(); const getGlobalQuery = useCallback( (customFilters: Filter[]) => { @@ -284,7 +285,6 @@ export const AlertsTableComponent: React.FC = ({ ] ); - const defaultIndices = useMemo(() => [signalsIndex], [signalsIndex]); const defaultFiltersMemo = useMemo(() => { if (isEmpty(defaultFilters)) { return buildAlertStatusFilter(filterGroup); @@ -301,7 +301,6 @@ export const AlertsTableComponent: React.FC = ({ filterManager, footerText: i18n.TOTAL_COUNT_OF_ALERTS, id: timelineId, - indexToAdd: defaultIndices, loadingText: i18n.LOADING_ALERTS, selectAll: false, queryFields: requiredFieldsForActions, @@ -310,16 +309,12 @@ export const AlertsTableComponent: React.FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - setIndexToAdd({ id: timelineId, indexToAdd: defaultIndices }); - }, [timelineId, defaultIndices, setIndexToAdd]); - const headerFilterGroup = useMemo( () => , [onFilterGroupChangedCallback] ); - if (loading || indexPatternsLoading || isEmpty(signalsIndex)) { + if (loading || indexPatternsLoading || isEmpty(selectedPatterns)) { return ( @@ -330,12 +325,12 @@ export const AlertsTableComponent: React.FC = ({ return ( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index 4559e44b8c3c5..1a0b35620c9c9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -14,6 +14,7 @@ import { EuiContextMenuItem, } from '@elastic/eui'; import styled from 'styled-components'; +import { getOr } from 'lodash/fp'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { TimelineId } from '../../../../../common/types/timeline'; @@ -28,12 +29,7 @@ import { FILTER_OPEN, FILTER_CLOSED, FILTER_IN_PROGRESS } from '../alerts_filter import { updateAlertStatusAction } from '../actions'; import { SetEventsDeletedProps, SetEventsLoadingProps } from '../types'; import { Ecs } from '../../../../../common/ecs'; -import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline'; -import { - AddExceptionModal as AddExceptionModalComponent, - AddExceptionModalBaseProps, -} from '../../../../common/components/exceptions/add_exception_modal'; -import { getMappedNonEcsValue } from '../../../../common/components/exceptions/helpers'; +import { AddExceptionModal as AddExceptionModalComponent } from '../../../../common/components/exceptions/add_exception_modal'; import * as i18nCommon from '../../../../common/translations'; import * as i18n from '../translations'; import { @@ -43,27 +39,18 @@ import { } from '../../../../common/components/toasters'; import { inputsModel } from '../../../../common/store'; import { useUserData } from '../../user_info'; +import { ExceptionListType } from '../../../../../common/shared_imports'; interface AlertContextMenuProps { disabled: boolean; ecsRowData: Ecs; - nonEcsRowData: TimelineNonEcsData[]; refetch: inputsModel.Refetch; timelineId: string; } -const addExceptionModalInitialState: AddExceptionModalBaseProps = { - ruleName: '', - ruleId: '', - ruleIndices: [], - exceptionListType: 'detection', - alertData: undefined, -}; - const AlertContextMenuComponent: React.FC = ({ disabled, ecsRowData, - nonEcsRowData, refetch, timelineId, }) => { @@ -74,6 +61,23 @@ const AlertContextMenuComponent: React.FC = ({ (ecsRowData.signal?.status && (ecsRowData.signal.status[0] as Status)) ?? undefined ); const eventId = ecsRowData._id; + const ruleId = useMemo( + (): string | null => + (ecsRowData.signal?.rule && ecsRowData.signal.rule.id && ecsRowData.signal.rule.id[0]) ?? + null, + [ecsRowData] + ); + const ruleName = useMemo( + (): string => + (ecsRowData.signal?.rule && ecsRowData.signal.rule.name && ecsRowData.signal.rule.name[0]) ?? + '', + [ecsRowData] + ); + const ruleIndices = useMemo( + (): string[] => + (ecsRowData.signal?.rule && ecsRowData.signal.rule.index) ?? DEFAULT_INDEX_PATTERN, + [ecsRowData] + ); const { addWarning } = useAppToasts(); @@ -81,35 +85,26 @@ const AlertContextMenuComponent: React.FC = ({ setPopover(!isPopoverOpen); }, [isPopoverOpen]); - const closePopover = useCallback(() => { + const closePopover = useCallback((): void => { setPopover(false); }, []); - const [shouldShowAddExceptionModal, setShouldShowAddExceptionModal] = useState(false); - const [addExceptionModalState, setAddExceptionModalState] = useState( - addExceptionModalInitialState - ); + const [exceptionModalType, setOpenAddExceptionModal] = useState(null); const [{ canUserCRUD, hasIndexWrite }] = useUserData(); - const isEndpointAlert = useMemo(() => { - if (!nonEcsRowData) { + const isEndpointAlert = useMemo((): boolean => { + if (ecsRowData == null) { return false; } - const [module] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.original_event.module', - }); - const [kind] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.original_event.kind', - }); - return module === 'endpoint' && kind === 'alert'; - }, [nonEcsRowData]); + const eventModules = getOr([], 'signal.original_event.module', ecsRowData); + const kinds = getOr([], 'signal.original_event.kind', ecsRowData); - const closeAddExceptionModal = useCallback(() => { - setShouldShowAddExceptionModal(false); - setAddExceptionModalState(addExceptionModalInitialState); - }, [setShouldShowAddExceptionModal, setAddExceptionModalState]); + return eventModules.includes('endpoint') && kinds.includes('alert'); + }, [ecsRowData]); + + const closeAddExceptionModal = useCallback((): void => { + setOpenAddExceptionModal(null); + }, []); const onAddExceptionCancel = useCallback(() => { closeAddExceptionModal(); @@ -283,55 +278,6 @@ const AlertContextMenuComponent: React.FC = ({ ); - const openAddExceptionModal = useCallback( - ({ - ruleName, - ruleIndices, - ruleId, - exceptionListType, - alertData, - }: AddExceptionModalBaseProps) => { - if (alertData !== null && alertData !== undefined) { - setShouldShowAddExceptionModal(true); - setAddExceptionModalState({ - ruleName, - ruleId, - ruleIndices, - exceptionListType, - alertData, - }); - } - }, - [setShouldShowAddExceptionModal, setAddExceptionModalState] - ); - - const AddExceptionModal = useCallback( - () => - shouldShowAddExceptionModal === true && addExceptionModalState.alertData !== null ? ( - - ) : null, - [ - shouldShowAddExceptionModal, - addExceptionModalState.alertData, - addExceptionModalState.ruleName, - addExceptionModalState.ruleId, - addExceptionModalState.ruleIndices, - addExceptionModalState.exceptionListType, - onAddExceptionCancel, - onAddExceptionConfirm, - alertStatus, - ] - ); - const button = ( = ({ /> ); - const handleAddEndpointExceptionClick = useCallback(() => { - const [ruleName] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.name', - }); - const [ruleId] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.id', - }); - const ruleIndices = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.index', - }); - + const handleAddEndpointExceptionClick = useCallback((): void => { closePopover(); - - if (ruleId !== undefined) { - openAddExceptionModal({ - ruleName: ruleName ?? '', - ruleId, - ruleIndices: ruleIndices.length > 0 ? ruleIndices : DEFAULT_INDEX_PATTERN, - exceptionListType: 'endpoint', - alertData: { - ecsData: ecsRowData, - nonEcsData: nonEcsRowData, - }, - }); - } - }, [closePopover, ecsRowData, nonEcsRowData, openAddExceptionModal]); + setOpenAddExceptionModal('endpoint'); + }, [closePopover]); const addEndpointExceptionComponent = ( = ({ ); - const handleAddExceptionClick = useCallback(() => { - const [ruleName] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.name', - }); - const [ruleId] = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.id', - }); - const ruleIndices = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.index', - }); - + const handleAddExceptionClick = useCallback((): void => { closePopover(); + setOpenAddExceptionModal('detection'); + }, [closePopover]); - if (ruleId !== undefined) { - openAddExceptionModal({ - ruleName: ruleName ?? '', - ruleId, - ruleIndices: ruleIndices.length > 0 ? ruleIndices : DEFAULT_INDEX_PATTERN, - exceptionListType: 'detection', - alertData: { - ecsData: ecsRowData, - nonEcsData: nonEcsRowData, - }, - }); - } - }, [closePopover, ecsRowData, nonEcsRowData, openAddExceptionModal]); - - const areExceptionsAllowed = useMemo(() => { - const ruleTypes = getMappedNonEcsValue({ - data: nonEcsRowData, - fieldName: 'signal.rule.type', - }); + const areExceptionsAllowed = useMemo((): boolean => { + const ruleTypes = getOr([], 'signal.rule.type', ecsRowData); const [ruleType] = ruleTypes as Type[]; return !isMlRule(ruleType) && !isThresholdRule(ruleType); - }, [nonEcsRowData]); + }, [ecsRowData]); const addExceptionComponent = ( = ({
- + {exceptionModalType != null && ruleId != null && ecsRowData != null && ( + + )} ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx index 4ab5fa5e6012f..8960b7a76660b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_timeline_action.tsx @@ -7,6 +7,7 @@ import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; +import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; import { Ecs } from '../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../common/search_strategy/timeline'; @@ -15,7 +16,6 @@ import { useApolloClient } from '../../../../common/utils/apollo_context'; import { sendAlertToTimelineAction } from '../actions'; import { dispatchUpdateTimeline } from '../../../../timelines/components/open_timeline/helpers'; import { ActionIconItem } from '../../../../timelines/components/timeline/body/actions/action_icon_item'; - import { CreateTimelineProps } from '../types'; import { ACTION_INVESTIGATE_IN_TIMELINE, @@ -31,6 +31,9 @@ const InvestigateInTimelineActionComponent: React.FC { + const { + data: { search: searchStrategyClient }, + } = useKibana().services; const dispatch = useDispatch(); const apolloClient = useApolloClient(); @@ -49,6 +52,8 @@ const InvestigateInTimelineActionComponent: React.FC void; diff --git a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx b/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx index 78a18dc336e5b..1a2deb059ad4f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx @@ -10,7 +10,7 @@ import { HeaderPage, HeaderPageProps } from '../../../common/components/header_p import * as i18n from './translations'; const DetectionEngineHeaderPageComponent: React.FC = (props) => ( - + ); DetectionEngineHeaderPageComponent.defaultProps = { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx index f2eb5cf5b94f3..2ce9d1ea68b3c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.test.tsx @@ -13,6 +13,7 @@ import { esFilters, FilterManager, UI_SETTINGS, + IndexPattern, } from '../../../../../../../../src/plugins/data/public'; import { SeverityBadge } from '../severity_badge'; @@ -140,11 +141,11 @@ describe('helpers', () => { filterManager: mockFilterManager, query: mockQueryBarWithFilters.query, savedId: mockQueryBarWithFilters.saved_id, - indexPatterns: { + indexPatterns: ({ fields: [{ name: 'event.category', type: 'test type' }], title: 'test title', getFormatterForField: () => ({ convert: (val: unknown) => val }), - }, + } as unknown) as IndexPattern, }); const wrapper = shallow(result[0].description as React.ReactElement); const filterLabelComponent = wrapper.find(esFilters.FilterLabel).at(0); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx index 0ddf4d06fb0fc..366bd156e0c1b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_status/index.tsx @@ -69,7 +69,9 @@ const RuleStatusComponent: React.FC = ({ ruleId, ruleEnabled }) <> - {currentStatus?.status ?? getEmptyTagValue()} + + {currentStatus?.status ?? getEmptyTagValue()} + {currentStatus?.status_date != null && currentStatus?.status != null && ( @@ -84,6 +86,7 @@ const RuleStatusComponent: React.FC = ({ ruleId, ruleEnabled }) )} } > diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx index cb25785eaa5b2..2bdc813639740 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx @@ -7,9 +7,11 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { ThemeProvider } from 'styled-components'; import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; +import { act } from '@testing-library/react'; +import { stubIndexPattern } from 'src/plugins/data/common/index_patterns/index_pattern.stub'; import { StepAboutRule } from '.'; - +import { useFetchIndex } from '../../../../common/containers/source'; import { mockAboutStepRule } from '../../../pages/detection_engine/rules/all/__mocks__/mock'; import { StepRuleDescription } from '../description_step'; import { stepAboutDefaultValue } from './default_value'; @@ -20,19 +22,19 @@ import { } from '../../../pages/detection_engine/rules/types'; import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules/helpers'; +jest.mock('../../../../common/containers/source'); const theme = () => ({ eui: euiDarkVars, darkMode: true }); - -/* eslint-disable no-console */ -// Silence until enzyme fixed to use ReactTestUtils.act() -const originalError = console.error; -beforeAll(() => { - console.error = jest.fn(); -}); -afterAll(() => { - console.error = originalError; +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + // eslint-disable-next-line react/display-name, @typescript-eslint/no-explicit-any + EuiFieldText: (props: any) => { + const { isInvalid, isLoading, fullWidth, inputRef, isDisabled, ...validInputProps } = props; + return ; + }, + }; }); -/* eslint-enable no-console */ - describe('StepAboutRuleComponent', () => { let formHook: RuleStepsFormHooks[RuleStep.aboutRule] | null = null; const setFormHook = ( @@ -44,9 +46,15 @@ describe('StepAboutRuleComponent', () => { beforeEach(() => { formHook = null; + (useFetchIndex as jest.Mock).mockImplementation(() => [ + false, + { + indexPatterns: stubIndexPattern, + }, + ]); }); - test('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { + it('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { const wrapper = shallow( { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') - .first() - .simulate('change', { target: { value: 'Test name text' } }); - - const result = await formHook(); - expect(result?.isValid).toEqual(false); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') + .first() + .simulate('change', { target: { value: 'Test name text' } }); + + const result = await formHook(); + expect(result?.isValid).toEqual(false); + }); }); it('is invalid if no "name" is present', async () => { @@ -101,17 +110,18 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') - .first() - .simulate('change', { target: { value: 'Test description text' } }); - - const result = await formHook(); - expect(result?.isValid).toEqual(false); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') + .first() + .simulate('change', { target: { value: 'Test description text' } }); + const result = await formHook(); + expect(result?.isValid).toEqual(false); + }); }); it('is valid if both "name" and "description" are present', async () => { @@ -128,10 +138,6 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleDescription"] textarea') .first() @@ -165,12 +171,17 @@ describe('StepAboutRuleComponent', () => { ], }; - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data).toEqual(expected); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data).toEqual(expected); + }); }); - test('it allows user to set the risk score as a number (and not a string)', async () => { + it('it allows user to set the risk score as a number (and not a string)', async () => { const wrapper = mount( { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') .first() @@ -227,9 +234,14 @@ describe('StepAboutRuleComponent', () => { ], }; - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data).toEqual(expected); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data).toEqual(expected); + }); }); it('does not modify the provided risk score until the user changes the severity', async () => { @@ -246,10 +258,6 @@ describe('StepAboutRuleComponent', () => { ); - if (!formHook) { - throw new Error('Form hook not set, but tests depend on it'); - } - wrapper .find('[data-test-subj="detectionEngineStepAboutRuleName"] input') .first() @@ -260,18 +268,23 @@ describe('StepAboutRuleComponent', () => { .first() .simulate('change', { target: { value: 'Test description text' } }); - const result = await formHook(); - expect(result?.isValid).toEqual(true); - expect(result?.data?.riskScore.value).toEqual(21); - - wrapper - .find('[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]') - .last() - .simulate('click'); - wrapper.find('button#medium').simulate('click'); - - const result2 = await formHook(); - expect(result2?.isValid).toEqual(true); - expect(result2?.data?.riskScore.value).toEqual(47); + await act(async () => { + if (!formHook) { + throw new Error('Form hook not set, but tests depend on it'); + } + const result = await formHook(); + expect(result?.isValid).toEqual(true); + expect(result?.data?.riskScore.value).toEqual(21); + + wrapper + .find('[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]') + .last() + .simulate('click'); + wrapper.find('button#medium').simulate('click'); + + const result2 = await formHook(); + expect(result2?.isValid).toEqual(true); + expect(result2?.data?.riskScore.value).toEqual(47); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx index 66f95f5ce15d2..90b70e53a459e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx @@ -39,8 +39,8 @@ import { NextStep } from '../next_step'; import { MarkdownEditorForm } from '../../../../common/components/markdown_editor/eui_form'; import { SeverityField } from '../severity_mapping'; import { RiskScoreField } from '../risk_score_mapping'; -import { useFetchIndexPatterns } from '../../../containers/detection_engine/rules'; import { AutocompleteField } from '../autocomplete_field'; +import { useFetchIndex } from '../../../../common/containers/source'; const CommonUseField = getUseField({ component: Field }); @@ -74,10 +74,8 @@ const StepAboutRuleComponent: FC = ({ }) => { const initialState = defaultValues ?? stepAboutDefaultValue; const [severityValue, setSeverityValue] = useState(initialState.severity.value); - const [{ isLoading: indexPatternLoading, indexPatterns }] = useFetchIndexPatterns( - defineRuleData?.index ?? [], - RuleStep.aboutRule - ); + const [indexPatternLoading, { indexPatterns }] = useFetchIndex(defineRuleData?.index ?? []); + const canUseExceptions = defineRuleData?.ruleType && !isMlRule(defineRuleData.ruleType) && diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 7846f0c406668..dc31db76c3911 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -7,6 +7,8 @@ import { EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; import React, { FC, memo, useCallback, useState, useEffect } from 'react'; import styled from 'styled-components'; +// Prefer importing entire lodash library, e.g. import { get } from "lodash" +// eslint-disable-next-line no-restricted-imports import isEqual from 'lodash/isEqual'; import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; @@ -14,7 +16,6 @@ import { DEFAULT_TIMELINE_TITLE } from '../../../../timelines/components/timelin import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions'; import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license'; -import { useFetchIndexPatterns } from '../../../containers/detection_engine/rules'; import { useMlCapabilities } from '../../../../common/components/ml/hooks/use_ml_capabilities'; import { useUiSetting$ } from '../../../../common/lib/kibana'; import { @@ -48,6 +49,7 @@ import { schema } from './schema'; import * as i18n from './translations'; import { isEqlRule, isThresholdRule } from '../../../../../common/detection_engine/utils'; import { EqlQueryBar } from '../eql_query_bar'; +import { useFetchIndex } from '../../../../common/containers/source'; const CommonUseField = getUseField({ component: Field }); @@ -125,10 +127,7 @@ const StepDefineRuleComponent: FC = ({ }) as unknown) as [Partial]; const index = formIndex || initialState.index; const ruleType = formRuleType || initialState.ruleType; - const [{ browserFields, indexPatterns, isLoading: indexPatternsLoading }] = useFetchIndexPatterns( - index, - RuleStep.defineRule - ); + const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(index); // reset form when rule type changes useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx index e1a29c3575d95..00e108ffb89b6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.tsx @@ -169,22 +169,19 @@ export const useUserInfo = (): State => { if (loading !== privilegeLoading || indexNameLoading) { dispatch({ type: 'updateLoading', loading: privilegeLoading || indexNameLoading }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, privilegeLoading, indexNameLoading]); + }, [dispatch, loading, privilegeLoading, indexNameLoading]); useEffect(() => { if (!loading && hasIndexManage !== hasApiIndexManage && hasApiIndexManage != null) { dispatch({ type: 'updateHasIndexManage', hasIndexManage: hasApiIndexManage }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasIndexManage, hasApiIndexManage]); + }, [dispatch, loading, hasIndexManage, hasApiIndexManage]); useEffect(() => { if (!loading && hasIndexWrite !== hasApiIndexWrite && hasApiIndexWrite != null) { dispatch({ type: 'updateHasIndexWrite', hasIndexWrite: hasApiIndexWrite }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasIndexWrite, hasApiIndexWrite]); + }, [dispatch, loading, hasIndexWrite, hasApiIndexWrite]); useEffect(() => { if ( @@ -194,36 +191,31 @@ export const useUserInfo = (): State => { ) { dispatch({ type: 'updateIsSignalIndexExists', isSignalIndexExists: isApiSignalIndexExists }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, isSignalIndexExists, isApiSignalIndexExists]); + }, [dispatch, loading, isSignalIndexExists, isApiSignalIndexExists]); useEffect(() => { if (!loading && isAuthenticated !== isApiAuthenticated && isApiAuthenticated != null) { dispatch({ type: 'updateIsAuthenticated', isAuthenticated: isApiAuthenticated }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, isAuthenticated, isApiAuthenticated]); + }, [dispatch, loading, isAuthenticated, isApiAuthenticated]); useEffect(() => { if (!loading && hasEncryptionKey !== isApiEncryptionKey && isApiEncryptionKey != null) { dispatch({ type: 'updateHasEncryptionKey', hasEncryptionKey: isApiEncryptionKey }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, hasEncryptionKey, isApiEncryptionKey]); + }, [dispatch, loading, hasEncryptionKey, isApiEncryptionKey]); useEffect(() => { if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, canUserCRUD, capabilitiesCanUserCRUD]); + }, [dispatch, loading, canUserCRUD, capabilitiesCanUserCRUD]); useEffect(() => { if (!loading && signalIndexName !== apiSignalIndexName && apiSignalIndexName != null) { dispatch({ type: 'updateSignalIndexName', signalIndexName: apiSignalIndexName }); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, signalIndexName, apiSignalIndexName]); + }, [dispatch, loading, signalIndexName, apiSignalIndexName]); useEffect(() => { if ( diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx index 591e1c81cd2ad..bae5a237bd124 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.test.tsx @@ -5,7 +5,7 @@ */ import React, { FormEvent } from 'react'; import { mount, ReactWrapper } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { TestProviders } from '../../../common/mock'; import { ValueListsForm } from './form'; @@ -24,7 +24,7 @@ const mockSelectFile:

(container: ReactWrapper

, file: File) => Promise { const fileChange = container.find('EuiFilePicker').prop('onChange'); - act(() => { + await waitFor(() => { if (fileChange) { fileChange(({ item: () => file } as unknown) as FormEvent); } diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.tsx index c35cc612129d5..5baa0ee5569d2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/form.tsx @@ -21,7 +21,7 @@ import { useImportList, ListSchema, Type } from '../../../shared_imports'; import * as i18n from './translations'; import { useKibana } from '../../../common/lib/kibana'; -const options: EuiSelectOption[] = [ +export const listFormOptions: EuiSelectOption[] = [ { value: 'keyword', text: i18n.KEYWORDS_RADIO, @@ -145,7 +145,7 @@ export const ValueListsFormComponent: React.FC = ({ onError { ); expect(container.find('EuiModal')).toHaveLength(0); - container.unmount(); }); it('renders modal if showModal is true', () => { @@ -57,7 +56,6 @@ describe('ValueListsModal', () => { ); expect(container.find('EuiModal')).toHaveLength(1); - container.unmount(); }); it('calls onClose when modal is closed', () => { @@ -71,7 +69,6 @@ describe('ValueListsModal', () => { container.find('button[data-test-subj="value-lists-modal-close-action"]').simulate('click'); expect(onClose).toHaveBeenCalled(); - container.unmount(); }); it('renders ValueListsForm and an EuiTable', () => { @@ -83,29 +80,27 @@ describe('ValueListsModal', () => { expect(container.find('ValueListsForm')).toHaveLength(1); expect(container.find('EuiBasicTable')).toHaveLength(1); - container.unmount(); }); describe('modal table actions', () => { - it('calls exportList when export is clicked', () => { + it('calls exportList when export is clicked', async () => { const container = mount( ); - act(() => { + await waitFor(() => { container .find('button[data-test-subj="action-export-value-list"]') .first() .simulate('click'); - container.unmount(); }); expect(exportList).toHaveBeenCalledWith(expect.objectContaining({ listId: 'some-list-id' })); }); - it('calls deleteList when delete is clicked', () => { + it('calls deleteList when delete is clicked', async () => { const deleteListMock = jest.fn(); (useDeleteList as jest.Mock).mockReturnValue({ start: deleteListMock, @@ -117,12 +112,11 @@ describe('ValueListsModal', () => { ); - act(() => { + await waitFor(() => { container .find('button[data-test-subj="action-delete-value-list"]') .first() .simulate('click'); - container.unmount(); }); expect(deleteListMock).toHaveBeenCalledWith(expect.objectContaining({ id: 'some-list-id' })); diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/table_helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/table_helpers.tsx index bb3a97749a11a..0b9b30aa48f81 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/table_helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/table_helpers.tsx @@ -14,6 +14,7 @@ import { ListSchema } from '../../../../../lists/common/schemas/response'; import { FormattedDate } from '../../../common/components/formatted_date'; import * as i18n from './translations'; import { TableItemCallback, TableProps } from './types'; +import { listFormOptions } from './form'; const AlignedSpinner = styled(EuiLoadingSpinner)` margin: ${({ theme }) => theme.eui.euiSizeXS}; @@ -29,6 +30,16 @@ export const buildColumns = ( name: i18n.COLUMN_FILE_NAME, truncateText: true, }, + { + field: 'type', + name: i18n.COLUMN_TYPE, + width: '15%', + truncateText: true, + render: (type: ListSchema['type']) => { + const option = listFormOptions.find(({ value }) => value === type); + return <>{option ? option.text : type}; + }, + }, { field: 'created_at', name: i18n.COLUMN_UPLOAD_DATE, diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/translations.ts b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/translations.ts index 9beebdfb923dc..992c696121485 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/translations.ts @@ -76,6 +76,13 @@ export const COLUMN_FILE_NAME = i18n.translate( } ); +export const COLUMN_TYPE = i18n.translate( + 'xpack.securitySolution.lists.valueListsTable.typeColumn', + { + defaultMessage: 'Type', + } +); + export const COLUMN_UPLOAD_DATE = i18n.translate( 'xpack.securitySolution.lists.valueListsTable.uploadDateColumn', { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx deleted file mode 100644 index d36c19a6a35c6..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.test.tsx +++ /dev/null @@ -1,475 +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 { renderHook, act } from '@testing-library/react-hooks'; - -import { DEFAULT_INDEX_PATTERN } from '../../../../../common/constants'; -import { useApolloClient } from '../../../../common/utils/apollo_context'; -import { mocksSource } from '../../../../common/containers/source/mock'; - -import { useFetchIndexPatterns, Return } from './fetch_index_patterns'; - -const mockUseApolloClient = useApolloClient as jest.Mock; -jest.mock('../../../../common/utils/apollo_context'); - -describe('useFetchIndexPatterns', () => { - beforeEach(() => { - mockUseApolloClient.mockClear(); - }); - test('happy path', async () => { - await act(async () => { - mockUseApolloClient.mockImplementation(() => ({ - query: () => Promise.resolve(mocksSource[0].result), - })); - const { result, waitForNextUpdate } = renderHook(() => - useFetchIndexPatterns(DEFAULT_INDEX_PATTERN) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual([ - { - browserFields: { - base: { - fields: { - '@timestamp': { - category: 'base', - description: - 'Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', - example: '2016-05-23T08:05:34.853Z', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: '@timestamp', - searchable: true, - type: 'date', - aggregatable: true, - }, - }, - }, - agent: { - fields: { - 'agent.ephemeral_id': { - category: 'agent', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.ephemeral_id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.hostname': { - category: 'agent', - description: null, - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.hostname', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.id': { - category: 'agent', - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'agent.name': { - category: 'agent', - description: - 'Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.', - example: 'foo', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'agent.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - auditd: { - fields: { - 'auditd.data.a0': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a0', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'auditd.data.a1': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a1', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'auditd.data.a2': { - category: 'auditd', - description: null, - example: null, - format: '', - indexes: ['auditbeat'], - name: 'auditd.data.a2', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - client: { - fields: { - 'client.address': { - category: 'client', - description: - 'Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'client.bytes': { - category: 'client', - description: 'Bytes sent from the client to the server.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - 'client.domain': { - category: 'client', - description: 'Client domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'client.geo.country_iso_code': { - category: 'client', - description: 'Country ISO code.', - example: 'CA', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - cloud: { - fields: { - 'cloud.account.id': { - category: 'cloud', - description: - 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: '666777888999', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.account.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'cloud.availability_zone': { - category: 'cloud', - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - container: { - fields: { - 'container.id': { - category: 'container', - description: 'Unique container id.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.id', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'container.image.name': { - category: 'container', - description: 'Name of the image the container was built on.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'container.image.tag': { - category: 'container', - description: 'Container image tag.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'container.image.tag', - searchable: true, - type: 'string', - aggregatable: true, - }, - }, - }, - destination: { - fields: { - 'destination.address': { - category: 'destination', - description: - 'Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the `.address` field. Then it should be duplicated to `.ip` or `.domain`, depending on which one it is.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.address', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'destination.bytes': { - category: 'destination', - description: 'Bytes sent from the destination to the source.', - example: '184', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.bytes', - searchable: true, - type: 'number', - aggregatable: true, - }, - 'destination.domain': { - category: 'destination', - description: 'Destination domain.', - example: null, - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.domain', - searchable: true, - type: 'string', - aggregatable: true, - }, - 'destination.ip': { - aggregatable: true, - category: 'destination', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.ip', - searchable: true, - type: 'ip', - }, - 'destination.port': { - aggregatable: true, - category: 'destination', - description: 'Port of the destination.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'destination.port', - searchable: true, - type: 'long', - }, - }, - }, - source: { - fields: { - 'source.ip': { - aggregatable: true, - category: 'source', - description: - 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.ip', - searchable: true, - type: 'ip', - }, - 'source.port': { - aggregatable: true, - category: 'source', - description: 'Port of the source.', - example: '', - format: '', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], - name: 'source.port', - searchable: true, - type: 'long', - }, - }, - }, - event: { - fields: { - 'event.end': { - aggregatable: true, - category: 'event', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: null, - format: '', - indexes: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - name: 'event.end', - searchable: true, - type: 'date', - }, - }, - }, - }, - isLoading: false, - indices: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - indicesExists: true, - docValueFields: [ - { - field: '@timestamp', - format: 'date_time', - }, - { - field: 'event.end', - format: 'date_time', - }, - ], - indexPatterns: { - fields: [ - { name: '@timestamp', searchable: true, type: 'date', aggregatable: true }, - { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.name', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a0', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a1', searchable: true, type: 'string', aggregatable: true }, - { name: 'auditd.data.a2', searchable: true, type: 'string', aggregatable: true }, - { name: 'client.address', searchable: true, type: 'string', aggregatable: true }, - { name: 'client.bytes', searchable: true, type: 'number', aggregatable: true }, - { name: 'client.domain', searchable: true, type: 'string', aggregatable: true }, - { - name: 'client.geo.country_iso_code', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'cloud.account.id', searchable: true, type: 'string', aggregatable: true }, - { - name: 'cloud.availability_zone', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'container.id', searchable: true, type: 'string', aggregatable: true }, - { - name: 'container.image.name', - searchable: true, - type: 'string', - aggregatable: true, - }, - { name: 'container.image.tag', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.address', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.bytes', searchable: true, type: 'number', aggregatable: true }, - { name: 'destination.domain', searchable: true, type: 'string', aggregatable: true }, - { name: 'destination.ip', searchable: true, type: 'ip', aggregatable: true }, - { name: 'destination.port', searchable: true, type: 'long', aggregatable: true }, - { name: 'source.ip', searchable: true, type: 'ip', aggregatable: true }, - { name: 'source.port', searchable: true, type: 'long', aggregatable: true }, - { name: 'event.end', searchable: true, type: 'date', aggregatable: true }, - ], - title: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - }, - }, - result.current[1], - ]); - }); - }); - - test('unhappy path', async () => { - await act(async () => { - mockUseApolloClient.mockImplementation(() => ({ - query: () => Promise.reject(new Error('Something went wrong')), - })); - const { result, waitForNextUpdate } = renderHook(() => - useFetchIndexPatterns(DEFAULT_INDEX_PATTERN) - ); - - await waitForNextUpdate(); - await waitForNextUpdate(); - - expect(result.current).toEqual([ - { - browserFields: {}, - docValueFields: [], - indexPatterns: { - fields: [], - title: '', - }, - indices: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - indicesExists: false, - isLoading: false, - }, - result.current[1], - ]); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx deleted file mode 100644 index 82c9292af7451..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/fetch_index_patterns.tsx +++ /dev/null @@ -1,132 +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 { isEmpty, get } from 'lodash/fp'; -import { useEffect, useState, Dispatch, SetStateAction } from 'react'; -import deepEqual from 'fast-deep-equal'; - -import { IIndexPattern } from '../../../../../../../../src/plugins/data/public'; -import { - BrowserFields, - getBrowserFields, - getDocValueFields, - getIndexFields, - sourceQuery, - DocValueFields, -} from '../../../../common/containers/source'; -import { errorToToaster, useStateToaster } from '../../../../common/components/toasters'; -import { SourceQuery } from '../../../../graphql/types'; -import { useApolloClient } from '../../../../common/utils/apollo_context'; - -import * as i18n from './translations'; - -interface FetchIndexPatternReturn { - browserFields: BrowserFields; - docValueFields: DocValueFields[]; - isLoading: boolean; - indices: string[]; - indicesExists: boolean; - indexPatterns: IIndexPattern; -} - -export type Return = [FetchIndexPatternReturn, Dispatch>]; - -const DEFAULT_BROWSER_FIELDS = {}; -const DEFAULT_INDEX_PATTERNS = { fields: [], title: '' }; -const DEFAULT_DOC_VALUE_FIELDS: DocValueFields[] = []; - -// Fun fact: When using this hook multiple times within a component (e.g. add_exception_modal & edit_exception_modal), -// the apolloClient will perform queryDeduplication and prevent the first query from executing. A deep compare is not -// performed on `indices`, so another field must be passed to circumvent this. -// For details, see https://github.com/apollographql/react-apollo/issues/2202 -export const useFetchIndexPatterns = ( - defaultIndices: string[] = [], - queryDeduplication?: string -): Return => { - const apolloClient = useApolloClient(); - const [indices, setIndices] = useState(defaultIndices); - - const [state, setState] = useState({ - browserFields: DEFAULT_BROWSER_FIELDS, - docValueFields: DEFAULT_DOC_VALUE_FIELDS, - indices: defaultIndices, - indicesExists: false, - indexPatterns: DEFAULT_INDEX_PATTERNS, - isLoading: false, - }); - - const [, dispatchToaster] = useStateToaster(); - - useEffect(() => { - if (!deepEqual(defaultIndices, indices)) { - setIndices(defaultIndices); - setState((prevState) => ({ ...prevState, indices: defaultIndices })); - } - }, [defaultIndices, indices]); - - useEffect(() => { - let isSubscribed = true; - const abortCtrl = new AbortController(); - - async function fetchIndexPatterns() { - if (apolloClient && !isEmpty(indices)) { - setState((prevState) => ({ ...prevState, isLoading: true })); - apolloClient - .query({ - query: sourceQuery, - fetchPolicy: 'cache-first', - variables: { - sourceId: 'default', - defaultIndex: indices, - ...(queryDeduplication != null ? { queryDeduplication } : {}), - }, - context: { - fetchOptions: { - signal: abortCtrl.signal, - }, - }, - }) - .then( - (result) => { - if (isSubscribed) { - setState({ - browserFields: getBrowserFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - docValueFields: getDocValueFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - indices, - isLoading: false, - indicesExists: get('data.source.status.indicesExist', result), - indexPatterns: getIndexFields( - indices.join(), - get('data.source.status.indexFields', result) - ), - }); - } - }, - (error) => { - if (isSubscribed) { - setState((prevState) => ({ ...prevState, isLoading: false })); - errorToToaster({ title: i18n.RULE_ADD_FAILURE, error, dispatchToaster }); - } - } - ); - } - } - fetchIndexPatterns(); - return () => { - isSubscribed = false; - abortCtrl.abort(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [indices]); - - return [state, setIndices]; -}; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts index a40ab2e487851..930391261ac87 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/index.ts @@ -5,7 +5,6 @@ */ export * from './api'; -export * from './fetch_index_patterns'; export * from './use_update_rule'; export * from './use_create_rule'; export * from './types'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 8c21f6a1e8cb7..0982b5740b893 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { useParams } from 'react-router-dom'; - +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { apolloClientObservable, @@ -20,7 +20,7 @@ import { import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions'; import { DetectionEnginePageComponent } from './detection_engine'; import { useUserData } from '../../components/user_info'; -import { useWithSource } from '../../../common/containers/source'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; import { createStore, State } from '../../../common/store'; import { mockHistory, Router } from '../../../cases/components/__mock__/router'; @@ -34,7 +34,7 @@ jest.mock('../../../common/components/query_bar', () => ({ })); jest.mock('../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../components/user_info'); -jest.mock('../../../common/containers/source'); +jest.mock('../../../common/containers/sourcerer'); jest.mock('../../../common/components/link_to'); jest.mock('../../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ @@ -74,13 +74,13 @@ describe('DetectionEnginePageComponent', () => { beforeAll(() => { (useParams as jest.Mock).mockReturnValue({}); (useUserData as jest.Mock).mockReturnValue([{}]); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, }); }); - it('renders correctly', () => { + it('renders correctly', async () => { const wrapper = mount( @@ -93,7 +93,8 @@ describe('DetectionEnginePageComponent', () => { ); - - expect(wrapper.find('FiltersGlobal').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('FiltersGlobal').exists()).toBe(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 3a3854f145db3..b39cd37521602 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -13,7 +13,6 @@ import { useHistory } from 'react-router-dom'; import { SecurityPageName } from '../../../app/types'; import { TimelineId } from '../../../../common/types/timeline'; import { useGlobalTime } from '../../../common/containers/use_global_time'; -import { useWithSource } from '../../../common/containers/source'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { FiltersGlobal } from '../../../common/components/filters_global'; import { getRulesUrl } from '../../../common/components/link_to/redirect_to_detection_engine'; @@ -46,6 +45,8 @@ import { timelineSelectors } from '../../../timelines/store/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../../timelines/store/timeline/model'; import { buildShowBuildingBlockFilter } from '../../components/alerts_table/default_config'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; export const DetectionEnginePageComponent: React.FC = ({ filters, @@ -117,10 +118,7 @@ export const DetectionEnginePageComponent: React.FC = ({ [setShowBuildingBlockAlerts] ); - const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ - signalIndexName, - ]); - const { indicesExist, indexPattern } = useWithSource('default', indexToAdd); + const { indicesExist, indexPattern } = useSourcererScope(SourcererScopeName.detections); if (isUserAuthenticated != null && !isUserAuthenticated && !loading) { return ( @@ -202,7 +200,6 @@ export const DetectionEnginePageComponent: React.FC = ({ defaultFilters={alertsTableDefaultFilters} showBuildingBlockAlerts={showBuildingBlockAlerts} onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChangedCallback} - signalsIndex={signalIndexName ?? ''} to={to} /> diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 9f486dc11e99d..13c6985a30c2b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -6,12 +6,11 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import '../../../../../common/mock/match_media'; +import '../../../../../common/mock/formatted_relative'; import { TestProviders } from '../../../../../common/mock'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { wait as waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import { AllRules } from './index'; jest.mock('react-router-dom', () => { @@ -198,11 +197,9 @@ describe('AllRules', () => { ); - await act(async () => { - await waitFor(() => { - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); - }); + await waitFor(() => { + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); }); }); @@ -226,12 +223,10 @@ describe('AllRules', () => { const monitoringTab = wrapper.find('[data-test-subj="allRulesTableTab-monitoring"] button'); monitoringTab.simulate('click'); - await act(async () => { - await waitFor(() => { - wrapper.update(); - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeTruthy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeFalsy(); - }); + await waitFor(() => { + wrapper.update(); + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeTruthy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx index f8f9da78b2a06..afa4777e74856 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { mount } from 'enzyme'; +import { waitFor } from '@testing-library/react'; import '../../../../../common/mock/match_media'; import { @@ -20,7 +21,7 @@ import { RuleDetailsPageComponent } from './index'; import { createStore, State } from '../../../../../common/store'; import { setAbsoluteRangeDatePicker } from '../../../../../common/store/inputs/actions'; import { useUserData } from '../../../../components/user_info'; -import { useWithSource } from '../../../../../common/containers/source'; +import { useSourcererScope } from '../../../../../common/containers/sourcerer'; import { useParams } from 'react-router-dom'; import { mockHistory, Router } from '../../../../../cases/components/__mock__/router'; @@ -35,7 +36,7 @@ jest.mock('../../../../../common/components/query_bar', () => ({ jest.mock('../../../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); -jest.mock('../../../../../common/containers/source'); +jest.mock('../../../../../common/containers/sourcerer'); jest.mock('../../../../../common/containers/use_global_time', () => ({ useGlobalTime: jest.fn().mockReturnValue({ from: '2020-07-07T08:20:18.966Z', @@ -71,13 +72,13 @@ describe('RuleDetailsPageComponent', () => { beforeAll(() => { (useUserData as jest.Mock).mockReturnValue([{}]); (useParams as jest.Mock).mockReturnValue({}); - (useWithSource as jest.Mock).mockReturnValue({ + (useSourcererScope as jest.Mock).mockReturnValue({ indicesExist: true, indexPattern: {}, }); }); - it('renders correctly', () => { + it('renders correctly', async () => { const wrapper = mount( @@ -93,7 +94,8 @@ describe('RuleDetailsPageComponent', () => { wrappingComponent: TestProviders, } ); - - expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); + await waitFor(() => { + expect(wrapper.find('[data-test-subj="header-page-title"]').exists()).toBe(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 68799f46eee57..ad8ab3ed3a148 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -36,10 +36,7 @@ import { SiemSearchBar } from '../../../../../common/components/search_bar'; import { WrapperPage } from '../../../../../common/components/wrapper_page'; import { Rule } from '../../../../containers/detection_engine/rules'; import { useListsConfig } from '../../../../containers/detection_engine/lists/use_lists_config'; - -import { useWithSource } from '../../../../../common/containers/source'; import { SpyRoute } from '../../../../../common/utils/route/spy_routes'; - import { StepAboutRuleToggleDetails } from '../../../../components/rules/step_about_rule_details'; import { DetectionEngineHeaderPage } from '../../../../components/detection_engine_header_page'; import { AlertsHistogramPanel } from '../../../../components/alerts_histogram_panel'; @@ -89,6 +86,9 @@ import { showGlobalFilters } from '../../../../../timelines/components/timeline/ import { timelineSelectors } from '../../../../../timelines/store/timeline'; import { timelineDefaults } from '../../../../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../../../../timelines/store/timeline/model'; +import { useSourcererScope } from '../../../../../common/containers/sourcerer'; +import { SourcererScopeName } from '../../../../../common/store/sourcerer/model'; +import { AlertsHistogramOption } from '../../../../components/alerts_histogram_panel/types'; enum RuleDetailTabs { alerts = 'alerts', @@ -265,10 +265,6 @@ export const RuleDetailsPageComponent: FC = ({ [rule, ruleDetailTab] ); - const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ - signalIndexName, - ]); - const updateDateRangeCallback = useCallback( ({ x }) => { if (!x) { @@ -308,7 +304,7 @@ export const RuleDetailsPageComponent: FC = ({ [setShowBuildingBlockAlerts] ); - const { indicesExist, indexPattern } = useWithSource('default', indexToAdd); + const { indicesExist, indexPattern } = useSourcererScope(SourcererScopeName.detections); const exceptionLists = useMemo((): { lists: ExceptionIdentifiers[]; @@ -350,6 +346,11 @@ export const RuleDetailsPageComponent: FC = ({ return null; } + const defaultRuleStackByOption: AlertsHistogramOption = { + text: 'event.category', + value: 'event.category', + }; + return ( <> {hasIndexWrite != null && !hasIndexWrite && } @@ -485,6 +486,7 @@ export const RuleDetailsPageComponent: FC = ({ signalIndexName={signalIndexName} setQuery={setQuery} stackByOptions={alertsHistogramOptions} + defaultStackByOption={defaultRuleStackByOption} to={to} updateDateRange={updateDateRangeCallback} /> @@ -500,7 +502,6 @@ export const RuleDetailsPageComponent: FC = ({ loading={loading} showBuildingBlockAlerts={showBuildingBlockAlerts} onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChangedCallback} - signalsIndex={signalIndexName ?? ''} to={to} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index f11b0ac4ec3f8..8545e5da512bb 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import moment from 'moment'; import { GetStepsData, getDefineStepsData, @@ -31,6 +31,8 @@ import { } from './types'; describe('rule helpers', () => { + // @ts-ignore + moment.suppressDeprecationWarnings = true; describe('getStepsData', () => { test('returns object with about, define, schedule and actions step properties formatted', () => { const { diff --git a/x-pack/plugins/security_solution/public/graphql/introspection.json b/x-pack/plugins/security_solution/public/graphql/introspection.json index b32083fec1b5e..8d780137b847c 100644 --- a/x-pack/plugins/security_solution/public/graphql/introspection.json +++ b/x-pack/plugins/security_solution/public/graphql/introspection.json @@ -683,9 +683,15 @@ "deprecationReason": null }, { - "name": "Authentications", - "description": "Gets Authentication success and failures based on a timerange", + "name": "Hosts", + "description": "Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified", "args": [ + { + "name": "id", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, { "name": "timerange", "description": "", @@ -710,6 +716,16 @@ }, "defaultValue": null }, + { + "name": "sort", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "HostsSortField", "ofType": null } + }, + "defaultValue": null + }, { "name": "filterQuery", "description": "", @@ -760,65 +776,41 @@ "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "AuthenticationsData", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "HostsData", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "Timeline", + "name": "HostOverview", "description": "", "args": [ { - "name": "pagination", + "name": "id", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "PaginationInput", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "defaultValue": null }, { - "name": "sortField", + "name": "hostName", "description": "", "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "SortField", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } }, "defaultValue": null }, { - "name": "fieldRequested", + "name": "timerange", "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 } - } - } + "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } }, "defaultValue": null }, - { - "name": "timerange", - "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, { "name": "defaultIndex", "description": "", @@ -836,54 +828,28 @@ } }, "defaultValue": null - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null } ], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineData", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "HostItem", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "TimelineDetails", + "name": "HostFirstLastSeen", "description": "", "args": [ { - "name": "eventId", + "name": "id", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "defaultValue": null }, { - "name": "indexName", + "name": "hostName", "description": "", "type": { "kind": "NON_NULL", @@ -936,41 +902,140 @@ "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineDetailsData", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "FirstLastSeenHost", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SourceConfiguration", + "description": "A set of configuration options for a security data source", + "fields": [ + { + "name": "fields", + "description": "The field mapping to use for this source", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "SourceFields", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SourceFields", + "description": "A mapping of semantic fields to their document counterparts", + "fields": [ + { + "name": "container", + "description": "The field to identify a container by", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "LastEventTime", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "indexKey", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "LastEventIndexKey", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "details", - "description": "", - "type": { + "name": "host", + "description": "The fields to identify a host by", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "The fields that may contain the log event message. The first field found win.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "LastTimeDetails", "ofType": null } - }, - "defaultValue": null - }, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pod", + "description": "The field to identify a pod by", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "tiebreaker", + "description": "The field to use as a tiebreaker for log events that have identical timestamps", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "timestamp", + "description": "The field to use as a timestamp for metrics and logs", + "args": [], + "type": { + "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": "SourceStatus", + "description": "The status of an infrastructure data source", + "fields": [ + { + "name": "indicesExist", + "description": "Whether the configured alias or wildcard pattern resolve to any auditbeat indices", + "args": [ { "name": "defaultIndex", "description": "", @@ -988,9 +1053,22 @@ } }, "defaultValue": null - }, + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "indexFields", + "description": "The list of fields defined in the index mappings", + "args": [ { - "name": "docValueFields", + "name": "defaultIndex", "description": "", "type": { "kind": "NON_NULL", @@ -1001,11 +1079,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } }, @@ -1015,4215 +1089,16 @@ "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "LastEventTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "Hosts", - "description": "Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "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": "HostsSortField", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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 - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostsData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "HostOverview", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "hostName", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "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": "HostItem", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "HostFirstLastSeen", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "hostName", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "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 - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "FirstLastSeenHost", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "IpOverview", - "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": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "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 - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "type": { "kind": "OBJECT", "name": "IpOverviewData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "Users", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "ip", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "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": "UsersSortField", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "flowTarget", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "FlowTarget", "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": "UsersData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "KpiNetwork", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "OBJECT", "name": "KpiNetworkData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "KpiHosts", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "KpiHostsData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "KpiHostDetails", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "KpiHostDetailsData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "MatrixHistogram", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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 - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "histogramType", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "HistogramType", "ofType": null } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "MatrixHistogramOverTimeData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "NetworkTopCountries", - "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": "flowTarget", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "FlowTargetSourceDest", "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": "NetworkTopTablesSortField", - "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": "NetworkTopCountriesData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "NetworkTopNFlow", - "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": "flowTarget", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "FlowTargetSourceDest", "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": "NetworkTopTablesSortField", - "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": "NetworkTopNFlowData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "NetworkDns", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "isPtrIncluded", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "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": "NetworkDnsSortField", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "NetworkDnsData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "NetworkDnsHistogram", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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 - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "stackByField", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "docValueFields", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkDsOverTimeData", "ofType": null } - }, - "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": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "OBJECT", "name": "OverviewNetworkData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "OverviewHost", - "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "OBJECT", "name": "OverviewHostData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "Tls", - "description": "", - "args": [ - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "ip", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "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": "TlsSortField", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "flowTarget", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "FlowTargetSourceDest", "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": "TlsData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "UncommonProcesses", - "description": "Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified", - "args": [ - { - "name": "timerange", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "pagination", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "PaginationInputPaginated", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "filterQuery", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "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": "UncommonProcessesData", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "whoAmI", - "description": "Just a simple example to get the app name", - "args": [], - "type": { "kind": "OBJECT", "name": "SayMyName", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SourceConfiguration", - "description": "A set of configuration options for a security data source", - "fields": [ - { - "name": "fields", - "description": "The field mapping to use for this source", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "SourceFields", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SourceFields", - "description": "A mapping of semantic fields to their document counterparts", - "fields": [ - { - "name": "container", - "description": "The field to identify a container by", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "host", - "description": "The fields to identify a host by", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": "The fields that may contain the log event message. The first field found win.", - "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": "pod", - "description": "The field to identify a pod by", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tiebreaker", - "description": "The field to use as a tiebreaker for log events that have identical timestamps", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timestamp", - "description": "The field to use as a timestamp for metrics and logs", - "args": [], - "type": { - "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": "SourceStatus", - "description": "The status of an infrastructure data source", - "fields": [ - { - "name": "indicesExist", - "description": "Whether the configured alias or wildcard pattern resolve to any auditbeat indices", - "args": [ - { - "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": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "indexFields", - "description": "The list of fields defined in the index mappings", - "args": [ - { - "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": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "IndexField", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Boolean", - "description": "The `Boolean` scalar type represents `true` or `false`.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "IndexField", - "description": "A descriptor of a field in an index", - "fields": [ - { - "name": "category", - "description": "Where the field belong", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "example", - "description": "Example of field's value", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "indexes", - "description": "whether the field's belong to an alias index", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "The name of the field", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "The type of the field's values as recognized by Kibana", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "searchable", - "description": "Whether the field's values can be efficiently searched for", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "aggregatable", - "description": "Whether the field's values can be aggregated", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": "Description of the field", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "format", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "esTypes", - "description": "the elastic type as mapped in the index", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArrayNoNullable", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subType", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToIFieldSubTypeNonNullable", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToStringArrayNoNullable", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToIFieldSubTypeNonNullable", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "TimerangeInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "interval", - "description": "The interval string to use for last bucket. The format is '{value}{unit}'. For example '5m' would return the metrics for the last 5 minutes of the timespan.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "to", - "description": "The end of the timerange", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "from", - "description": "The beginning of the timerange", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "PaginationInputPaginated", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "activePage", - "description": "The activePage parameter defines the page of results you want to fetch", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "cursorStart", - "description": "The cursorStart parameter defines the start of the results to be displayed", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "fakePossibleCount", - "description": "The fakePossibleCount parameter determines the total count in order to show 5 additional pages", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "querySize", - "description": "The querySize parameter is the number of items to be returned", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "docValueFieldsInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "format", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AuthenticationsData", - "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": "AuthenticationsEdges", "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": "AuthenticationsEdges", - "description": "", - "fields": [ - { - "name": "node", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AuthenticationItem", "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": "AuthenticationItem", - "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "failures", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "successes", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UserEcsFields", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSuccess", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "LastSourceHost", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastFailure", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "LastSourceHost", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UserEcsFields", - "description": "", - "fields": [ - { - "name": "domain", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "full_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "email", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "hash", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "group", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToStringArray", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "LastSourceHost", - "description": "", - "fields": [ - { - "name": "timestamp", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "source", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SourceEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "host", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Date", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SourceEcsFields", - "description": "", - "fields": [ - { - "name": "bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ip", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "port", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "domain", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "geo", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "packets", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToNumberArray", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "GeoEcsFields", - "description": "", - "fields": [ - { - "name": "city_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "continent_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "country_iso_code", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "country_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "location", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Location", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "region_iso_code", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "region_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Location", - "description": "", - "fields": [ - { - "name": "lon", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lat", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HostEcsFields", - "description": "", - "fields": [ - { - "name": "architecture", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ip", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "mac", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "os", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "OsEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "OsEcsFields", - "description": "", - "fields": [ - { - "name": "platform", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "full", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "family", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "version", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "kernel", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CursorType", - "description": "", - "fields": [ - { - "name": "value", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tiebreaker", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "PageInfoPaginated", - "description": "", - "fields": [ - { - "name": "activePage", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "fakeTotalCount", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "showMorePagesIndicator", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Inspect", - "description": "", - "fields": [ - { - "name": "dsl", - "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": "response", - "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": "INPUT_OBJECT", - "name": "PaginationInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "limit", - "description": "The limit parameter allows you to configure the maximum amount of items to be returned", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "cursor", - "description": "The cursor parameter defines the next result you want to fetch", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "tiebreaker", - "description": "The tiebreaker parameter allow to be more precise to fetch the next item", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "SortField", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "sortFieldId", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "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": "TimelineData", - "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": "TimelineEdges", "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": "PageInfo", "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": "TimelineEdges", - "description": "", - "fields": [ - { - "name": "node", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineItem", "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": "TimelineItem", - "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "_index", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "data", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineNonEcsData", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ecs", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ECS", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TimelineNonEcsData", - "description": "", - "fields": [ - { - "name": "field", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "value", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ECS", - "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "_index", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "agent", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AgentEcsField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "auditd", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AuditdEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "destination", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "DestinationEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "dns", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "DnsEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endgame", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "EndgameEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "event", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "EventEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "geo", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "host", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "network", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "NetworkEcsField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "rule", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "RuleEcsField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "signal", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SignalField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "source", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SourceEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "suricata", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SuricataEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tls", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TlsEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "zeek", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "http", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "HttpEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "url", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "UrlEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timestamp", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "UserEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "winlog", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "WinlogEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "process", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ProcessEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "file", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "FileFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "system", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SystemEcsField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AgentEcsField", - "description": "", - "fields": [ - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AuditdEcsFields", - "description": "", - "fields": [ - { - "name": "result", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "session", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "data", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AuditdData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "summary", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Summary", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "sequence", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AuditdData", - "description": "", - "fields": [ - { - "name": "acct", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "terminal", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "op", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Summary", - "description": "", - "fields": [ - { - "name": "actor", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "PrimarySecondary", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "object", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "PrimarySecondary", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "how", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message_type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "sequence", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "PrimarySecondary", - "description": "", - "fields": [ - { - "name": "primary", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "secondary", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DestinationEcsFields", - "description": "", - "fields": [ - { - "name": "bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ip", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "port", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "domain", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "geo", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "packets", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DnsEcsFields", - "description": "", - "fields": [ - { - "name": "question", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "DnsQuestionData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "resolved_ip", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "response_code", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DnsQuestionData", - "description": "", - "fields": [ - { - "name": "name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EndgameEcsFields", - "description": "", - "fields": [ - { - "name": "exit_code", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "file_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "file_path", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "logon_type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "parent_process_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pid", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "process_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subject_domain_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subject_logon_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subject_user_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "target_domain_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "target_logon_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "target_user_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EventEcsFields", - "description": "", - "fields": [ - { - "name": "action", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "category", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "code", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "dataset", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "duration", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "end", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "hash", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "kind", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "module", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "original", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "outcome", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "risk_score", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "risk_score_norm", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "severity", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "start", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timezone", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToDateArray", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "NetworkEcsField", - "description": "", - "fields": [ - { - "name": "bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "community_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "direction", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "packets", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "protocol", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "transport", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RuleEcsField", - "description": "", - "fields": [ - { - "name": "reference", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SignalField", - "description": "", - "fields": [ - { - "name": "rule", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "RuleField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "original_time", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "status", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RuleField", - "description": "", - "fields": [ - { - "name": "id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "rule_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "false_positives", - "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": "saved_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timeline_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timeline_title", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "max_signals", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "risk_score", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "output_index", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "from", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "immutable", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "index", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "interval", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "language", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "query", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "references", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "severity", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tags", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "threat", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "size", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "to", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "enabled", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "filters", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_by", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_by", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "version", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "note", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "threshold", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "exceptions_list", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToBooleanArray", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ToAny", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuricataEcsFields", - "description": "", - "fields": [ - { - "name": "eve", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SuricataEveData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuricataEveData", - "description": "", - "fields": [ - { - "name": "alert", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "SuricataAlertData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "flow_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "proto", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuricataAlertData", - "description": "", - "fields": [ - { - "name": "signature", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "signature_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TlsEcsFields", - "description": "", - "fields": [ - { - "name": "client_certificate", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TlsClientCertificateData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "fingerprints", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TlsFingerprintsData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "server_certificate", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TlsServerCertificateData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TlsClientCertificateData", - "description": "", - "fields": [ - { - "name": "fingerprint", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "FingerprintData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FingerprintData", - "description": "", - "fields": [ - { - "name": "sha1", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TlsFingerprintsData", - "description": "", - "fields": [ - { - "name": "ja3", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TlsJa3Data", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TlsJa3Data", - "description": "", - "fields": [ - { - "name": "hash", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, "isDeprecated": false, "deprecationReason": null } @@ -5234,350 +1109,246 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "TlsServerCertificateData", - "description": "", - "fields": [ - { - "name": "fingerprint", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "FingerprintData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], + "kind": "SCALAR", + "name": "Boolean", + "description": "The `Boolean` scalar type represents `true` or `false`.", + "fields": null, "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "ZeekEcsFields", + "kind": "INPUT_OBJECT", + "name": "TimerangeInput", "description": "", - "fields": [ - { - "name": "session_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "connection", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekConnectionData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "notice", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekNoticeData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "dns", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekDnsData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": [ { - "name": "http", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekHttpData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "interval", + "description": "The interval string to use for last bucket. The format is '{value}{unit}'. For example '5m' would return the metrics for the last 5 minutes of the timespan.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null }, { - "name": "files", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekFileData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "to", + "description": "The end of the timerange", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null }, { - "name": "ssl", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ZeekSslData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "from", + "description": "The beginning of the timerange", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "ZeekConnectionData", + "kind": "INPUT_OBJECT", + "name": "PaginationInputPaginated", "description": "", - "fields": [ - { - "name": "local_resp", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": [ { - "name": "local_orig", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "activePage", + "description": "The activePage parameter defines the page of results you want to fetch", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "defaultValue": null }, { - "name": "missed_bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "cursorStart", + "description": "The cursorStart parameter defines the start of the results to be displayed", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "defaultValue": null }, { - "name": "state", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "fakePossibleCount", + "description": "The fakePossibleCount parameter determines the total count in order to show 5 additional pages", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "defaultValue": null }, { - "name": "history", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "querySize", + "description": "The querySize parameter is the number of items to be returned", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "ZeekNoticeData", + "kind": "INPUT_OBJECT", + "name": "HostsSortField", "description": "", - "fields": [ - { - "name": "suppress_for", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "msg", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "note", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "sub", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "dst", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": [ { - "name": "dropped", + "name": "field", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "HostsFields", "ofType": null } + }, + "defaultValue": null }, { - "name": "peer_descr", + "name": "direction", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "ZeekDnsData", + "kind": "ENUM", + "name": "HostsFields", "description": "", - "fields": [ - { - "name": "AA", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "qclass_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "RD", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "qtype_name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "rejected", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "qtype", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "query", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "trans_id", + "name": "hostName", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "qclass", + "name": "lastSeen", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "docValueFieldsInput", + "description": "", + "fields": null, + "inputFields": [ { - "name": "RA", + "name": "field", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null }, { - "name": "TC", + "name": "format", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "ZeekHttpData", + "name": "HostsData", "description": "", "fields": [ { - "name": "resp_mime_types", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "trans_depth", + "name": "edges", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "HostsEdges", "ofType": null } + } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "status_msg", + "name": "totalCount", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "resp_fuids", + "name": "pageInfo", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "tags", + "name": "inspect", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -5589,150 +1360,221 @@ }, { "kind": "OBJECT", - "name": "ZeekFileData", + "name": "HostsEdges", "description": "", "fields": [ { - "name": "session_ids", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timedout", + "name": "node", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "HostItem", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "local_orig", + "name": "cursor", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "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": "HostItem", + "description": "", + "fields": [ { - "name": "tx_host", + "name": "_id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "source", + "name": "cloud", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "is_orig", + "name": "endpoint", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "EndpointFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "overflow_bytes", + "name": "host", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "sha1", + "name": "inspect", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "duration", + "name": "lastSeen", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloudFields", + "description": "", + "fields": [ { - "name": "depth", + "name": "instance", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "CloudInstance", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "analyzers", + "name": "machine", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "CloudMachine", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "mime_type", + "name": "provider", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "rx_host", + "name": "region", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloudInstance", + "description": "", + "fields": [ { - "name": "total_bytes", + "name": "id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloudMachine", + "description": "", + "fields": [ { - "name": "fuid", + "name": "type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "EndpointFields", + "description": "", + "fields": [ { - "name": "seen_bytes", + "name": "endpointPolicy", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "missing_bytes", + "name": "sensorVersion", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "md5", + "name": "policyStatus", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "ENUM", "name": "HostPolicyResponseActionStatus", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -5743,55 +1585,36 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "ZeekSslData", + "kind": "ENUM", + "name": "HostPolicyResponseActionStatus", "description": "", - "fields": [ - { - "name": "cipher", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "established", + "name": "success", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "resumed", + "name": "failure", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { - "name": "version", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, + { "name": "warning", "description": "", "isDeprecated": false, "deprecationReason": null } + ], "possibleTypes": null }, { "kind": "OBJECT", - "name": "HttpEcsFields", + "name": "HostEcsFields", "description": "", "fields": [ { - "name": "version", + "name": "architecture", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5799,34 +1622,23 @@ "deprecationReason": null }, { - "name": "request", + "name": "id", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "HttpRequestData", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "response", + "name": "ip", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "HttpResponseData", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HttpRequestData", - "description": "", - "fields": [ + }, { - "name": "method", + "name": "mac", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5834,26 +1646,26 @@ "deprecationReason": null }, { - "name": "body", + "name": "name", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "HttpBodyData", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "referrer", + "name": "os", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "OsEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "bytes", + "name": "type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -5864,74 +1676,38 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "HttpBodyData", + "kind": "SCALAR", + "name": "ToStringArray", "description": "", - "fields": [ - { - "name": "content", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], + "fields": null, "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "HttpResponseData", + "name": "OsEcsFields", "description": "", "fields": [ { - "name": "status_code", + "name": "platform", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "body", + "name": "name", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "HttpBodyData", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "bytes", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UrlEcsFields", - "description": "", - "fields": [ - { - "name": "domain", + "name": "full", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5939,7 +1715,7 @@ "deprecationReason": null }, { - "name": "original", + "name": "family", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5947,7 +1723,7 @@ "deprecationReason": null }, { - "name": "username", + "name": "version", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5955,7 +1731,7 @@ "deprecationReason": null }, { - "name": "password", + "name": "kernel", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, @@ -5970,14 +1746,46 @@ }, { "kind": "OBJECT", - "name": "WinlogEcsFields", + "name": "Inspect", "description": "", "fields": [ { - "name": "event_id", + "name": "dsl", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "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": "response", + "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 } @@ -5987,88 +1795,116 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "SCALAR", + "name": "Date", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", - "name": "ProcessEcsFields", + "name": "CursorType", "description": "", "fields": [ { - "name": "hash", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "ProcessHashData", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pid", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", + "name": "value", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ppid", + "name": "tiebreaker", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "PageInfoPaginated", + "description": "", + "fields": [ { - "name": "args", + "name": "activePage", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "entity_id", + "name": "fakeTotalCount", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "executable", + "name": "showMorePagesIndicator", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "FirstLastSeenHost", + "description": "", + "fields": [ { - "name": "title", + "name": "inspect", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "thread", + "name": "firstSeen", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Thread", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "working_directory", + "name": "lastSeen", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6080,285 +1916,322 @@ }, { "kind": "OBJECT", - "name": "ProcessHashData", + "name": "TimelineResult", "description": "", "fields": [ { - "name": "md5", + "name": "columns", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "ColumnHeaderResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "sha1", + "name": "created", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "sha256", + "name": "createdBy", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Thread", - "description": "", - "fields": [ + }, { - "name": "id", + "name": "dataProviders", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "DataProviderResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "start", + "name": "dateRange", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "DateRangePickerResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FileFields", - "description": "", - "fields": [ + }, { - "name": "name", + "name": "description", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "path", + "name": "eventIdToNoteIds", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "target_path", + "name": "eventType", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "extension", + "name": "excludedRowRendererIds", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "RowRendererId", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", + "name": "favorite", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "FavoriteTimelineResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "device", + "name": "filters", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "FilterTimelineResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inode", + "name": "kqlMode", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "uid", + "name": "kqlQuery", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "OBJECT", "name": "SerializedFilterQueryResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "owner", + "name": "indexNames", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "gid", + "name": "notes", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "group", + "name": "noteIds", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "mode", + "name": "pinnedEventIds", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "size", + "name": "pinnedEventsSaveObject", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "PinnedEvent", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "mtime", + "name": "savedQueryId", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ctime", + "name": "savedObjectId", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, + "type": { + "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": "SystemEcsField", - "description": "", - "fields": [ + }, { - "name": "audit", + "name": "sort", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "AuditEcsFields", "ofType": null }, + "type": { "kind": "OBJECT", "name": "SortTimelineResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "auth", + "name": "status", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "AuthEcsFields", "ofType": null }, + "type": { "kind": "ENUM", "name": "TimelineStatus", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AuditEcsFields", - "description": "", - "fields": [ + }, { - "name": "package", + "name": "title", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "PackageEcsFields", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "PackageEcsFields", - "description": "", - "fields": [ + }, { - "name": "arch", + "name": "templateTimelineId", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "entity_id", + "name": "templateTimelineVersion", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "name", + "name": "timelineType", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "size", + "name": "updated", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "summary", + "name": "updatedBy", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, @@ -6366,7 +2239,11 @@ "name": "version", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null } @@ -6378,84 +2255,51 @@ }, { "kind": "OBJECT", - "name": "AuthEcsFields", + "name": "ColumnHeaderResult", "description": "", "fields": [ { - "name": "ssh", + "name": "aggregatable", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "SshEcsFields", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SshEcsFields", - "description": "", - "fields": [ + }, { - "name": "method", + "name": "category", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "signature", + "name": "columnHeaderType", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "PageInfo", - "description": "", - "fields": [ + }, { - "name": "endCursor", + "name": "description", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "CursorType", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "hasNextPage", + "name": "example", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TimelineDetailsData", - "description": "", - "fields": [ + }, { - "name": "data", + "name": "indexes", "description": "", "args": [], "type": { @@ -6464,56 +2308,49 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "DetailItem", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "id", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DetailItem", - "description": "", - "fields": [ + }, { - "name": "field", + "name": "name", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "values", + "name": "placeholder", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "searchable", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "originalValue", + "name": "type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "EsValue", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6524,198 +2361,130 @@ "possibleTypes": null }, { - "kind": "SCALAR", - "name": "EsValue", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "LastEventIndexKey", + "kind": "OBJECT", + "name": "DataProviderResult", "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "fields": [ { - "name": "hostDetails", + "name": "id", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { "name": "hosts", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "ipDetails", + "name": "name", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { "name": "network", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "LastTimeDetails", - "description": "", - "fields": null, - "inputFields": [ { - "name": "hostName", + "name": "enabled", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "ip", + "name": "excluded", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "LastEventTimeData", - "description": "", - "fields": [ + "args": [], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { - "name": "lastSeen", + "name": "kqlQuery", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "queryMatch", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "OBJECT", "name": "QueryMatchResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "HostsSortField", - "description": "", - "fields": null, - "inputFields": [ + }, { - "name": "field", + "name": "type", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "HostsFields", "ofType": null } - }, - "defaultValue": null + "args": [], + "type": { "kind": "ENUM", "name": "DataProviderType", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "direction", + "name": "and", "description": "", + "args": [], "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "DataProviderResult", "ofType": null } + } }, - "defaultValue": null + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "ENUM", - "name": "HostsFields", + "kind": "OBJECT", + "name": "QueryMatchResult", "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "fields": [ { - "name": "hostName", + "name": "field", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "lastSeen", - "description": "", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "HostsData", - "description": "", - "fields": [ - { - "name": "edges", + "name": "displayField", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostsEdges", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", + "name": "value", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", + "name": "displayValue", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "operator", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6725,32 +2494,47 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "ENUM", + "name": "DataProviderType", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "default", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "template", + "description": "", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "OBJECT", - "name": "HostsEdges", + "name": "DateRangePickerResult", "description": "", "fields": [ { - "name": "node", + "name": "start", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostItem", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "cursor", + "name": "end", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6761,106 +2545,110 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "HostItem", + "kind": "SCALAR", + "name": "ToAny", "description": "", - "fields": [ + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "RowRendererId", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { "name": "auditd", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "_id", + "name": "auditd_file", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "cloud", + "name": "netflow", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "CloudFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, + { "name": "plain", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "endpoint", + "name": "suricata", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "EndpointFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, + { "name": "system", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "host", + "name": "system_dns", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "system_endgame_process", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "lastSeen", + "name": "system_file", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } + }, + { + "name": "system_fim", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "system_security_event", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "system_socket", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "zeek", "description": "", "isDeprecated": false, "deprecationReason": null } ], - "inputFields": null, - "interfaces": [], - "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "CloudFields", + "name": "FavoriteTimelineResult", "description": "", "fields": [ { - "name": "instance", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "CloudInstance", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "machine", + "name": "fullName", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "CloudMachine", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "provider", + "name": "userName", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "region", + "name": "favoriteDate", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6872,57 +2660,27 @@ }, { "kind": "OBJECT", - "name": "CloudInstance", + "name": "FilterTimelineResult", "description": "", "fields": [ { - "name": "id", + "name": "exists", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CloudMachine", - "description": "", - "fields": [ + }, { - "name": "type", + "name": "meta", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "FilterMetaTimelineResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EndpointFields", - "description": "", - "fields": [ + }, { - "name": "endpointPolicy", + "name": "match_all", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, @@ -6930,7 +2688,7 @@ "deprecationReason": null }, { - "name": "sensorVersion", + "name": "missing", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, @@ -6938,10 +2696,26 @@ "deprecationReason": null }, { - "name": "policyStatus", + "name": "query", "description": "", "args": [], - "type": { "kind": "ENUM", "name": "HostPolicyResponseActionStatus", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "range", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "script", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -6952,118 +2726,95 @@ "possibleTypes": null }, { - "kind": "ENUM", - "name": "HostPolicyResponseActionStatus", + "kind": "OBJECT", + "name": "FilterMetaTimelineResult", "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "fields": [ { - "name": "success", + "name": "alias", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "failure", + "name": "controlledBy", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { "name": "warning", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FirstLastSeenHost", - "description": "", - "fields": [ { - "name": "inspect", + "name": "disabled", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "firstSeen", + "name": "field", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "lastSeen", + "name": "formattedValue", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "IpOverviewData", - "description": "", - "fields": [ + }, { - "name": "client", + "name": "index", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "destination", + "name": "key", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "host", + "name": "negate", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "server", + "name": "params", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "source", + "name": "type", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Overview", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "value", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -7075,46 +2826,41 @@ }, { "kind": "OBJECT", - "name": "Overview", + "name": "SerializedFilterQueryResult", "description": "", "fields": [ { - "name": "firstSeen", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "lastSeen", + "name": "filterQuery", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, + "type": { "kind": "OBJECT", "name": "SerializedKueryQueryResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SerializedKueryQueryResult", + "description": "", + "fields": [ { - "name": "autonomousSystem", + "name": "kuery", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "AutonomousSystem", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "KueryFilterQueryResult", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "geo", + "name": "serializedQuery", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -7126,22 +2872,22 @@ }, { "kind": "OBJECT", - "name": "AutonomousSystem", + "name": "KueryFilterQueryResult", "description": "", "fields": [ { - "name": "number", + "name": "kind", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "organization", + "name": "expression", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "AutonomousSystemOrganization", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -7153,11 +2899,19 @@ }, { "kind": "OBJECT", - "name": "AutonomousSystemOrganization", + "name": "SortTimelineResult", "description": "", "fields": [ { - "name": "name", + "name": "columnId", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "sortDirection", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, @@ -7171,268 +2925,167 @@ "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "UsersSortField", + "kind": "ENUM", + "name": "TimelineStatus", "description": "", "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "UsersFields", "ofType": null } - }, - "defaultValue": null - }, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { "name": "active", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "draft", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "direction", + "name": "immutable", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } - }, - "defaultValue": null + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, - "enumValues": null, "possibleTypes": null }, { - "kind": "ENUM", - "name": "UsersFields", - "description": "", + "kind": "SCALAR", + "name": "Int", + "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", "fields": null, "inputFields": null, "interfaces": null, - "enumValues": [ - { "name": "name", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "count", "description": "", "isDeprecated": false, "deprecationReason": null } - ], + "enumValues": null, "possibleTypes": null }, { "kind": "ENUM", - "name": "FlowTarget", + "name": "TimelineType", "description": "", "fields": null, "inputFields": null, "interfaces": null, "enumValues": [ - { "name": "client", "description": "", "isDeprecated": false, "deprecationReason": null }, - { - "name": "destination", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { "name": "server", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UsersData", - "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": "UsersEdges", "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", + "name": "default", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "template", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, "isDeprecated": false, "deprecationReason": null } ], - "inputFields": null, - "interfaces": [], - "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "UsersEdges", + "kind": "INPUT_OBJECT", + "name": "PageInfoTimeline", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "node", + "name": "pageIndex", "description": "", - "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "UsersNode", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "cursor", + "name": "pageSize", "description": "", - "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "UsersNode", + "kind": "INPUT_OBJECT", + "name": "SortTimeline", "description": "", - "fields": [ - { - "name": "_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": [ { - "name": "timestamp", + "name": "sortField", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "SortFieldTimeline", "ofType": null } + }, + "defaultValue": null }, { - "name": "user", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "UsersItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "name": "sortOrder", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "UsersItem", + "kind": "ENUM", + "name": "SortFieldTimeline", "description": "", - "fields": [ - { - "name": "name", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { "name": "title", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "groupId", + "name": "description", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "groupName", + "name": "updated", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { - "name": "count", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } + { "name": "created", "description": "", "isDeprecated": false, "deprecationReason": null } ], - "inputFields": null, - "interfaces": [], - "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "KpiNetworkData", + "name": "ResponseTimelines", "description": "", "fields": [ { - "name": "networkEvents", + "name": "timeline", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "OBJECT", "name": "TimelineResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "uniqueFlowId", + "name": "totalCount", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7440,7 +3093,7 @@ "deprecationReason": null }, { - "name": "uniqueSourcePrivateIps", + "name": "defaultTimelineCount", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7448,23 +3101,7 @@ "deprecationReason": null }, { - "name": "uniqueSourcePrivateIpsHistogram", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiNetworkHistogramData", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "uniqueDestinationPrivateIps", + "name": "templateTimelineCount", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7472,23 +3109,7 @@ "deprecationReason": null }, { - "name": "uniqueDestinationPrivateIpsHistogram", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiNetworkHistogramData", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "dnsQueries", + "name": "elasticTemplateTimelineCount", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7496,7 +3117,7 @@ "deprecationReason": null }, { - "name": "tlsHandshakes", + "name": "customTemplateTimelineCount", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7504,10 +3125,10 @@ "deprecationReason": null }, { - "name": "inspect", + "name": "favoriteCount", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -7519,177 +3140,308 @@ }, { "kind": "OBJECT", - "name": "KpiNetworkHistogramData", + "name": "Mutation", "description": "", "fields": [ { - "name": "x", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "persistNote", + "description": "Persists a note", + "args": [ + { + "name": "noteId", + "description": "", + "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, + "defaultValue": null + }, + { + "name": "version", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "note", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "NoteInput", "ofType": null } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "ResponseNote", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "y", + "name": "deleteNote", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + } + } + }, + "defaultValue": null + } + ], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "KpiHostsData", - "description": "", - "fields": [ + }, { - "name": "hosts", + "name": "deleteNoteByTimelineId", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "args": [ + { + "name": "timelineId", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "version", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + } + ], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "hostsHistogram", - "description": "", - "args": [], + "name": "persistPinnedEventOnTimeline", + "description": "Persists a pinned event in a timeline", + "args": [ + { + "name": "pinnedEventId", + "description": "", + "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, + "defaultValue": null + }, + { + "name": "eventId", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "timelineId", + "description": "", + "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, + "defaultValue": null + } + ], + "type": { "kind": "OBJECT", "name": "PinnedEvent", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deletePinnedEventOnTimeline", + "description": "Remove a pinned events in a timeline", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + } + } + }, + "defaultValue": null + } + ], "type": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } - } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "authSuccess", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "authSuccessHistogram", - "description": "", - "args": [], + "name": "deleteAllPinnedEventsOnTimeline", + "description": "Remove all pinned events in a timeline", + "args": [ + { + "name": "timelineId", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + }, + "defaultValue": null + } + ], "type": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } - } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "authFailure", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "authFailureHistogram", - "description": "", - "args": [], + "name": "persistTimeline", + "description": "Persists a timeline", + "args": [ + { + "name": "id", + "description": "", + "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, + "defaultValue": null + }, + { + "name": "version", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "timeline", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "TimelineInput", "ofType": null } + }, + "defaultValue": null + } + ], "type": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } - } + "ofType": { "kind": "OBJECT", "name": "ResponseTimeline", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "uniqueSourceIps", + "name": "persistFavorite", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "args": [ + { + "name": "timelineId", + "description": "", + "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "ResponseFavoriteTimeline", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "uniqueSourceIpsHistogram", + "name": "deleteTimeline", "description": "", - "args": [], + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } + } + } + }, + "defaultValue": null + } + ], "type": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } - } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "NoteInput", + "description": "", + "fields": null, + "inputFields": [ { - "name": "uniqueDestinationIps", + "name": "eventId", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "uniqueDestinationIpsHistogram", + "name": "note", "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "inspect", + "name": "timelineId", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "KpiHostHistogramData", + "name": "ResponseNote", "description": "", "fields": [ { - "name": "x", + "name": "code", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, @@ -7697,10 +3449,22 @@ "deprecationReason": null }, { - "name": "y", + "name": "message", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "note", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null } @@ -7711,750 +3475,624 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "KpiHostDetailsData", + "kind": "INPUT_OBJECT", + "name": "TimelineInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "authSuccess", + "name": "columns", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "ColumnHeaderInput", "ofType": null } + } + }, + "defaultValue": null }, { - "name": "authSuccessHistogram", + "name": "dataProviders", "description": "", - "args": [], "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } + "ofType": { "kind": "INPUT_OBJECT", "name": "DataProviderInput", "ofType": null } } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "authFailure", + "name": "description", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "eventType", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "authFailureHistogram", + "name": "excludedRowRendererIds", "description": "", - "args": [], "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } + "ofType": { "kind": "ENUM", "name": "RowRendererId", "ofType": null } } }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "uniqueSourceIps", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "uniqueSourceIpsHistogram", + "name": "filters", "description": "", - "args": [], "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } + "ofType": { "kind": "INPUT_OBJECT", "name": "FilterTimelineInput", "ofType": null } } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "uniqueDestinationIps", + "name": "kqlMode", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "kqlQuery", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "SerializedFilterQueryInput", + "ofType": null + }, + "defaultValue": null }, { - "name": "uniqueDestinationIpsHistogram", + "name": "indexNames", "description": "", - "args": [], "type": { "kind": "LIST", "name": null, "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "KpiHostHistogramData", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "inspect", + "name": "title", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "HistogramType", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, { - "name": "authentications", + "name": "templateTimelineId", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "anomalies", + "name": "templateTimelineVersion", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, + "defaultValue": null }, - { "name": "events", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "alerts", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "dns", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "MatrixHistogramOverTimeData", - "description": "", - "fields": [ { - "name": "inspect", + "name": "timelineType", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, + "defaultValue": null }, { - "name": "matrixHistogramData", + "name": "dateRange", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "INPUT_OBJECT", "name": "DateRangePickerInput", "ofType": null }, + "defaultValue": null }, { - "name": "totalCount", + "name": "savedQueryId", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "sort", + "description": "", + "type": { "kind": "INPUT_OBJECT", "name": "SortTimelineInput", "ofType": null }, + "defaultValue": null + }, + { + "name": "status", + "description": "", + "type": { "kind": "ENUM", "name": "TimelineStatus", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", + "kind": "INPUT_OBJECT", + "name": "ColumnHeaderInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "x", + "name": "aggregatable", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null }, { - "name": "y", + "name": "category", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "g", + "name": "columnHeaderType", "description": "", - "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "FlowTargetSourceDest", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "defaultValue": null + }, { - "name": "destination", + "name": "description", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, - { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "NetworkTopTablesSortField", - "description": "", - "fields": null, - "inputFields": [ { - "name": "field", + "name": "example", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "NetworkTopTablesFields", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "defaultValue": null }, { - "name": "direction", + "name": "indexes", "description": "", "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } }, "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "NetworkTopTablesFields", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + }, { - "name": "bytes_in", + "name": "id", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "bytes_out", + "name": "name", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, - { "name": "flows", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "destination_ips", + "name": "placeholder", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "source_ips", + "name": "searchable", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null + }, + { + "name": "type", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], + "interfaces": null, + "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkTopCountriesData", + "kind": "INPUT_OBJECT", + "name": "DataProviderInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "edges", + "name": "id", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkTopCountriesEdges", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "totalCount", + "name": "name", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "enabled", + "description": "", + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null + }, + { + "name": "excluded", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null }, { - "name": "pageInfo", + "name": "kqlQuery", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "inspect", + "name": "queryMatch", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "NetworkTopCountriesEdges", - "description": "", - "fields": [ + "type": { "kind": "INPUT_OBJECT", "name": "QueryMatchInput", "ofType": null }, + "defaultValue": null + }, { - "name": "node", + "name": "and", "description": "", - "args": [], "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkTopCountriesItem", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "DataProviderInput", "ofType": null } + } }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "cursor", + "name": "type", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "ENUM", "name": "DataProviderType", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkTopCountriesItem", + "kind": "INPUT_OBJECT", + "name": "QueryMatchInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "_id", + "name": "field", "description": "", - "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "source", + "name": "displayField", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TopCountriesItemSource", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "destination", + "name": "value", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TopCountriesItemDestination", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "network", + "name": "displayValue", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "TopNetworkTablesEcsField", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "operator", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "TopCountriesItemSource", + "kind": "INPUT_OBJECT", + "name": "FilterTimelineInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "country", + "name": "exists", "description": "", - "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null }, { - "name": "destination_ips", + "name": "meta", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "INPUT_OBJECT", "name": "FilterMetaTimelineInput", "ofType": null }, + "defaultValue": null }, { - "name": "flows", + "name": "match_all", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "location", + "name": "missing", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "source_ips", + "name": "query", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "GeoItem", - "description": "", - "fields": [ + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, { - "name": "geo", + "name": "range", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "flowTarget", + "name": "script", "description": "", - "args": [], - "type": { "kind": "ENUM", "name": "FlowTargetSourceDest", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "TopCountriesItemDestination", + "kind": "INPUT_OBJECT", + "name": "FilterMetaTimelineInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "country", + "name": "alias", "description": "", - "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "defaultValue": null + }, + { + "name": "controlledBy", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "destination_ips", + "name": "disabled", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null }, { - "name": "flows", + "name": "field", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "location", + "name": "formattedValue", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "index", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "source_ips", + "name": "key", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "negate", + "description": "", + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": null + }, + { + "name": "params", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "type", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "value", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "SerializedFilterQueryInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "filterQuery", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "INPUT_OBJECT", "name": "SerializedKueryQueryInput", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "TopNetworkTablesEcsField", + "kind": "INPUT_OBJECT", + "name": "SerializedKueryQueryInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "bytes_in", + "name": "kuery", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "INPUT_OBJECT", "name": "KueryFilterQueryInput", "ofType": null }, + "defaultValue": null }, { - "name": "bytes_out", + "name": "serializedQuery", "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkTopNFlowData", + "kind": "INPUT_OBJECT", + "name": "KueryFilterQueryInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "edges", + "name": "kind", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkTopNFlowEdges", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "totalCount", + "name": "expression", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "DateRangePickerInput", + "description": "", + "fields": null, + "inputFields": [ { - "name": "pageInfo", + "name": "start", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, + "defaultValue": null }, { - "name": "inspect", + "name": "end", "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkTopNFlowEdges", + "kind": "INPUT_OBJECT", + "name": "SortTimelineInput", "description": "", - "fields": [ + "fields": null, + "inputFields": [ { - "name": "node", + "name": "columnId", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkTopNFlowItem", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null }, { - "name": "cursor", + "name": "sortDirection", "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null } ], - "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "NetworkTopNFlowItem", + "name": "ResponseTimeline", "description": "", "fields": [ { - "name": "_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "source", + "name": "code", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "TopNFlowItemSource", "ofType": null }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "destination", + "name": "message", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "TopNFlowItemDestination", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "network", + "name": "timeline", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "TopNetworkTablesEcsField", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "TimelineResult", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null } @@ -8466,35 +4104,19 @@ }, { "kind": "OBJECT", - "name": "TopNFlowItemSource", + "name": "ResponseFavoriteTimeline", "description": "", "fields": [ { - "name": "autonomous_system", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AutonomousSystemItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "domain", + "name": "code", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ip", + "name": "message", "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, @@ -8502,53 +4124,42 @@ "deprecationReason": null }, { - "name": "location", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "GeoItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "flows", + "name": "savedObjectId", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "destination_ips", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AutonomousSystemItem", - "description": "", - "fields": [ - { - "name": "name", + "name": "version", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "number", + "name": "favorite", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "FavoriteTimelineResult", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null } @@ -8560,62 +4171,74 @@ }, { "kind": "OBJECT", - "name": "TopNFlowItemDestination", - "description": "", + "name": "__Schema", + "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", "fields": [ { - "name": "autonomous_system", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "AutonomousSystemItem", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "domain", - "description": "", + "name": "types", + "description": "A list of all types supported by this server.", "args": [], "type": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, "ofType": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + } } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ip", - "description": "", + "name": "queryType", + "description": "The type that query operations will be rooted at.", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "location", - "description": "", + "name": "mutationType", + "description": "If this server supports mutation, the type that mutation operations will be rooted at.", "args": [], - "type": { "kind": "OBJECT", "name": "GeoItem", "ofType": null }, + "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "flows", - "description": "", + "name": "subscriptionType", + "description": "If this server support subscription, the type that subscription operations will be rooted at.", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "source_ips", - "description": "", + "name": "directives", + "description": "A list of all directives supported by this server.", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Directive", "ofType": null } + } + } + }, "isDeprecated": false, "deprecationReason": null } @@ -8626,137 +4249,119 @@ "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "NetworkDnsSortField", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "NetworkDnsFields", "ofType": null } - }, - "defaultValue": null - }, + "kind": "OBJECT", + "name": "__Type", + "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", + "fields": [ { - "name": "direction", - "description": "", + "name": "kind", + "description": null, + "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + "ofType": { "kind": "ENUM", "name": "__TypeKind", "ofType": null } }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "NetworkDnsFields", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "dnsName", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "queryCount", - "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "uniqueDomains", - "description": "", + "name": "name", + "description": null, + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dnsBytesIn", - "description": "", + "name": "description", + "description": null, + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dnsBytesOut", - "description": "", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "NetworkDnsData", - "description": "", - "fields": [ - { - "name": "edges", - "description": "", - "args": [], + "name": "fields", + "description": null, + "args": [ + { + "name": "includeDeprecated", + "description": null, + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": "false" + } + ], "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, "ofType": { - "kind": "LIST", + "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkDnsEdges", "ofType": null } - } + "ofType": { "kind": "OBJECT", "name": "__Field", "ofType": null } } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", - "description": "", + "name": "interfaces", + "description": null, "args": [], "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", - "description": "", + "name": "possibleTypes", + "description": null, "args": [], "type": { - "kind": "NON_NULL", + "kind": "LIST", "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "name": "enumValues", + "description": null, + "args": [ + { + "name": "includeDeprecated", + "description": null, + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "defaultValue": "false" + } + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__EnumValue", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "histogram", - "description": "", + "name": "inputFields", + "description": null, "args": [], "type": { "kind": "LIST", @@ -8764,15 +4369,19 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverOrdinalHistogramData", - "ofType": null - } + "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } } }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "ofType", + "description": null, + "args": [], + "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -8781,90 +4390,138 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkDnsEdges", - "description": "", - "fields": [ + "kind": "ENUM", + "name": "__TypeKind", + "description": "An enum describing what kind of type a given `__Type` is.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "node", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkDnsItem", "ofType": null } - }, + "name": "SCALAR", + "description": "Indicates this type is a scalar.", "isDeprecated": false, "deprecationReason": null }, { - "name": "cursor", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, + "name": "OBJECT", + "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INTERFACE", + "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "UNION", + "description": "Indicates this type is a union. `possibleTypes` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ENUM", + "description": "Indicates this type is an enum. `enumValues` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INPUT_OBJECT", + "description": "Indicates this type is an input object. `inputFields` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "LIST", + "description": "Indicates this type is a list. `ofType` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "NON_NULL", + "description": "Indicates this type is a non-null. `ofType` is a valid field.", "isDeprecated": false, "deprecationReason": null } ], - "inputFields": null, - "interfaces": [], - "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "NetworkDnsItem", - "description": "", + "name": "__Field", + "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", "fields": [ { - "name": "_id", - "description": "", + "name": "name", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dnsBytesIn", - "description": "", + "name": "description", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dnsBytesOut", - "description": "", + "name": "args", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } + } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dnsName", - "description": "", + "name": "type", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "queryCount", - "description": "", + "name": "isDeprecated", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "uniqueDomains", - "description": "", + "name": "deprecationReason", + "description": null, "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -8876,12 +4533,12 @@ }, { "kind": "OBJECT", - "name": "MatrixOverOrdinalHistogramData", - "description": "", + "name": "__InputValue", + "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", "fields": [ { - "name": "x", - "description": "", + "name": "name", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -8892,28 +4549,32 @@ "deprecationReason": null }, { - "name": "y", - "description": "", + "name": "description", + "description": null, "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "g", - "description": "", + "name": "type", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "defaultValue", + "description": "A GraphQL-formatted string representing the default value for this input value.", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -8923,52 +4584,48 @@ }, { "kind": "OBJECT", - "name": "NetworkDsOverTimeData", - "description": "", + "name": "__EnumValue", + "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", "fields": [ { - "name": "inspect", - "description": "", + "name": "name", + "description": null, "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "matrixHistogramData", - "description": "", + "name": "description", + "description": null, "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "MatrixOverTimeHistogramData", - "ofType": null - } - } - } - }, + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", - "description": "", + "name": "isDeprecated", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "deprecationReason", + "description": null, + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -8977,34 +4634,33 @@ "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "NetworkHttpSortField", - "description": "", - "fields": null, - "inputFields": [ + "kind": "OBJECT", + "name": "__Directive", + "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", + "fields": [ { - "name": "direction", - "description": "", + "name": "name", + "description": null, + "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "NetworkHttpData", - "description": "", - "fields": [ + "isDeprecated": false, + "deprecationReason": null + }, { - "name": "edges", - "description": "", + "name": "description", + "description": null, + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "locations", + "description": null, "args": [], "type": { "kind": "NON_NULL", @@ -9015,7 +4671,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkHttpEdges", "ofType": null } + "ofType": { "kind": "ENUM", "name": "__DirectiveLocation", "ofType": null } } } }, @@ -9023,71 +4679,60 @@ "deprecationReason": null }, { - "name": "totalCount", - "description": "", + "name": "args", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } + } + } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", - "description": "", + "name": "onOperation", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "Use `locations`." }, { - "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": "", + "name": "onFragment", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "NetworkHttpItem", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "Use `locations`." }, { - "name": "cursor", - "description": "", + "name": "onField", + "description": null, "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } }, - "isDeprecated": false, - "deprecationReason": null + "isDeprecated": true, + "deprecationReason": "Use `locations`." } ], "inputFields": null, @@ -9096,451 +4741,288 @@ "possibleTypes": null }, { - "kind": "OBJECT", - "name": "NetworkHttpItem", - "description": "", - "fields": [ + "kind": "ENUM", + "name": "__DirectiveLocation", + "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "name": "QUERY", + "description": "Location adjacent to a query operation.", "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 } - } - } - }, + "name": "MUTATION", + "description": "Location adjacent to a mutation operation.", "isDeprecated": false, "deprecationReason": null }, { - "name": "lastHost", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "name": "SUBSCRIPTION", + "description": "Location adjacent to a subscription operation.", "isDeprecated": false, "deprecationReason": null }, { - "name": "lastSourceIp", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "name": "FIELD", + "description": "Location adjacent to a field.", "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 } - } - } - }, + "name": "FRAGMENT_DEFINITION", + "description": "Location adjacent to a fragment definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "path", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "name": "FRAGMENT_SPREAD", + "description": "Location adjacent to a fragment spread.", "isDeprecated": false, "deprecationReason": null }, { - "name": "requestCount", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "INLINE_FRAGMENT", + "description": "Location adjacent to an inline fragment.", "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 } - } - } - }, + "name": "SCHEMA", + "description": "Location adjacent to a schema definition.", "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "OverviewNetworkData", - "description": "", - "fields": [ + }, { - "name": "auditbeatSocket", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "SCALAR", + "description": "Location adjacent to a scalar definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatCisco", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "OBJECT", + "description": "Location adjacent to an object type definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatNetflow", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "FIELD_DEFINITION", + "description": "Location adjacent to a field definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatPanw", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "ARGUMENT_DEFINITION", + "description": "Location adjacent to an argument definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatSuricata", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "INTERFACE", + "description": "Location adjacent to an interface definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatZeek", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "UNION", + "description": "Location adjacent to a union definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "packetbeatDNS", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "ENUM", + "description": "Location adjacent to an enum definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "packetbeatFlow", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "ENUM_VALUE", + "description": "Location adjacent to an enum value definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "packetbeatTLS", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "name": "INPUT_OBJECT", + "description": "Location adjacent to an input object type definition.", "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "name": "INPUT_FIELD_DEFINITION", + "description": "Location adjacent to an input object field definition.", "isDeprecated": false, "deprecationReason": null } ], + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "ToStringArrayNoNullable", + "description": "", + "fields": null, "inputFields": null, - "interfaces": [], + "interfaces": null, "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "OverviewHostData", + "name": "EventEcsFields", "description": "", "fields": [ { - "name": "auditbeatAuditd", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "auditbeatFIM", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "auditbeatLogin", + "name": "action", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "auditbeatPackage", + "name": "category", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "auditbeatProcess", + "name": "code", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "auditbeatUser", + "name": "created", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameDns", + "name": "dataset", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameFile", + "name": "duration", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameImageLoad", + "name": "end", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameNetwork", + "name": "hash", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameProcess", + "name": "id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameRegistry", + "name": "kind", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "endgameSecurity", + "name": "module", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "filebeatSystemModule", + "name": "original", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "winlogbeatSecurity", + "name": "outcome", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "winlogbeatMWSysmonOperational", + "name": "risk_score", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "risk_score_norm", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "TlsSortField", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "TlsFields", "ofType": null } - }, - "defaultValue": null }, { - "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": "ENUM", - "name": "TlsFields", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { "name": "_id", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TlsData", - "description": "", - "fields": [ - { - "name": "edges", + "name": "severity", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TlsEdges", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", + "name": "start", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", + "name": "timezone", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "type", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9550,32 +5032,44 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "SCALAR", + "name": "ToDateArray", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "ToNumberArray", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", - "name": "TlsEdges", + "name": "Location", "description": "", "fields": [ { - "name": "node", + "name": "lon", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TlsNode", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "cursor", + "name": "lat", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9587,149 +5081,62 @@ }, { "kind": "OBJECT", - "name": "TlsNode", + "name": "GeoEcsFields", "description": "", "fields": [ { - "name": "_id", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timestamp", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "notAfter", + "name": "city_name", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "subjects", + "name": "continent_name", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ja3", + "name": "country_iso_code", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "issuers", - "description": "", - "args": [], - "type": { - "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": "UncommonProcessesData", - "description": "", - "fields": [ - { - "name": "edges", + "name": "country_name", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UncommonProcessesEdges", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", + "name": "location", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "Location", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", + "name": "region_iso_code", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfoPaginated", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inspect", + "name": "region_name", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9741,30 +5148,30 @@ }, { "kind": "OBJECT", - "name": "UncommonProcessesEdges", + "name": "PrimarySecondary", "description": "", "fields": [ { - "name": "node", + "name": "primary", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "UncommonProcessItem", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "cursor", + "name": "secondary", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9776,70 +5183,46 @@ }, { "kind": "OBJECT", - "name": "UncommonProcessItem", + "name": "Summary", "description": "", "fields": [ { - "name": "_id", + "name": "actor", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "PrimarySecondary", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "instances", + "name": "object", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "PrimarySecondary", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "process", + "name": "how", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ProcessEcsFields", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "hosts", + "name": "message_type", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "user", + "name": "sequence", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "UserEcsFields", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9851,18 +5234,14 @@ }, { "kind": "OBJECT", - "name": "SayMyName", + "name": "AgentEcsField", "description": "", "fields": [ { - "name": "appName", - "description": "The id of the source", + "name": "type", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -9874,318 +5253,293 @@ }, { "kind": "OBJECT", - "name": "TimelineResult", + "name": "AuditdData", "description": "", "fields": [ { - "name": "columns", + "name": "acct", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ColumnHeaderResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "created", + "name": "terminal", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "createdBy", + "name": "op", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "AuditdEcsFields", + "description": "", + "fields": [ { - "name": "dataProviders", + "name": "result", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "DataProviderResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "dateRange", + "name": "session", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "DateRangePickerResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "description", + "name": "data", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "AuditdData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "eventIdToNoteIds", + "name": "summary", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } - } - }, + "type": { "kind": "OBJECT", "name": "Summary", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "eventType", + "name": "sequence", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Thread", + "description": "", + "fields": [ { - "name": "excludedRowRendererIds", + "name": "id", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "RowRendererId", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "favorite", + "name": "start", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "FavoriteTimelineResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ProcessHashData", + "description": "", + "fields": [ { - "name": "filters", + "name": "md5", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "FilterTimelineResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "kqlMode", + "name": "sha1", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "kqlQuery", + "name": "sha256", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "SerializedFilterQueryResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ProcessEcsFields", + "description": "", + "fields": [ { - "name": "notes", + "name": "hash", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } - } - }, + "type": { "kind": "OBJECT", "name": "ProcessHashData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "noteIds", + "name": "pid", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pinnedEventIds", + "name": "name", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pinnedEventsSaveObject", + "name": "ppid", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "PinnedEvent", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "savedQueryId", + "name": "args", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "savedObjectId", + "name": "entity_id", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "sort", + "name": "executable", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "SortTimelineResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "status", + "name": "title", "description": "", "args": [], - "type": { "kind": "ENUM", "name": "TimelineStatus", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "title", + "name": "thread", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "Thread", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "templateTimelineId", + "name": "working_directory", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SourceEcsFields", + "description": "", + "fields": [ + { + "name": "bytes", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "templateTimelineVersion", + "name": "ip", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "timelineType", + "name": "port", "description": "", "args": [], - "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "updated", + "name": "domain", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "updatedBy", + "name": "geo", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "version", + "name": "packets", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10197,102 +5551,116 @@ }, { "kind": "OBJECT", - "name": "ColumnHeaderResult", + "name": "DestinationEcsFields", "description": "", "fields": [ { - "name": "aggregatable", + "name": "bytes", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "category", + "name": "ip", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "columnHeaderType", + "name": "port", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "description", + "name": "domain", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "example", + "name": "geo", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "indexes", + "name": "packets", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DnsQuestionData", + "description": "", + "fields": [ { - "name": "id", + "name": "name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "name", + "name": "type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DnsEcsFields", + "description": "", + "fields": [ { - "name": "placeholder", + "name": "question", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "DnsQuestionData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "searchable", + "name": "resolved_ip", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", + "name": "response_code", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10304,129 +5672,110 @@ }, { "kind": "OBJECT", - "name": "DataProviderResult", + "name": "EndgameEcsFields", "description": "", "fields": [ { - "name": "id", + "name": "exit_code", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "name", + "name": "file_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "enabled", + "name": "file_path", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "excluded", + "name": "logon_type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "kqlQuery", + "name": "parent_process_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "queryMatch", + "name": "pid", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "QueryMatchResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", + "name": "process_name", "description": "", "args": [], - "type": { "kind": "ENUM", "name": "DataProviderType", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "and", + "name": "subject_domain_name", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "DataProviderResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "QueryMatchResult", - "description": "", - "fields": [ + }, { - "name": "field", + "name": "subject_logon_id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "displayField", + "name": "subject_user_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "value", + "name": "target_domain_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "displayValue", + "name": "target_logon_id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "operator", + "name": "target_user_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10436,151 +5785,97 @@ "enumValues": null, "possibleTypes": null }, - { - "kind": "ENUM", - "name": "DataProviderType", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "default", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "template", - "description": "", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, { "kind": "OBJECT", - "name": "DateRangePickerResult", + "name": "SuricataAlertData", "description": "", "fields": [ { - "name": "start", + "name": "signature", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "end", + "name": "signature_id", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } ], "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "RowRendererId", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { "name": "auditd", "description": "", "isDeprecated": false, "deprecationReason": null }, - { - "name": "auditd_file", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "netflow", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { "name": "plain", "description": "", "isDeprecated": false, "deprecationReason": null }, - { - "name": "suricata", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { "name": "system", "description": "", "isDeprecated": false, "deprecationReason": null }, - { - "name": "system_dns", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "system_endgame_process", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "system_file", - "description": "", - "isDeprecated": false, - "deprecationReason": null - }, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SuricataEveData", + "description": "", + "fields": [ { - "name": "system_fim", + "name": "alert", "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "SuricataAlertData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "system_security_event", + "name": "flow_id", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "system_socket", + "name": "proto", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, - { "name": "zeek", "description": "", "isDeprecated": false, "deprecationReason": null } + } ], + "inputFields": null, + "interfaces": [], + "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "FavoriteTimelineResult", + "name": "SuricataEcsFields", "description": "", "fields": [ { - "name": "fullName", - "description": "", - "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "userName", + "name": "eve", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "SuricataEveData", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TlsJa3Data", + "description": "", + "fields": [ { - "name": "favoriteDate", + "name": "hash", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10592,62 +5887,106 @@ }, { "kind": "OBJECT", - "name": "FilterTimelineResult", + "name": "FingerprintData", "description": "", "fields": [ { - "name": "exists", + "name": "sha1", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TlsClientCertificateData", + "description": "", + "fields": [ { - "name": "meta", + "name": "fingerprint", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "FilterMetaTimelineResult", "ofType": null }, + "type": { "kind": "OBJECT", "name": "FingerprintData", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TlsServerCertificateData", + "description": "", + "fields": [ { - "name": "match_all", + "name": "fingerprint", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "FingerprintData", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TlsFingerprintsData", + "description": "", + "fields": [ { - "name": "missing", + "name": "ja3", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "TlsJa3Data", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TlsEcsFields", + "description": "", + "fields": [ { - "name": "query", + "name": "client_certificate", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "TlsClientCertificateData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "range", + "name": "fingerprints", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "TlsFingerprintsData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "script", + "name": "server_certificate", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "TlsServerCertificateData", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10659,94 +5998,123 @@ }, { "kind": "OBJECT", - "name": "FilterMetaTimelineResult", + "name": "ZeekConnectionData", "description": "", "fields": [ { - "name": "alias", + "name": "local_resp", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "controlledBy", + "name": "local_orig", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "disabled", + "name": "missed_bytes", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "field", + "name": "state", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "formattedValue", + "name": "history", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "ToBooleanArray", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ZeekNoticeData", + "description": "", + "fields": [ + { + "name": "suppress_for", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "index", + "name": "msg", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "key", + "name": "note", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "negate", + "name": "sub", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "params", + "name": "dst", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", + "name": "dropped", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "value", + "name": "peer_descr", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -10758,577 +6126,303 @@ }, { "kind": "OBJECT", - "name": "SerializedFilterQueryResult", + "name": "ZeekDnsData", "description": "", "fields": [ { - "name": "filterQuery", + "name": "AA", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "SerializedKueryQueryResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SerializedKueryQueryResult", - "description": "", - "fields": [ + }, { - "name": "kuery", + "name": "qclass_name", "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "KueryFilterQueryResult", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "serializedQuery", + "name": "RD", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "KueryFilterQueryResult", - "description": "", - "fields": [ + }, { - "name": "kind", + "name": "qtype_name", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "expression", + "name": "rejected", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SortTimelineResult", - "description": "", - "fields": [ + }, { - "name": "columnId", + "name": "qtype", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "sortDirection", + "name": "query", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "TimelineStatus", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { "name": "active", "description": "", "isDeprecated": false, "deprecationReason": null }, - { "name": "draft", "description": "", "isDeprecated": false, "deprecationReason": null }, + }, { - "name": "immutable", + "name": "trans_id", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Int", - "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "TimelineType", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + }, { - "name": "default", + "name": "qclass", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "template", + "name": "RA", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "PageInfoTimeline", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "pageIndex", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null }, { - "name": "pageSize", + "name": "TC", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } - }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "SortTimeline", + "kind": "OBJECT", + "name": "FileFields", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "sortField", + "name": "name", "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "SortFieldTimeline", "ofType": null } - }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "sortOrder", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "SortFieldTimeline", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { "name": "title", "description": "", "isDeprecated": false, "deprecationReason": null }, - { - "name": "description", + "name": "path", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "updated", + "name": "target_path", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { "name": "created", "description": "", "isDeprecated": false, "deprecationReason": null } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ResponseTimelines", - "description": "", - "fields": [ { - "name": "timeline", + "name": "extension", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineResult", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "totalCount", + "name": "type", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "defaultTimelineCount", + "name": "device", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "templateTimelineCount", + "name": "inode", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "elasticTemplateTimelineCount", + "name": "uid", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "customTemplateTimelineCount", + "name": "owner", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "favoriteCount", + "name": "gid", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Mutation", - "description": "", - "fields": [ + }, { - "name": "persistNote", - "description": "Persists a note", - "args": [ - { - "name": "noteId", - "description": "", - "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, - "defaultValue": null - }, - { - "name": "version", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "note", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "NoteInput", "ofType": null } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ResponseNote", "ofType": null } - }, + "name": "group", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deleteNote", + "name": "mode", "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deleteNoteByTimelineId", + "name": "size", "description": "", - "args": [ - { - "name": "timelineId", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "version", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "persistPinnedEventOnTimeline", - "description": "Persists a pinned event in a timeline", - "args": [ - { - "name": "pinnedEventId", - "description": "", - "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, - "defaultValue": null - }, - { - "name": "eventId", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "timelineId", - "description": "", - "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, - "defaultValue": null - } - ], - "type": { "kind": "OBJECT", "name": "PinnedEvent", "ofType": null }, + "name": "mtime", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deletePinnedEventOnTimeline", - "description": "Remove a pinned events in a timeline", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, + "name": "ctime", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToDateArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ZeekHttpData", + "description": "", + "fields": [ + { + "name": "resp_mime_types", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deleteAllPinnedEventsOnTimeline", - "description": "Remove all pinned events in a timeline", - "args": [ - { - "name": "timelineId", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, + "name": "trans_depth", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "persistTimeline", - "description": "Persists a timeline", - "args": [ - { - "name": "id", - "description": "", - "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, - "defaultValue": null - }, - { - "name": "version", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "timeline", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "TimelineInput", "ofType": null } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ResponseTimeline", "ofType": null } - }, + "name": "status_msg", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "persistFavorite", + "name": "resp_fuids", "description": "", - "args": [ - { - "name": "timelineId", - "description": "", - "type": { "kind": "SCALAR", "name": "ID", "ofType": null }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ResponseFavoriteTimeline", "ofType": null } - }, + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deleteTimeline", + "name": "tags", "description": "", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "ID", "ofType": null } - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "HttpBodyData", + "description": "", + "fields": [ + { + "name": "content", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "bytes", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -11339,64 +6433,74 @@ "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "NoteInput", + "kind": "OBJECT", + "name": "HttpRequestData", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "eventId", + "name": "method", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "note", + "name": "body", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "HttpBodyData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "timelineId", + "name": "referrer", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "bytes", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "ResponseNote", + "name": "HttpResponseData", "description": "", "fields": [ { - "name": "code", + "name": "status_code", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "message", + "name": "body", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "HttpBodyData", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "note", + "name": "bytes", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "NoteResult", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -11407,610 +6511,608 @@ "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "TimelineInput", + "kind": "OBJECT", + "name": "HttpEcsFields", "description": "", - "fields": null, - "inputFields": [ - { - "name": "columns", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "ColumnHeaderInput", "ofType": null } - } - }, - "defaultValue": null - }, - { - "name": "dataProviders", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "DataProviderInput", "ofType": null } - } - }, - "defaultValue": null - }, - { - "name": "description", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "eventType", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "excludedRowRendererIds", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "RowRendererId", "ofType": null } - } - }, - "defaultValue": null - }, - { - "name": "filters", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "FilterTimelineInput", "ofType": null } - } - }, - "defaultValue": null - }, - { - "name": "kqlMode", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "kqlQuery", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "SerializedFilterQueryInput", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "title", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, + "fields": [ { - "name": "templateTimelineId", + "name": "version", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "templateTimelineVersion", + "name": "request", "description": "", - "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "HttpRequestData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "timelineType", + "name": "response", "description": "", - "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, - "defaultValue": null - }, + "args": [], + "type": { "kind": "OBJECT", "name": "HttpResponseData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UrlEcsFields", + "description": "", + "fields": [ { - "name": "dateRange", + "name": "domain", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "DateRangePickerInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "savedQueryId", + "name": "original", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "sort", + "name": "username", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "SortTimelineInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "status", + "name": "password", "description": "", - "type": { "kind": "ENUM", "name": "TimelineStatus", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "ColumnHeaderInput", + "kind": "OBJECT", + "name": "ZeekFileData", "description": "", - "fields": null, - "inputFields": [ - { - "name": "aggregatable", - "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null - }, + "fields": [ { - "name": "category", + "name": "session_ids", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "columnHeaderType", + "name": "timedout", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "description", + "name": "local_orig", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "example", + "name": "tx_host", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "indexes", + "name": "source", "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - } - }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "id", + "name": "is_orig", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "name", + "name": "overflow_bytes", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "placeholder", + "name": "sha1", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "searchable", + "name": "duration", "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "type", + "name": "depth", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "DataProviderInput", - "description": "", - "fields": null, - "inputFields": [ + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { - "name": "id", + "name": "analyzers", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "name", + "name": "mime_type", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "enabled", + "name": "rx_host", "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "excluded", + "name": "total_bytes", "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "kqlQuery", + "name": "fuid", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "queryMatch", + "name": "seen_bytes", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "QueryMatchInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "and", + "name": "missing_bytes", "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "INPUT_OBJECT", "name": "DataProviderInput", "ofType": null } - } - }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "type", + "name": "md5", "description": "", - "type": { "kind": "ENUM", "name": "DataProviderType", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "QueryMatchInput", + "kind": "OBJECT", + "name": "ZeekSslData", "description": "", - "fields": null, - "inputFields": [ - { - "name": "field", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, + "fields": [ { - "name": "displayField", + "name": "cipher", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "value", + "name": "established", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "displayValue", + "name": "resumed", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "operator", + "name": "version", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "FilterTimelineInput", + "kind": "OBJECT", + "name": "ZeekEcsFields", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "exists", + "name": "session_id", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "meta", + "name": "connection", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "FilterMetaTimelineInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekConnectionData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "match_all", + "name": "notice", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekNoticeData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "missing", + "name": "dns", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekDnsData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "query", + "name": "http", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekHttpData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "range", + "name": "files", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekFileData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "script", + "name": "ssl", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekSslData", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "FilterMetaTimelineInput", + "kind": "OBJECT", + "name": "UserEcsFields", "description": "", - "fields": null, - "inputFields": [ - { - "name": "alias", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "controlledBy", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, - { - "name": "disabled", - "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null - }, - { - "name": "field", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, + "fields": [ { - "name": "formattedValue", + "name": "domain", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "index", + "name": "id", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "key", + "name": "name", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "negate", + "name": "full_name", "description": "", - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "params", + "name": "email", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "type", + "name": "hash", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "value", + "name": "group", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "SerializedFilterQueryInput", + "kind": "OBJECT", + "name": "WinlogEcsFields", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "filterQuery", + "name": "event_id", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "SerializedKueryQueryInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "SerializedKueryQueryInput", + "kind": "OBJECT", + "name": "NetworkEcsField", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "kuery", + "name": "bytes", "description": "", - "type": { "kind": "INPUT_OBJECT", "name": "KueryFilterQueryInput", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "serializedQuery", + "name": "community_id", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "KueryFilterQueryInput", - "description": "", - "fields": null, - "inputFields": [ + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { - "name": "kind", + "name": "direction", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "expression", + "name": "packets", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "protocol", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "transport", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "DateRangePickerInput", + "kind": "OBJECT", + "name": "PackageEcsFields", "description": "", - "fields": null, - "inputFields": [ + "fields": [ { - "name": "start", + "name": "arch", "description": "", - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "end", + "name": "entity_id", "description": "", - "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "name", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "size", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "summary", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "version", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { - "kind": "INPUT_OBJECT", - "name": "SortTimelineInput", + "kind": "OBJECT", + "name": "AuditEcsFields", "description": "", - "fields": null, - "inputFields": [ - { - "name": "columnId", - "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null - }, + "fields": [ { - "name": "sortDirection", + "name": "package", "description": "", - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "defaultValue": null + "args": [], + "type": { "kind": "OBJECT", "name": "PackageEcsFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null } ], - "interfaces": null, + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, { "kind": "OBJECT", - "name": "ResponseTimeline", + "name": "SshEcsFields", "description": "", "fields": [ { - "name": "code", + "name": "method", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "message", + "name": "signature", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "AuthEcsFields", + "description": "", + "fields": [ { - "name": "timeline", + "name": "ssh", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "TimelineResult", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "SshEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -12022,80 +7124,56 @@ }, { "kind": "OBJECT", - "name": "ResponseFavoriteTimeline", + "name": "SystemEcsField", "description": "", "fields": [ { - "name": "code", + "name": "audit", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "Float", "ofType": null }, + "type": { "kind": "OBJECT", "name": "AuditEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "message", + "name": "auth", "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "OBJECT", "name": "AuthEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RuleField", + "description": "", + "fields": [ { - "name": "savedObjectId", + "name": "id", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "version", + "name": "rule_id", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "favorite", + "name": "false_positives", "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "FavoriteTimelineResult", "ofType": null } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Schema", - "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", - "fields": [ - { - "name": "types", - "description": "A list of all types supported by this server.", - "args": [], "type": { "kind": "NON_NULL", "name": null, @@ -12105,7 +7183,7 @@ "ofType": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } } } }, @@ -12113,384 +7191,242 @@ "deprecationReason": null }, { - "name": "queryType", - "description": "The type that query operations will be rooted at.", + "name": "saved_id", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "mutationType", - "description": "If this server supports mutation, the type that mutation operations will be rooted at.", + "name": "timeline_id", + "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "subscriptionType", - "description": "If this server support subscription, the type that subscription operations will be rooted at.", + "name": "timeline_title", + "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "directives", - "description": "A list of all directives supported by this server.", + "name": "max_signals", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Directive", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "ToNumberArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Type", - "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", - "fields": [ + }, { - "name": "kind", - "description": null, + "name": "risk_score", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "__TypeKind", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "name", - "description": null, + "name": "output_index", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { "name": "description", - "description": null, + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "fields", - "description": null, - "args": [ - { - "name": "includeDeprecated", - "description": null, - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": "false" - } - ], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Field", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "interfaces", - "description": null, + "name": "from", + "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, - { - "name": "possibleTypes", - "description": null, - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } - } - }, + { + "name": "immutable", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "enumValues", - "description": null, - "args": [ - { - "name": "includeDeprecated", - "description": null, - "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, - "defaultValue": "false" - } - ], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__EnumValue", "ofType": null } - } - }, + "name": "index", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "inputFields", - "description": null, + "name": "interval", + "description": "", "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ofType", - "description": null, + "name": "language", + "description": "", "args": [], - "type": { "kind": "OBJECT", "name": "__Type", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "__TypeKind", - "description": "An enum describing what kind of type a given `__Type` is.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + }, { - "name": "SCALAR", - "description": "Indicates this type is a scalar.", + "name": "query", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "OBJECT", - "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", + "name": "references", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INTERFACE", - "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", + "name": "severity", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "UNION", - "description": "Indicates this type is a union. `possibleTypes` is a valid field.", + "name": "tags", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ENUM", - "description": "Indicates this type is an enum. `enumValues` is a valid field.", + "name": "threat", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INPUT_OBJECT", - "description": "Indicates this type is an input object. `inputFields` is a valid field.", + "name": "type", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "LIST", - "description": "Indicates this type is a list. `ofType` is a valid field.", + "name": "size", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "NON_NULL", - "description": "Indicates this type is a non-null. `ofType` is a valid field.", + "name": "to", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Field", - "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", - "fields": [ + }, { - "name": "name", - "description": null, + "name": "enabled", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToBooleanArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "description", - "description": null, + "name": "filters", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "args", - "description": null, + "name": "created_at", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } - } - } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", - "description": null, + "name": "updated_at", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "isDeprecated", - "description": null, + "name": "created_by", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "deprecationReason", - "description": null, + "name": "updated_by", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__InputValue", - "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", - "fields": [ + }, { - "name": "name", - "description": null, + "name": "version", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "description", - "description": null, + "name": "note", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "type", - "description": null, + "name": "threshold", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__Type", "ofType": null } - }, + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "defaultValue", - "description": "A GraphQL-formatted string representing the default value for this input value.", + "name": "exceptions_list", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToAny", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -12502,46 +7438,49 @@ }, { "kind": "OBJECT", - "name": "__EnumValue", - "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", + "name": "SignalField", + "description": "", "fields": [ { - "name": "name", - "description": null, + "name": "rule", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "RuleField", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "description", - "description": null, + "name": "original_time", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "isDeprecated", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, + "name": "status", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null - }, + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RuleEcsField", + "description": "", + "fields": [ { - "name": "deprecationReason", - "description": null, + "name": "reference", + "description": "", "args": [], - "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -12553,12 +7492,12 @@ }, { "kind": "OBJECT", - "name": "__Directive", - "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", + "name": "ECS", + "description": "", "fields": [ { - "name": "name", - "description": null, + "name": "_id", + "description": "", "args": [], "type": { "kind": "NON_NULL", @@ -12569,240 +7508,202 @@ "deprecationReason": null }, { - "name": "description", - "description": null, + "name": "_index", + "description": "", "args": [], "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "locations", - "description": null, + "name": "agent", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "ENUM", "name": "__DirectiveLocation", "ofType": null } - } - } - }, + "type": { "kind": "OBJECT", "name": "AgentEcsField", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "args", - "description": null, + "name": "auditd", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "__InputValue", "ofType": null } - } - } - }, + "type": { "kind": "OBJECT", "name": "AuditdEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "onOperation", - "description": null, + "name": "destination", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." + "type": { "kind": "OBJECT", "name": "DestinationEcsFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "onFragment", - "description": null, + "name": "dns", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." + "type": { "kind": "OBJECT", "name": "DnsEcsFields", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null }, { - "name": "onField", - "description": null, + "name": "endgame", + "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "__DirectiveLocation", - "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "QUERY", - "description": "Location adjacent to a query operation.", + "type": { "kind": "OBJECT", "name": "EndgameEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "MUTATION", - "description": "Location adjacent to a mutation operation.", + "name": "event", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "EventEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "SUBSCRIPTION", - "description": "Location adjacent to a subscription operation.", + "name": "geo", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "GeoEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "FIELD", - "description": "Location adjacent to a field.", + "name": "host", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "HostEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "FRAGMENT_DEFINITION", - "description": "Location adjacent to a fragment definition.", + "name": "network", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "NetworkEcsField", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "FRAGMENT_SPREAD", - "description": "Location adjacent to a fragment spread.", + "name": "rule", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "RuleEcsField", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INLINE_FRAGMENT", - "description": "Location adjacent to an inline fragment.", + "name": "signal", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "SignalField", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "SCHEMA", - "description": "Location adjacent to a schema definition.", + "name": "source", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "SourceEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "SCALAR", - "description": "Location adjacent to a scalar definition.", + "name": "suricata", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "SuricataEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "OBJECT", - "description": "Location adjacent to an object type definition.", + "name": "tls", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "TlsEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "FIELD_DEFINITION", - "description": "Location adjacent to a field definition.", + "name": "zeek", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "ZeekEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ARGUMENT_DEFINITION", - "description": "Location adjacent to an argument definition.", + "name": "http", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "HttpEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INTERFACE", - "description": "Location adjacent to an interface definition.", + "name": "url", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "UrlEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "UNION", - "description": "Location adjacent to a union definition.", + "name": "timestamp", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Date", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ENUM", - "description": "Location adjacent to an enum definition.", + "name": "message", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArray", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "ENUM_VALUE", - "description": "Location adjacent to an enum value definition.", + "name": "user", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "UserEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INPUT_OBJECT", - "description": "Location adjacent to an input object type definition.", + "name": "winlog", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "WinlogEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "INPUT_FIELD_DEFINITION", - "description": "Location adjacent to an input object field definition.", + "name": "process", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "ProcessEcsFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EcsEdges", - "description": "", - "fields": [ + }, { - "name": "node", + "name": "file", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "ECS", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "FileFields", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "cursor", + "name": "system", "description": "", "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } - }, + "type": { "kind": "OBJECT", "name": "SystemEcsField", "ofType": null }, "isDeprecated": false, "deprecationReason": null } @@ -12814,60 +7715,32 @@ }, { "kind": "OBJECT", - "name": "EventsTimelineData", + "name": "EcsEdges", "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": "EcsEdges", "ofType": null } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCount", + "name": "node", "description": "", "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "ECS", "ofType": null } }, "isDeprecated": false, "deprecationReason": null }, { - "name": "pageInfo", + "name": "cursor", "description": "", "args": [], "type": { "kind": "NON_NULL", "name": null, - "ofType": { "kind": "OBJECT", "name": "PageInfo", "ofType": null } + "ofType": { "kind": "OBJECT", "name": "CursorType", "ofType": null } }, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "inspect", - "description": "", - "args": [], - "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, - "isDeprecated": false, - "deprecationReason": null } ], "inputFields": null, @@ -13010,128 +7883,292 @@ "possibleTypes": null }, { - "kind": "ENUM", - "name": "NetworkDirectionEcs", + "kind": "SCALAR", + "name": "ToIFieldSubTypeNonNullable", "description": "", "fields": null, "inputFields": null, "interfaces": null, - "enumValues": [ + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "IndexField", + "description": "A descriptor of a field in an index", + "fields": [ { - "name": "inbound", - "description": "", + "name": "category", + "description": "Where the field belong", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "outbound", - "description": "", + "name": "example", + "description": "Example of field's value", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "internal", - "description": "", + "name": "indexes", + "description": "whether the field's belong to an alias index", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "external", - "description": "", + "name": "name", + "description": "The name of the field", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, "isDeprecated": false, "deprecationReason": null }, { - "name": "incoming", - "description": "", + "name": "type", + "description": "The type of the field's values as recognized by Kibana", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "searchable", + "description": "Whether the field's values can be efficiently searched for", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "aggregatable", + "description": "Whether the field's values can be aggregated", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Boolean", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": "Description of the field", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "outgoing", + "name": "format", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "esTypes", + "description": "the elastic type as mapped in the index", + "args": [], + "type": { "kind": "SCALAR", "name": "ToStringArrayNoNullable", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "listening", + "name": "subType", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "ToIFieldSubTypeNonNullable", "ofType": null }, "isDeprecated": false, "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "PaginationInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "limit", + "description": "The limit parameter allows you to configure the maximum amount of items to be returned", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "defaultValue": null }, - { "name": "unknown", "description": "", "isDeprecated": false, "deprecationReason": null } + { + "name": "cursor", + "description": "The cursor parameter defines the next result you want to fetch", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "tiebreaker", + "description": "The tiebreaker parameter allow to be more precise to fetch the next item", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + } ], + "interfaces": null, + "enumValues": null, "possibleTypes": null }, { "kind": "ENUM", - "name": "NetworkHttpFields", + "name": "FlowTarget", "description": "", "fields": null, "inputFields": null, "interfaces": null, "enumValues": [ + { "name": "client", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "domains", + "name": "destination", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "server", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } + ], + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "FlowTargetSourceDest", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "lastHost", + "name": "destination", "description": "", "isDeprecated": false, "deprecationReason": null }, + { "name": "source", "description": "", "isDeprecated": false, "deprecationReason": null } + ], + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "FlowDirection", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ { - "name": "lastSourceIp", + "name": "uniDirectional", "description": "", "isDeprecated": false, "deprecationReason": null }, { - "name": "methods", + "name": "biDirectional", "description": "", "isDeprecated": false, "deprecationReason": null - }, - { "name": "path", "description": "", "isDeprecated": false, "deprecationReason": null }, + } + ], + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "SortField", + "description": "", + "fields": null, + "inputFields": [ { - "name": "requestCount", + "name": "sortFieldId", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + }, + "defaultValue": null }, { - "name": "statuses", + "name": "direction", "description": "", - "isDeprecated": false, - "deprecationReason": null + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "ENUM", "name": "Direction", "ofType": null } + }, + "defaultValue": null } ], + "interfaces": null, + "enumValues": null, "possibleTypes": null }, { - "kind": "ENUM", - "name": "FlowDirection", + "kind": "OBJECT", + "name": "PageInfo", "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ + "fields": [ { - "name": "uniDirectional", + "name": "endCursor", "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "CursorType", "ofType": null }, "isDeprecated": false, "deprecationReason": null }, { - "name": "biDirectional", + "name": "hasNextPage", "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Boolean", "ofType": null }, "isDeprecated": false, "deprecationReason": null } ], + "inputFields": null, + "interfaces": [], + "enumValues": null, "possibleTypes": null }, { diff --git a/x-pack/plugins/security_solution/public/graphql/types.ts b/x-pack/plugins/security_solution/public/graphql/types.ts index 65d9212f77dcc..5cc8fd1f37d2e 100644 --- a/x-pack/plugins/security_solution/public/graphql/types.ts +++ b/x-pack/plugins/security_solution/public/graphql/types.ts @@ -40,65 +40,16 @@ export interface PaginationInputPaginated { querySize: number; } -export interface DocValueFieldsInput { - field: string; - - format: string; -} - -export interface PaginationInput { - /** The limit parameter allows you to configure the maximum amount of items to be returned */ - limit: number; - /** The cursor parameter defines the next result you want to fetch */ - cursor?: Maybe; - /** The tiebreaker parameter allow to be more precise to fetch the next item */ - tiebreaker?: Maybe; -} - -export interface SortField { - sortFieldId: string; - - direction: Direction; -} - -export interface LastTimeDetails { - hostName?: Maybe; - - ip?: Maybe; -} - export interface HostsSortField { field: HostsFields; direction: Direction; } -export interface UsersSortField { - field: UsersFields; - - direction: Direction; -} - -export interface NetworkTopTablesSortField { - field: NetworkTopTablesFields; - - direction: Direction; -} - -export interface NetworkDnsSortField { - field: NetworkDnsFields; - - direction: Direction; -} - -export interface NetworkHttpSortField { - direction: Direction; -} - -export interface TlsSortField { - field: TlsFields; +export interface DocValueFieldsInput { + field: string; - direction: Direction; + format: string; } export interface PageInfoTimeline { @@ -138,6 +89,8 @@ export interface TimelineInput { kqlQuery?: Maybe; + indexNames?: Maybe; + title?: Maybe; templateTimelineId?: Maybe; @@ -277,6 +230,21 @@ export interface SortTimelineInput { sortDirection?: Maybe; } +export interface PaginationInput { + /** The limit parameter allows you to configure the maximum amount of items to be returned */ + limit: number; + /** The cursor parameter defines the next result you want to fetch */ + cursor?: Maybe; + /** The tiebreaker parameter allow to be more precise to fetch the next item */ + tiebreaker?: Maybe; +} + +export interface SortField { + sortFieldId: string; + + direction: Direction; +} + export interface FavoriteTimelineInput { fullName?: Maybe; @@ -295,13 +263,6 @@ export enum Direction { desc = 'desc', } -export enum LastEventIndexKey { - hostDetails = 'hostDetails', - hosts = 'hosts', - ipDetails = 'ipDetails', - network = 'network', -} - export enum HostsFields { hostName = 'hostName', lastSeen = 'lastSeen', @@ -313,51 +274,6 @@ export enum HostPolicyResponseActionStatus { warning = 'warning', } -export enum UsersFields { - name = 'name', - count = 'count', -} - -export enum FlowTarget { - client = 'client', - destination = 'destination', - server = 'server', - source = 'source', -} - -export enum HistogramType { - authentications = 'authentications', - anomalies = 'anomalies', - events = 'events', - alerts = 'alerts', - dns = 'dns', -} - -export enum FlowTargetSourceDest { - destination = 'destination', - source = 'source', -} - -export enum NetworkTopTablesFields { - bytes_in = 'bytes_in', - bytes_out = 'bytes_out', - flows = 'flows', - destination_ips = 'destination_ips', - source_ips = 'source_ips', -} - -export enum NetworkDnsFields { - dnsName = 'dnsName', - queryCount = 'queryCount', - uniqueDomains = 'uniqueDomains', - dnsBytesIn = 'dnsBytesIn', - dnsBytesOut = 'dnsBytesOut', -} - -export enum TlsFields { - _id = '_id', -} - export enum DataProviderType { default = 'default', template = 'template', @@ -397,25 +313,16 @@ export enum SortFieldTimeline { created = 'created', } -export enum NetworkDirectionEcs { - inbound = 'inbound', - outbound = 'outbound', - internal = 'internal', - external = 'external', - incoming = 'incoming', - outgoing = 'outgoing', - listening = 'listening', - unknown = 'unknown', +export enum FlowTarget { + client = 'client', + destination = 'destination', + server = 'server', + source = 'source', } -export enum NetworkHttpFields { - domains = 'domains', - lastHost = 'lastHost', - lastSourceIp = 'lastSourceIp', - methods = 'methods', - path = 'path', - requestCount = 'requestCount', - statuses = 'statuses', +export enum FlowTargetSourceDest { + destination = 'destination', + source = 'source', } export enum FlowDirection { @@ -423,23 +330,21 @@ export enum FlowDirection { biDirectional = 'biDirectional', } -export type ToStringArrayNoNullable = any; - -export type ToIFieldSubTypeNonNullable = any; - export type ToStringArray = string[]; export type Date = string; -export type ToNumberArray = number[]; +export type ToAny = any; + +export type ToStringArrayNoNullable = any; export type ToDateArray = string[]; -export type ToBooleanArray = boolean[]; +export type ToNumberArray = number[]; -export type ToAny = any; +export type ToBooleanArray = boolean[]; -export type EsValue = any; +export type ToIFieldSubTypeNonNullable = any; // ==================================================== // Scalars @@ -528,52 +433,12 @@ export interface Source { configuration: SourceConfiguration; /** The status of the source */ status: SourceStatus; - /** Gets Authentication success and failures based on a timerange */ - Authentications: AuthenticationsData; - - Timeline: TimelineData; - - TimelineDetails: TimelineDetailsData; - - LastEventTime: LastEventTimeData; /** Gets Hosts based on timerange and specified criteria, or all events in the timerange if no criteria is specified */ Hosts: HostsData; HostOverview: HostItem; HostFirstLastSeen: FirstLastSeenHost; - - IpOverview?: Maybe; - - Users: UsersData; - - KpiNetwork?: Maybe; - - KpiHosts: KpiHostsData; - - KpiHostDetails: KpiHostDetailsData; - - MatrixHistogram: MatrixHistogramOverTimeData; - - NetworkTopCountries: NetworkTopCountriesData; - - NetworkTopNFlow: NetworkTopNFlowData; - - NetworkDns: NetworkDnsData; - - NetworkDnsHistogram: NetworkDsOverTimeData; - - NetworkHttp: NetworkHttpData; - - OverviewNetwork?: Maybe; - - OverviewHost?: Maybe; - - Tls: TlsData; - /** Gets UncommonProcesses based on a timerange, or all UncommonProcesses if no criteria is specified */ - UncommonProcesses: UncommonProcessesData; - /** Just a simple example to get the app name */ - whoAmI?: Maybe; } /** A set of configuration options for a security data source */ @@ -603,37 +468,11 @@ export interface SourceStatus { /** Whether the configured alias or wildcard pattern resolve to any auditbeat indices */ indicesExist: boolean; /** The list of fields defined in the index mappings */ - indexFields: IndexField[]; -} - -/** A descriptor of a field in an index */ -export interface IndexField { - /** Where the field belong */ - category: string; - /** Example of field's value */ - example?: Maybe; - /** whether the field's belong to an alias index */ - indexes: (Maybe)[]; - /** The name of the field */ - name: string; - /** The type of the field's values as recognized by Kibana */ - type: string; - /** Whether the field's values can be efficiently searched for */ - searchable: boolean; - /** Whether the field's values can be aggregated */ - aggregatable: boolean; - /** Description of the field */ - description?: Maybe; - - format?: Maybe; - /** the elastic type as mapped in the index */ - esTypes?: Maybe; - - subType?: Maybe; + indexFields: string[]; } -export interface AuthenticationsData { - edges: AuthenticationsEdges[]; +export interface HostsData { + edges: HostsEdges[]; totalCount: number; @@ -642,84 +481,50 @@ export interface AuthenticationsData { inspect?: Maybe; } -export interface AuthenticationsEdges { - node: AuthenticationItem; +export interface HostsEdges { + node: HostItem; cursor: CursorType; } -export interface AuthenticationItem { - _id: string; +export interface HostItem { + _id?: Maybe; - failures: number; + cloud?: Maybe; - successes: number; + endpoint?: Maybe; - user: UserEcsFields; + host?: Maybe; - lastSuccess?: Maybe; + inspect?: Maybe; - lastFailure?: Maybe; + lastSeen?: Maybe; } -export interface UserEcsFields { - domain?: Maybe; - - id?: Maybe; - - name?: Maybe; - - full_name?: Maybe; +export interface CloudFields { + instance?: Maybe; - email?: Maybe; + machine?: Maybe; - hash?: Maybe; + provider?: Maybe[]>; - group?: Maybe; + region?: Maybe[]>; } -export interface LastSourceHost { - timestamp?: Maybe; - - source?: Maybe; - - host?: Maybe; +export interface CloudInstance { + id?: Maybe[]>; } -export interface SourceEcsFields { - bytes?: Maybe; - - ip?: Maybe; - - port?: Maybe; - - domain?: Maybe; - - geo?: Maybe; - - packets?: Maybe; +export interface CloudMachine { + type?: Maybe[]>; } -export interface GeoEcsFields { - city_name?: Maybe; - - continent_name?: Maybe; - - country_iso_code?: Maybe; - - country_name?: Maybe; - - location?: Maybe; - - region_iso_code?: Maybe; - - region_name?: Maybe; -} +export interface EndpointFields { + endpointPolicy?: Maybe; -export interface Location { - lon?: Maybe; + sensorVersion?: Maybe; - lat?: Maybe; + policyStatus?: Maybe; } export interface HostEcsFields { @@ -752,6 +557,12 @@ export interface OsEcsFields { kernel?: Maybe; } +export interface Inspect { + dsl: string[]; + + response: string[]; +} + export interface CursorType { value?: Maybe; @@ -766,196 +577,267 @@ export interface PageInfoPaginated { showMorePagesIndicator: boolean; } -export interface Inspect { - dsl: string[]; +export interface FirstLastSeenHost { + inspect?: Maybe; - response: string[]; + firstSeen?: Maybe; + + lastSeen?: Maybe; } -export interface TimelineData { - edges: TimelineEdges[]; +export interface TimelineResult { + columns?: Maybe; - totalCount: number; + created?: Maybe; - pageInfo: PageInfo; + createdBy?: Maybe; - inspect?: Maybe; -} + dataProviders?: Maybe; -export interface TimelineEdges { - node: TimelineItem; + dateRange?: Maybe; - cursor: CursorType; -} + description?: Maybe; -export interface TimelineItem { - _id: string; + eventIdToNoteIds?: Maybe; - _index?: Maybe; + eventType?: Maybe; - data: TimelineNonEcsData[]; + excludedRowRendererIds?: Maybe; - ecs: Ecs; -} + favorite?: Maybe; -export interface TimelineNonEcsData { - field: string; + filters?: Maybe; - value?: Maybe; -} + kqlMode?: Maybe; -export interface Ecs { - _id: string; + kqlQuery?: Maybe; - _index?: Maybe; + indexNames?: Maybe; - agent?: Maybe; + notes?: Maybe; - auditd?: Maybe; + noteIds?: Maybe; - destination?: Maybe; + pinnedEventIds?: Maybe; - dns?: Maybe; + pinnedEventsSaveObject?: Maybe; - endgame?: Maybe; + savedQueryId?: Maybe; - event?: Maybe; + savedObjectId: string; - geo?: Maybe; + sort?: Maybe; - host?: Maybe; + status?: Maybe; - network?: Maybe; + title?: Maybe; - rule?: Maybe; + templateTimelineId?: Maybe; - signal?: Maybe; + templateTimelineVersion?: Maybe; - source?: Maybe; + timelineType?: Maybe; - suricata?: Maybe; + updated?: Maybe; - tls?: Maybe; + updatedBy?: Maybe; - zeek?: Maybe; + version: string; +} - http?: Maybe; +export interface ColumnHeaderResult { + aggregatable?: Maybe; - url?: Maybe; + category?: Maybe; - timestamp?: Maybe; + columnHeaderType?: Maybe; - message?: Maybe; + description?: Maybe; - user?: Maybe; + example?: Maybe; - winlog?: Maybe; + indexes?: Maybe; - process?: Maybe; + id?: Maybe; - file?: Maybe; + name?: Maybe; - system?: Maybe; -} + placeholder?: Maybe; -export interface AgentEcsField { - type?: Maybe; + searchable?: Maybe; + + type?: Maybe; } -export interface AuditdEcsFields { - result?: Maybe; +export interface DataProviderResult { + id?: Maybe; - session?: Maybe; + name?: Maybe; - data?: Maybe; + enabled?: Maybe; - summary?: Maybe

; + excluded?: Maybe; - sequence?: Maybe; + kqlQuery?: Maybe; + + queryMatch?: Maybe; + + type?: Maybe; + + and?: Maybe; } -export interface AuditdData { - acct?: Maybe; +export interface QueryMatchResult { + field?: Maybe; - terminal?: Maybe; + displayField?: Maybe; - op?: Maybe; + value?: Maybe; + + displayValue?: Maybe; + + operator?: Maybe; } -export interface Summary { - actor?: Maybe; +export interface DateRangePickerResult { + start?: Maybe; - object?: Maybe; + end?: Maybe; +} - how?: Maybe; +export interface FavoriteTimelineResult { + fullName?: Maybe; - message_type?: Maybe; + userName?: Maybe; - sequence?: Maybe; + favoriteDate?: Maybe; } -export interface PrimarySecondary { - primary?: Maybe; +export interface FilterTimelineResult { + exists?: Maybe; - secondary?: Maybe; + meta?: Maybe; - type?: Maybe; + match_all?: Maybe; + + missing?: Maybe; + + query?: Maybe; + + range?: Maybe; + + script?: Maybe; } -export interface DestinationEcsFields { - bytes?: Maybe; +export interface FilterMetaTimelineResult { + alias?: Maybe; - ip?: Maybe; + controlledBy?: Maybe; - port?: Maybe; + disabled?: Maybe; - domain?: Maybe; + field?: Maybe; - geo?: Maybe; + formattedValue?: Maybe; - packets?: Maybe; + index?: Maybe; + + key?: Maybe; + + negate?: Maybe; + + params?: Maybe; + + type?: Maybe; + + value?: Maybe; } -export interface DnsEcsFields { - question?: Maybe; +export interface SerializedFilterQueryResult { + filterQuery?: Maybe; +} - resolved_ip?: Maybe; +export interface SerializedKueryQueryResult { + kuery?: Maybe; - response_code?: Maybe; + serializedQuery?: Maybe; } -export interface DnsQuestionData { - name?: Maybe; +export interface KueryFilterQueryResult { + kind?: Maybe; - type?: Maybe; + expression?: Maybe; } -export interface EndgameEcsFields { - exit_code?: Maybe; +export interface SortTimelineResult { + columnId?: Maybe; - file_name?: Maybe; + sortDirection?: Maybe; +} - file_path?: Maybe; +export interface ResponseTimelines { + timeline: Maybe[]; - logon_type?: Maybe; + totalCount?: Maybe; - parent_process_name?: Maybe; + defaultTimelineCount?: Maybe; - pid?: Maybe; + templateTimelineCount?: Maybe; - process_name?: Maybe; + elasticTemplateTimelineCount?: Maybe; - subject_domain_name?: Maybe; + customTemplateTimelineCount?: Maybe; - subject_logon_id?: Maybe; + favoriteCount?: Maybe; +} - subject_user_name?: Maybe; +export interface Mutation { + /** Persists a note */ + persistNote: ResponseNote; - target_domain_name?: Maybe; + deleteNote?: Maybe; - target_logon_id?: Maybe; + deleteNoteByTimelineId?: Maybe; + /** Persists a pinned event in a timeline */ + persistPinnedEventOnTimeline?: Maybe; + /** Remove a pinned events in a timeline */ + deletePinnedEventOnTimeline: boolean; + /** Remove all pinned events in a timeline */ + deleteAllPinnedEventsOnTimeline: boolean; + /** Persists a timeline */ + persistTimeline: ResponseTimeline; - target_user_name?: Maybe; + persistFavorite: ResponseFavoriteTimeline; + + deleteTimeline: boolean; +} + +export interface ResponseNote { + code?: Maybe; + + message?: Maybe; + + note: NoteResult; +} + +export interface ResponseTimeline { + code?: Maybe; + + message?: Maybe; + + timeline: TimelineResult; +} + +export interface ResponseFavoriteTimeline { + code?: Maybe; + + message?: Maybe; + + savedObjectId: string; + + version: string; + + favorite?: Maybe; } export interface EventEcsFields { @@ -998,110 +880,176 @@ export interface EventEcsFields { type?: Maybe; } -export interface NetworkEcsField { - bytes?: Maybe; +export interface Location { + lon?: Maybe; - community_id?: Maybe; + lat?: Maybe; +} - direction?: Maybe; +export interface GeoEcsFields { + city_name?: Maybe; - packets?: Maybe; + continent_name?: Maybe; - protocol?: Maybe; + country_iso_code?: Maybe; - transport?: Maybe; -} + country_name?: Maybe; -export interface RuleEcsField { - reference?: Maybe; + location?: Maybe; + + region_iso_code?: Maybe; + + region_name?: Maybe; } -export interface SignalField { - rule?: Maybe; +export interface PrimarySecondary { + primary?: Maybe; - original_time?: Maybe; + secondary?: Maybe; - status?: Maybe; + type?: Maybe; } -export interface RuleField { - id?: Maybe; +export interface Summary { + actor?: Maybe; - rule_id?: Maybe; + object?: Maybe; - false_positives: string[]; + how?: Maybe; - saved_id?: Maybe; + message_type?: Maybe; - timeline_id?: Maybe; + sequence?: Maybe; +} - timeline_title?: Maybe; +export interface AgentEcsField { + type?: Maybe; +} - max_signals?: Maybe; +export interface AuditdData { + acct?: Maybe; - risk_score?: Maybe; + terminal?: Maybe; - output_index?: Maybe; + op?: Maybe; +} - description?: Maybe; +export interface AuditdEcsFields { + result?: Maybe; - from?: Maybe; + session?: Maybe; - immutable?: Maybe; + data?: Maybe; - index?: Maybe; + summary?: Maybe; - interval?: Maybe; + sequence?: Maybe; +} - language?: Maybe; +export interface Thread { + id?: Maybe; - query?: Maybe; + start?: Maybe; +} - references?: Maybe; +export interface ProcessHashData { + md5?: Maybe; - severity?: Maybe; + sha1?: Maybe; - tags?: Maybe; + sha256?: Maybe; +} - threat?: Maybe; +export interface ProcessEcsFields { + hash?: Maybe; - type?: Maybe; + pid?: Maybe; - size?: Maybe; + name?: Maybe; - to?: Maybe; + ppid?: Maybe; - enabled?: Maybe; + args?: Maybe; - filters?: Maybe; + entity_id?: Maybe; - created_at?: Maybe; + executable?: Maybe; - updated_at?: Maybe; + title?: Maybe; - created_by?: Maybe; + thread?: Maybe; - updated_by?: Maybe; + working_directory?: Maybe; +} - version?: Maybe; +export interface SourceEcsFields { + bytes?: Maybe; - note?: Maybe; + ip?: Maybe; - threshold?: Maybe; + port?: Maybe; - exceptions_list?: Maybe; + domain?: Maybe; + + geo?: Maybe; + + packets?: Maybe; } -export interface SuricataEcsFields { - eve?: Maybe; +export interface DestinationEcsFields { + bytes?: Maybe; + + ip?: Maybe; + + port?: Maybe; + + domain?: Maybe; + + geo?: Maybe; + + packets?: Maybe; } -export interface SuricataEveData { - alert?: Maybe; +export interface DnsQuestionData { + name?: Maybe; - flow_id?: Maybe; + type?: Maybe; +} - proto?: Maybe; +export interface DnsEcsFields { + question?: Maybe; + + resolved_ip?: Maybe; + + response_code?: Maybe; +} + +export interface EndgameEcsFields { + exit_code?: Maybe; + + file_name?: Maybe; + + file_path?: Maybe; + + logon_type?: Maybe; + + parent_process_name?: Maybe; + + pid?: Maybe; + + process_name?: Maybe; + + subject_domain_name?: Maybe; + + subject_logon_id?: Maybe; + + subject_user_name?: Maybe; + + target_domain_name?: Maybe; + + target_logon_id?: Maybe; + + target_user_name?: Maybe; } export interface SuricataAlertData { @@ -1110,48 +1058,44 @@ export interface SuricataAlertData { signature_id?: Maybe; } -export interface TlsEcsFields { - client_certificate?: Maybe; +export interface SuricataEveData { + alert?: Maybe; - fingerprints?: Maybe; + flow_id?: Maybe; - server_certificate?: Maybe; + proto?: Maybe; } -export interface TlsClientCertificateData { - fingerprint?: Maybe; +export interface SuricataEcsFields { + eve?: Maybe; } -export interface FingerprintData { - sha1?: Maybe; +export interface TlsJa3Data { + hash?: Maybe; } -export interface TlsFingerprintsData { - ja3?: Maybe; +export interface FingerprintData { + sha1?: Maybe; } -export interface TlsJa3Data { - hash?: Maybe; +export interface TlsClientCertificateData { + fingerprint?: Maybe; } export interface TlsServerCertificateData { fingerprint?: Maybe; } -export interface ZeekEcsFields { - session_id?: Maybe; - - connection?: Maybe; - - notice?: Maybe; - - dns?: Maybe; +export interface TlsFingerprintsData { + ja3?: Maybe; +} - http?: Maybe; +export interface TlsEcsFields { + client_certificate?: Maybe; - files?: Maybe; + fingerprints?: Maybe; - ssl?: Maybe; + server_certificate?: Maybe; } export interface ZeekConnectionData { @@ -1206,64 +1150,72 @@ export interface ZeekDnsData { TC?: Maybe; } -export interface ZeekHttpData { - resp_mime_types?: Maybe; - - trans_depth?: Maybe; +export interface FileFields { + name?: Maybe; - status_msg?: Maybe; + path?: Maybe; - resp_fuids?: Maybe; + target_path?: Maybe; - tags?: Maybe; -} + extension?: Maybe; -export interface ZeekFileData { - session_ids?: Maybe; + type?: Maybe; - timedout?: Maybe; + device?: Maybe; - local_orig?: Maybe; + inode?: Maybe; - tx_host?: Maybe; + uid?: Maybe; - source?: Maybe; + owner?: Maybe; - is_orig?: Maybe; + gid?: Maybe; - overflow_bytes?: Maybe; + group?: Maybe; - sha1?: Maybe; + mode?: Maybe; - duration?: Maybe; + size?: Maybe; - depth?: Maybe; + mtime?: Maybe; - analyzers?: Maybe; + ctime?: Maybe; +} - mime_type?: Maybe; +export interface ZeekHttpData { + resp_mime_types?: Maybe; - rx_host?: Maybe; + trans_depth?: Maybe; - total_bytes?: Maybe; + status_msg?: Maybe; - fuid?: Maybe; + resp_fuids?: Maybe; - seen_bytes?: Maybe; + tags?: Maybe; +} - missing_bytes?: Maybe; +export interface HttpBodyData { + content?: Maybe; - md5?: Maybe; + bytes?: Maybe; } -export interface ZeekSslData { - cipher?: Maybe; +export interface HttpRequestData { + method?: Maybe; - established?: Maybe; + body?: Maybe; - resumed?: Maybe; + referrer?: Maybe; - version?: Maybe; + bytes?: Maybe; +} + +export interface HttpResponseData { + status_code?: Maybe; + + body?: Maybe; + + bytes?: Maybe; } export interface HttpEcsFields { @@ -1274,120 +1226,112 @@ export interface HttpEcsFields { response?: Maybe; } -export interface HttpRequestData { - method?: Maybe; - - body?: Maybe; - - referrer?: Maybe; +export interface UrlEcsFields { + domain?: Maybe; - bytes?: Maybe; -} + original?: Maybe; -export interface HttpBodyData { - content?: Maybe; + username?: Maybe; - bytes?: Maybe; + password?: Maybe; } -export interface HttpResponseData { - status_code?: Maybe; +export interface ZeekFileData { + session_ids?: Maybe; - body?: Maybe; + timedout?: Maybe; - bytes?: Maybe; -} + local_orig?: Maybe; -export interface UrlEcsFields { - domain?: Maybe; + tx_host?: Maybe; - original?: Maybe; + source?: Maybe; - username?: Maybe; + is_orig?: Maybe; - password?: Maybe; -} + overflow_bytes?: Maybe; -export interface WinlogEcsFields { - event_id?: Maybe; -} + sha1?: Maybe; -export interface ProcessEcsFields { - hash?: Maybe; + duration?: Maybe; - pid?: Maybe; + depth?: Maybe; - name?: Maybe; + analyzers?: Maybe; - ppid?: Maybe; + mime_type?: Maybe; - args?: Maybe; + rx_host?: Maybe; - entity_id?: Maybe; + total_bytes?: Maybe; - executable?: Maybe; + fuid?: Maybe; - title?: Maybe; + seen_bytes?: Maybe; - thread?: Maybe; + missing_bytes?: Maybe; - working_directory?: Maybe; + md5?: Maybe; } -export interface ProcessHashData { - md5?: Maybe; +export interface ZeekSslData { + cipher?: Maybe; - sha1?: Maybe; + established?: Maybe; - sha256?: Maybe; + resumed?: Maybe; + + version?: Maybe; } -export interface Thread { - id?: Maybe; +export interface ZeekEcsFields { + session_id?: Maybe; - start?: Maybe; -} + connection?: Maybe; -export interface FileFields { - name?: Maybe; + notice?: Maybe; - path?: Maybe; + dns?: Maybe; - target_path?: Maybe; + http?: Maybe; - extension?: Maybe; + files?: Maybe; - type?: Maybe; + ssl?: Maybe; +} - device?: Maybe; +export interface UserEcsFields { + domain?: Maybe; - inode?: Maybe; + id?: Maybe; - uid?: Maybe; + name?: Maybe; - owner?: Maybe; + full_name?: Maybe; - gid?: Maybe; + email?: Maybe; + + hash?: Maybe; group?: Maybe; +} - mode?: Maybe; +export interface WinlogEcsFields { + event_id?: Maybe; +} - size?: Maybe; +export interface NetworkEcsField { + bytes?: Maybe; - mtime?: Maybe; + community_id?: Maybe; - ctime?: Maybe; -} + direction?: Maybe; -export interface SystemEcsField { - audit?: Maybe; + packets?: Maybe; - auth?: Maybe; -} + protocol?: Maybe; -export interface AuditEcsFields { - package?: Maybe; + transport?: Maybe; } export interface PackageEcsFields { @@ -1404,8 +1348,8 @@ export interface PackageEcsFields { version?: Maybe; } -export interface AuthEcsFields { - ssh?: Maybe; +export interface AuditEcsFields { + package?: Maybe; } export interface SshEcsFields { @@ -1414,4061 +1358,760 @@ export interface SshEcsFields { signature?: Maybe; } -export interface PageInfo { - endCursor?: Maybe; - - hasNextPage?: Maybe; +export interface AuthEcsFields { + ssh?: Maybe; } -export interface TimelineDetailsData { - data?: Maybe; +export interface SystemEcsField { + audit?: Maybe; - inspect?: Maybe; + auth?: Maybe; } -export interface DetailItem { - field: string; - - values?: Maybe; - - originalValue?: Maybe; -} +export interface RuleField { + id?: Maybe; -export interface LastEventTimeData { - lastSeen?: Maybe; + rule_id?: Maybe; - inspect?: Maybe; -} + false_positives: string[]; -export interface HostsData { - edges: HostsEdges[]; + saved_id?: Maybe; - totalCount: number; + timeline_id?: Maybe; - pageInfo: PageInfoPaginated; + timeline_title?: Maybe; - inspect?: Maybe; -} + max_signals?: Maybe; -export interface HostsEdges { - node: HostItem; + risk_score?: Maybe; - cursor: CursorType; -} + output_index?: Maybe; -export interface HostItem { - _id?: Maybe; + description?: Maybe; - cloud?: Maybe; + from?: Maybe; - endpoint?: Maybe; + immutable?: Maybe; - host?: Maybe; + index?: Maybe; - inspect?: Maybe; + interval?: Maybe; - lastSeen?: Maybe; -} + language?: Maybe; -export interface CloudFields { - instance?: Maybe; + query?: Maybe; - machine?: Maybe; + references?: Maybe; - provider?: Maybe<(Maybe)[]>; + severity?: Maybe; - region?: Maybe<(Maybe)[]>; -} + tags?: Maybe; -export interface CloudInstance { - id?: Maybe<(Maybe)[]>; -} + threat?: Maybe; -export interface CloudMachine { - type?: Maybe<(Maybe)[]>; -} + type?: Maybe; -export interface EndpointFields { - endpointPolicy?: Maybe; + size?: Maybe; - sensorVersion?: Maybe; + to?: Maybe; - policyStatus?: Maybe; -} + enabled?: Maybe; -export interface FirstLastSeenHost { - inspect?: Maybe; + filters?: Maybe; - firstSeen?: Maybe; + created_at?: Maybe; - lastSeen?: Maybe; -} + updated_at?: Maybe; -export interface IpOverviewData { - client?: Maybe; + created_by?: Maybe; - destination?: Maybe; + updated_by?: Maybe; - host: HostEcsFields; + version?: Maybe; - server?: Maybe; + note?: Maybe; - source?: Maybe; + threshold?: Maybe; - inspect?: Maybe; + exceptions_list?: Maybe; } -export interface Overview { - firstSeen?: Maybe; - - lastSeen?: Maybe; +export interface SignalField { + rule?: Maybe; - autonomousSystem: AutonomousSystem; + original_time?: Maybe; - geo: GeoEcsFields; + status?: Maybe; } -export interface AutonomousSystem { - number?: Maybe; - - organization?: Maybe; +export interface RuleEcsField { + reference?: Maybe; } -export interface AutonomousSystemOrganization { - name?: Maybe; -} +export interface Ecs { + _id: string; -export interface UsersData { - edges: UsersEdges[]; + _index?: Maybe; - totalCount: number; + agent?: Maybe; - pageInfo: PageInfoPaginated; + auditd?: Maybe; - inspect?: Maybe; -} + destination?: Maybe; -export interface UsersEdges { - node: UsersNode; + dns?: Maybe; - cursor: CursorType; -} + endgame?: Maybe; -export interface UsersNode { - _id?: Maybe; + event?: Maybe; - timestamp?: Maybe; + geo?: Maybe; - user?: Maybe; -} + host?: Maybe; -export interface UsersItem { - name?: Maybe; + network?: Maybe; - id?: Maybe; + rule?: Maybe; - groupId?: Maybe; + signal?: Maybe; - groupName?: Maybe; + source?: Maybe; - count?: Maybe; -} + suricata?: Maybe; -export interface KpiNetworkData { - networkEvents?: Maybe; + tls?: Maybe; - uniqueFlowId?: Maybe; + zeek?: Maybe; - uniqueSourcePrivateIps?: Maybe; + http?: Maybe; - uniqueSourcePrivateIpsHistogram?: Maybe; + url?: Maybe; - uniqueDestinationPrivateIps?: Maybe; + timestamp?: Maybe; - uniqueDestinationPrivateIpsHistogram?: Maybe; + message?: Maybe; - dnsQueries?: Maybe; + user?: Maybe; - tlsHandshakes?: Maybe; + winlog?: Maybe; - inspect?: Maybe; -} + process?: Maybe; -export interface KpiNetworkHistogramData { - x?: Maybe; + file?: Maybe; - y?: Maybe; + system?: Maybe; } -export interface KpiHostsData { - hosts?: Maybe; - - hostsHistogram?: Maybe; - - authSuccess?: Maybe; - - authSuccessHistogram?: Maybe; - - authFailure?: Maybe; - - authFailureHistogram?: Maybe; +export interface EcsEdges { + node: Ecs; - uniqueSourceIps?: Maybe; + cursor: CursorType; +} - uniqueSourceIpsHistogram?: Maybe; +export interface OsFields { + platform?: Maybe; - uniqueDestinationIps?: Maybe; + name?: Maybe; - uniqueDestinationIpsHistogram?: Maybe; + full?: Maybe; - inspect?: Maybe; -} + family?: Maybe; -export interface KpiHostHistogramData { - x?: Maybe; + version?: Maybe; - y?: Maybe; + kernel?: Maybe; } -export interface KpiHostDetailsData { - authSuccess?: Maybe; - - authSuccessHistogram?: Maybe; - - authFailure?: Maybe; +export interface HostFields { + architecture?: Maybe; - authFailureHistogram?: Maybe; + id?: Maybe; - uniqueSourceIps?: Maybe; + ip?: Maybe[]>; - uniqueSourceIpsHistogram?: Maybe; + mac?: Maybe[]>; - uniqueDestinationIps?: Maybe; + name?: Maybe; - uniqueDestinationIpsHistogram?: Maybe; + os?: Maybe; - inspect?: Maybe; + type?: Maybe; } -export interface MatrixHistogramOverTimeData { - inspect?: Maybe; +/** A descriptor of a field in an index */ +export interface IndexField { + /** Where the field belong */ + category: string; + /** Example of field's value */ + example?: Maybe; + /** whether the field's belong to an alias index */ + indexes: Maybe[]; + /** The name of the field */ + name: string; + /** The type of the field's values as recognized by Kibana */ + type: string; + /** Whether the field's values can be efficiently searched for */ + searchable: boolean; + /** Whether the field's values can be aggregated */ + aggregatable: boolean; + /** Description of the field */ + description?: Maybe; - matrixHistogramData: MatrixOverTimeHistogramData[]; + format?: Maybe; + /** the elastic type as mapped in the index */ + esTypes?: Maybe; - totalCount: number; + subType?: Maybe; } -export interface MatrixOverTimeHistogramData { - x?: Maybe; - - y?: Maybe; +export interface PageInfo { + endCursor?: Maybe; - g?: Maybe; + hasNextPage?: Maybe; } -export interface NetworkTopCountriesData { - edges: NetworkTopCountriesEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; +// ==================================================== +// Arguments +// ==================================================== - inspect?: Maybe; +export interface GetNoteQueryArgs { + id: string; } - -export interface NetworkTopCountriesEdges { - node: NetworkTopCountriesItem; - - cursor: CursorType; +export interface GetNotesByTimelineIdQueryArgs { + timelineId: string; } - -export interface NetworkTopCountriesItem { - _id?: Maybe; - - source?: Maybe; - - destination?: Maybe; - - network?: Maybe; +export interface GetNotesByEventIdQueryArgs { + eventId: string; } +export interface GetAllNotesQueryArgs { + pageInfo?: Maybe; -export interface TopCountriesItemSource { - country?: Maybe; - - destination_ips?: Maybe; - - flows?: Maybe; - - location?: Maybe; + search?: Maybe; - source_ips?: Maybe; + sort?: Maybe; } - -export interface GeoItem { - geo?: Maybe; - - flowTarget?: Maybe; +export interface GetAllPinnedEventsByTimelineIdQueryArgs { + timelineId: string; } - -export interface TopCountriesItemDestination { - country?: Maybe; - - destination_ips?: Maybe; - - flows?: Maybe; - - location?: Maybe; - - source_ips?: Maybe; +export interface SourceQueryArgs { + /** The id of the source */ + id: string; } - -export interface TopNetworkTablesEcsField { - bytes_in?: Maybe; - - bytes_out?: Maybe; +export interface GetOneTimelineQueryArgs { + id: string; } +export interface GetAllTimelineQueryArgs { + pageInfo: PageInfoTimeline; -export interface NetworkTopNFlowData { - edges: NetworkTopNFlowEdges[]; + search?: Maybe; - totalCount: number; + sort?: Maybe; - pageInfo: PageInfoPaginated; + onlyUserFavorite?: Maybe; - inspect?: Maybe; + timelineType?: Maybe; + + status?: Maybe; } +export interface HostsSourceArgs { + id?: Maybe; -export interface NetworkTopNFlowEdges { - node: NetworkTopNFlowItem; + timerange: TimerangeInput; - cursor: CursorType; -} + pagination: PaginationInputPaginated; -export interface NetworkTopNFlowItem { - _id?: Maybe; + sort: HostsSortField; - source?: Maybe; + filterQuery?: Maybe; - destination?: Maybe; + defaultIndex: string[]; - network?: Maybe; + docValueFields: DocValueFieldsInput[]; } +export interface HostOverviewSourceArgs { + id?: Maybe; -export interface TopNFlowItemSource { - autonomous_system?: Maybe; + hostName: string; - domain?: Maybe; + timerange: TimerangeInput; - ip?: Maybe; + defaultIndex: string[]; +} +export interface HostFirstLastSeenSourceArgs { + id?: Maybe; - location?: Maybe; + hostName: string; - flows?: Maybe; + defaultIndex: string[]; - destination_ips?: Maybe; + docValueFields: DocValueFieldsInput[]; } - -export interface AutonomousSystemItem { - name?: Maybe; - - number?: Maybe; +export interface IndicesExistSourceStatusArgs { + defaultIndex: string[]; } - -export interface TopNFlowItemDestination { - autonomous_system?: Maybe; - - domain?: Maybe; - - ip?: Maybe; - - location?: Maybe; - - flows?: Maybe; - - source_ips?: Maybe; +export interface IndexFieldsSourceStatusArgs { + defaultIndex: string[]; } +export interface PersistNoteMutationArgs { + noteId?: Maybe; -export interface NetworkDnsData { - edges: NetworkDnsEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; + version?: Maybe; - histogram?: Maybe; + note: NoteInput; } - -export interface NetworkDnsEdges { - node: NetworkDnsItem; - - cursor: CursorType; +export interface DeleteNoteMutationArgs { + id: string[]; } +export interface DeleteNoteByTimelineIdMutationArgs { + timelineId: string; -export interface NetworkDnsItem { - _id?: Maybe; - - dnsBytesIn?: Maybe; - - dnsBytesOut?: Maybe; - - dnsName?: Maybe; - - queryCount?: Maybe; - - uniqueDomains?: Maybe; + version?: Maybe; } +export interface PersistPinnedEventOnTimelineMutationArgs { + pinnedEventId?: Maybe; -export interface MatrixOverOrdinalHistogramData { - x: string; - - y: number; + eventId: string; - g: string; + timelineId?: Maybe; } - -export interface NetworkDsOverTimeData { - inspect?: Maybe; - - matrixHistogramData: MatrixOverTimeHistogramData[]; - - totalCount: number; +export interface DeletePinnedEventOnTimelineMutationArgs { + id: string[]; } - -export interface NetworkHttpData { - edges: NetworkHttpEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; +export interface DeleteAllPinnedEventsOnTimelineMutationArgs { + timelineId: string; } +export interface PersistTimelineMutationArgs { + id?: Maybe; -export interface NetworkHttpEdges { - node: NetworkHttpItem; + version?: Maybe; - cursor: CursorType; + timeline: TimelineInput; } - -export interface NetworkHttpItem { - _id?: Maybe; - - domains: string[]; - - lastHost?: Maybe; - - lastSourceIp?: Maybe; - - methods: string[]; - - path?: Maybe; - - requestCount?: Maybe; - - statuses: string[]; +export interface PersistFavoriteMutationArgs { + timelineId?: Maybe; +} +export interface DeleteTimelineMutationArgs { + id: string[]; } -export interface OverviewNetworkData { - auditbeatSocket?: Maybe; - - filebeatCisco?: Maybe; +// ==================================================== +// Documents +// ==================================================== - filebeatNetflow?: Maybe; +export namespace GetHostOverviewQuery { + export type Variables = { + sourceId: string; + hostName: string; + timerange: TimerangeInput; + defaultIndex: string[]; + inspect: boolean; + }; - filebeatPanw?: Maybe; + export type Query = { + __typename?: 'Query'; - filebeatSuricata?: Maybe; + source: Source; + }; - filebeatZeek?: Maybe; + export type Source = { + __typename?: 'Source'; - packetbeatDNS?: Maybe; + id: string; - packetbeatFlow?: Maybe; + HostOverview: HostOverview; + }; - packetbeatTLS?: Maybe; + export type HostOverview = { + __typename?: 'HostItem'; - inspect?: Maybe; -} + _id: Maybe; -export interface OverviewHostData { - auditbeatAuditd?: Maybe; + host: Maybe; - auditbeatFIM?: Maybe; + cloud: Maybe; - auditbeatLogin?: Maybe; + inspect: Maybe; - auditbeatPackage?: Maybe; + endpoint: Maybe; + }; - auditbeatProcess?: Maybe; - - auditbeatUser?: Maybe; - - endgameDns?: Maybe; - - endgameFile?: Maybe; - - endgameImageLoad?: Maybe; - - endgameNetwork?: Maybe; - - endgameProcess?: Maybe; - - endgameRegistry?: Maybe; - - endgameSecurity?: Maybe; - - filebeatSystemModule?: Maybe; - - winlogbeatSecurity?: Maybe; - - winlogbeatMWSysmonOperational?: Maybe; - - inspect?: Maybe; -} - -export interface TlsData { - edges: TlsEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface TlsEdges { - node: TlsNode; - - cursor: CursorType; -} - -export interface TlsNode { - _id?: Maybe; - - timestamp?: Maybe; - - notAfter?: Maybe; - - subjects?: Maybe; - - ja3?: Maybe; - - issuers?: Maybe; -} - -export interface UncommonProcessesData { - edges: UncommonProcessesEdges[]; - - totalCount: number; - - pageInfo: PageInfoPaginated; - - inspect?: Maybe; -} - -export interface UncommonProcessesEdges { - node: UncommonProcessItem; - - cursor: CursorType; -} - -export interface UncommonProcessItem { - _id: string; - - instances: number; - - process: ProcessEcsFields; - - hosts: HostEcsFields[]; - - user?: Maybe; -} - -export interface SayMyName { - /** The id of the source */ - appName: string; -} - -export interface TimelineResult { - columns?: Maybe; - - created?: Maybe; - - createdBy?: Maybe; - - dataProviders?: Maybe; - - dateRange?: Maybe; - - description?: Maybe; - - eventIdToNoteIds?: Maybe; - - eventType?: Maybe; - - excludedRowRendererIds?: Maybe; - - favorite?: Maybe; - - filters?: Maybe; - - kqlMode?: Maybe; - - kqlQuery?: Maybe; - - notes?: Maybe; - - noteIds?: Maybe; - - pinnedEventIds?: Maybe; - - pinnedEventsSaveObject?: Maybe; - - savedQueryId?: Maybe; - - savedObjectId: string; - - sort?: Maybe; - - status?: Maybe; - - title?: Maybe; - - templateTimelineId?: Maybe; - - templateTimelineVersion?: Maybe; - - timelineType?: Maybe; - - updated?: Maybe; - - updatedBy?: Maybe; - - version: string; -} - -export interface ColumnHeaderResult { - aggregatable?: Maybe; - - category?: Maybe; - - columnHeaderType?: Maybe; - - description?: Maybe; - - example?: Maybe; - - indexes?: Maybe; - - id?: Maybe; - - name?: Maybe; - - placeholder?: Maybe; - - searchable?: Maybe; - - type?: Maybe; -} - -export interface DataProviderResult { - id?: Maybe; - - name?: Maybe; - - enabled?: Maybe; - - excluded?: Maybe; - - kqlQuery?: Maybe; - - queryMatch?: Maybe; - - type?: Maybe; - - and?: Maybe; -} - -export interface QueryMatchResult { - field?: Maybe; - - displayField?: Maybe; - - value?: Maybe; - - displayValue?: Maybe; - - operator?: Maybe; -} - -export interface DateRangePickerResult { - start?: Maybe; - - end?: Maybe; -} - -export interface FavoriteTimelineResult { - fullName?: Maybe; - - userName?: Maybe; - - favoriteDate?: Maybe; -} - -export interface FilterTimelineResult { - exists?: Maybe; - - meta?: Maybe; - - match_all?: Maybe; - - missing?: Maybe; - - query?: Maybe; - - range?: Maybe; - - script?: Maybe; -} - -export interface FilterMetaTimelineResult { - alias?: Maybe; - - controlledBy?: Maybe; - - disabled?: Maybe; - - field?: Maybe; - - formattedValue?: Maybe; - - index?: Maybe; - - key?: Maybe; - - negate?: Maybe; - - params?: Maybe; - - type?: Maybe; - - value?: Maybe; -} - -export interface SerializedFilterQueryResult { - filterQuery?: Maybe; -} - -export interface SerializedKueryQueryResult { - kuery?: Maybe; - - serializedQuery?: Maybe; -} - -export interface KueryFilterQueryResult { - kind?: Maybe; - - expression?: Maybe; -} - -export interface SortTimelineResult { - columnId?: Maybe; - - sortDirection?: Maybe; -} - -export interface ResponseTimelines { - timeline: (Maybe)[]; - - totalCount?: Maybe; - - defaultTimelineCount?: Maybe; - - templateTimelineCount?: Maybe; - - elasticTemplateTimelineCount?: Maybe; - - customTemplateTimelineCount?: Maybe; - - favoriteCount?: Maybe; -} - -export interface Mutation { - /** Persists a note */ - persistNote: ResponseNote; - - deleteNote?: Maybe; - - deleteNoteByTimelineId?: Maybe; - /** Persists a pinned event in a timeline */ - persistPinnedEventOnTimeline?: Maybe; - /** Remove a pinned events in a timeline */ - deletePinnedEventOnTimeline: boolean; - /** Remove all pinned events in a timeline */ - deleteAllPinnedEventsOnTimeline: boolean; - /** Persists a timeline */ - persistTimeline: ResponseTimeline; - - persistFavorite: ResponseFavoriteTimeline; - - deleteTimeline: boolean; -} - -export interface ResponseNote { - code?: Maybe; - - message?: Maybe; - - note: NoteResult; -} - -export interface ResponseTimeline { - code?: Maybe; - - message?: Maybe; - - timeline: TimelineResult; -} - -export interface ResponseFavoriteTimeline { - code?: Maybe; - - message?: Maybe; - - savedObjectId: string; - - version: string; - - favorite?: Maybe; -} - -export interface EcsEdges { - node: Ecs; - - cursor: CursorType; -} - -export interface EventsTimelineData { - edges: EcsEdges[]; - - totalCount: number; - - pageInfo: PageInfo; - - inspect?: Maybe; -} - -export interface OsFields { - platform?: Maybe; - - name?: Maybe; - - full?: Maybe; - - family?: Maybe; - - version?: Maybe; - - kernel?: Maybe; -} - -export interface HostFields { - architecture?: Maybe; - - id?: Maybe; - - ip?: Maybe<(Maybe)[]>; - - mac?: Maybe<(Maybe)[]>; - - name?: Maybe; - - os?: Maybe; - - type?: Maybe; -} - -// ==================================================== -// Arguments -// ==================================================== - -export interface GetNoteQueryArgs { - id: string; -} -export interface GetNotesByTimelineIdQueryArgs { - timelineId: string; -} -export interface GetNotesByEventIdQueryArgs { - eventId: string; -} -export interface GetAllNotesQueryArgs { - pageInfo?: Maybe; - - search?: Maybe; - - sort?: Maybe; -} -export interface GetAllPinnedEventsByTimelineIdQueryArgs { - timelineId: string; -} -export interface SourceQueryArgs { - /** The id of the source */ - id: string; -} -export interface GetOneTimelineQueryArgs { - id: string; -} -export interface GetAllTimelineQueryArgs { - pageInfo: PageInfoTimeline; - - search?: Maybe; - - sort?: Maybe; - - onlyUserFavorite?: Maybe; - - timelineType?: Maybe; - - status?: Maybe; -} -export interface AuthenticationsSourceArgs { - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface TimelineSourceArgs { - pagination: PaginationInput; - - sortField: SortField; - - fieldRequested: string[]; - - timerange?: Maybe; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface TimelineDetailsSourceArgs { - eventId: string; - - indexName: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface LastEventTimeSourceArgs { - id?: Maybe; - - indexKey: LastEventIndexKey; - - details: LastTimeDetails; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface HostsSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - sort: HostsSortField; - - filterQuery?: Maybe; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface HostOverviewSourceArgs { - id?: Maybe; - - hostName: string; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface HostFirstLastSeenSourceArgs { - id?: Maybe; - - hostName: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface IpOverviewSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip: string; - - defaultIndex: string[]; - - docValueFields: DocValueFieldsInput[]; -} -export interface UsersSourceArgs { - filterQuery?: Maybe; - - id?: Maybe; - - ip: string; - - pagination: PaginationInputPaginated; - - sort: UsersSortField; - - flowTarget: FlowTarget; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface KpiNetworkSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface KpiHostsSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface KpiHostDetailsSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface MatrixHistogramSourceArgs { - filterQuery?: Maybe; - - defaultIndex: string[]; - - timerange: TimerangeInput; - - stackByField: string; - - histogramType: HistogramType; -} -export interface NetworkTopCountriesSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip?: Maybe; - - flowTarget: FlowTargetSourceDest; - - pagination: PaginationInputPaginated; - - sort: NetworkTopTablesSortField; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface NetworkTopNFlowSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip?: Maybe; - - flowTarget: FlowTargetSourceDest; - - pagination: PaginationInputPaginated; - - sort: NetworkTopTablesSortField; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface NetworkDnsSourceArgs { - filterQuery?: Maybe; - - id?: Maybe; - - isPtrIncluded: boolean; - - pagination: PaginationInputPaginated; - - sort: NetworkDnsSortField; - - stackByField?: Maybe; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface NetworkDnsHistogramSourceArgs { - filterQuery?: Maybe; - - defaultIndex: string[]; - - timerange: TimerangeInput; - - stackByField?: Maybe; - - docValueFields: DocValueFieldsInput[]; -} -export interface NetworkHttpSourceArgs { - id?: Maybe; - - filterQuery?: Maybe; - - ip?: Maybe; - - pagination: PaginationInputPaginated; - - sort: NetworkHttpSortField; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface OverviewNetworkSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface OverviewHostSourceArgs { - id?: Maybe; - - timerange: TimerangeInput; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface TlsSourceArgs { - filterQuery?: Maybe; - - id?: Maybe; - - ip: string; - - pagination: PaginationInputPaginated; - - sort: TlsSortField; - - flowTarget: FlowTargetSourceDest; - - timerange: TimerangeInput; - - defaultIndex: string[]; -} -export interface UncommonProcessesSourceArgs { - timerange: TimerangeInput; - - pagination: PaginationInputPaginated; - - filterQuery?: Maybe; - - defaultIndex: string[]; -} -export interface IndicesExistSourceStatusArgs { - defaultIndex: string[]; -} -export interface IndexFieldsSourceStatusArgs { - defaultIndex: string[]; -} -export interface PersistNoteMutationArgs { - noteId?: Maybe; - - version?: Maybe; - - note: NoteInput; -} -export interface DeleteNoteMutationArgs { - id: string[]; -} -export interface DeleteNoteByTimelineIdMutationArgs { - timelineId: string; - - version?: Maybe; -} -export interface PersistPinnedEventOnTimelineMutationArgs { - pinnedEventId?: Maybe; - - eventId: string; - - timelineId?: Maybe; -} -export interface DeletePinnedEventOnTimelineMutationArgs { - id: string[]; -} -export interface DeleteAllPinnedEventsOnTimelineMutationArgs { - timelineId: string; -} -export interface PersistTimelineMutationArgs { - id?: Maybe; - - version?: Maybe; - - timeline: TimelineInput; -} -export interface PersistFavoriteMutationArgs { - timelineId?: Maybe; -} -export interface DeleteTimelineMutationArgs { - id: string[]; -} - -// ==================================================== -// Documents -// ==================================================== - -export namespace GetLastEventTimeQuery { - export type Variables = { - sourceId: string; - indexKey: LastEventIndexKey; - details: LastTimeDetails; - defaultIndex: string[]; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - LastEventTime: LastEventTime; - }; - - export type LastEventTime = { - __typename?: 'LastEventTimeData'; - - lastSeen: Maybe; - }; -} - -export namespace GetMatrixHistogramQuery { - export type Variables = { - defaultIndex: string[]; - filterQuery?: Maybe; - histogramType: HistogramType; - inspect: boolean; - sourceId: string; - stackByField: string; - timerange: TimerangeInput; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - MatrixHistogram: MatrixHistogram; - }; - - export type MatrixHistogram = { - __typename?: 'MatrixHistogramOverTimeData'; - - matrixHistogramData: MatrixHistogramData[]; - - totalCount: number; - - inspect: Maybe; - }; - - export type MatrixHistogramData = { - __typename?: 'MatrixOverTimeHistogramData'; - - x: Maybe; - - y: Maybe; - - g: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace SourceQuery { - export type Variables = { - sourceId?: Maybe; - defaultIndex: string[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - status: Status; - }; - - export type Status = { - __typename?: 'SourceStatus'; - - indicesExist: boolean; - - indexFields: IndexFields[]; - }; - - export type IndexFields = { - __typename?: 'IndexField'; - - category: string; - - description: Maybe; - - example: Maybe; - - indexes: (Maybe)[]; - - name: string; - - searchable: boolean; - - type: string; - - aggregatable: boolean; - - format: Maybe; - - esTypes: Maybe; - - subType: Maybe; - }; -} - -export namespace GetAuthenticationsQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Authentications: Authentications; - }; - - export type Authentications = { - __typename?: 'AuthenticationsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'AuthenticationsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'AuthenticationItem'; - - _id: string; - - failures: number; - - successes: number; - - user: User; - - lastSuccess: Maybe; - - lastFailure: Maybe; - }; - - export type User = { - __typename?: 'UserEcsFields'; - - name: Maybe; - }; - - export type LastSuccess = { - __typename?: 'LastSourceHost'; - - timestamp: Maybe; - - source: Maybe<_Source>; - - host: Maybe; - }; - - export type _Source = { - __typename?: 'SourceEcsFields'; - - ip: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type LastFailure = { - __typename?: 'LastSourceHost'; - - timestamp: Maybe; - - source: Maybe<__Source>; - - host: Maybe<_Host>; - }; - - export type __Source = { - __typename?: 'SourceEcsFields'; - - ip: Maybe; - }; - - export type _Host = { - __typename?: 'HostEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetHostFirstLastSeenQuery { - export type Variables = { - sourceId: string; - hostName: string; - defaultIndex: string[]; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - HostFirstLastSeen: HostFirstLastSeen; - }; - - export type HostFirstLastSeen = { - __typename?: 'FirstLastSeenHost'; - - firstSeen: Maybe; - - lastSeen: Maybe; - }; -} - -export namespace GetHostsTableQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - sort: HostsSortField; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Hosts: Hosts; - }; - - export type Hosts = { - __typename?: 'HostsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'HostsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'HostItem'; - - _id: Maybe; - - lastSeen: Maybe; - - host: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - id: Maybe; - - name: Maybe; - - os: Maybe; - }; - - export type Os = { - __typename?: 'OsEcsFields'; - - name: Maybe; - - version: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetHostOverviewQuery { - export type Variables = { - sourceId: string; - hostName: string; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - HostOverview: HostOverview; - }; - - export type HostOverview = { - __typename?: 'HostItem'; - - _id: Maybe; - - host: Maybe; - - cloud: Maybe; - - inspect: Maybe; - - endpoint: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - architecture: Maybe; - - id: Maybe; - - ip: Maybe; - - mac: Maybe; - - name: Maybe; - - os: Maybe; - - type: Maybe; - }; - - export type Os = { - __typename?: 'OsEcsFields'; - - family: Maybe; - - name: Maybe; - - platform: Maybe; - - version: Maybe; - }; - - export type Cloud = { - __typename?: 'CloudFields'; - - instance: Maybe; - - machine: Maybe; - - provider: Maybe<(Maybe)[]>; - - region: Maybe<(Maybe)[]>; - }; - - export type Instance = { - __typename?: 'CloudInstance'; - - id: Maybe<(Maybe)[]>; - }; - - export type Machine = { - __typename?: 'CloudMachine'; - - type: Maybe<(Maybe)[]>; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; - - export type Endpoint = { - __typename?: 'EndpointFields'; - - endpointPolicy: Maybe; - - policyStatus: Maybe; - - sensorVersion: Maybe; - }; -} - -export namespace GetKpiHostDetailsQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - KpiHostDetails: KpiHostDetails; - }; - - export type KpiHostDetails = { - __typename?: 'KpiHostDetailsData'; - - authSuccess: Maybe; - - authSuccessHistogram: Maybe; - - authFailure: Maybe; - - authFailureHistogram: Maybe; - - uniqueSourceIps: Maybe; - - uniqueSourceIpsHistogram: Maybe; - - uniqueDestinationIps: Maybe; - - uniqueDestinationIpsHistogram: Maybe; - - inspect: Maybe; - }; - - export type AuthSuccessHistogram = KpiHostDetailsChartFields.Fragment; - - export type AuthFailureHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueSourceIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type UniqueDestinationIpsHistogram = KpiHostDetailsChartFields.Fragment; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetKpiHostsQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - KpiHosts: KpiHosts; - }; - - export type KpiHosts = { - __typename?: 'KpiHostsData'; - - hosts: Maybe; - - hostsHistogram: Maybe; - - authSuccess: Maybe; - - authSuccessHistogram: Maybe; - - authFailure: Maybe; - - authFailureHistogram: Maybe; - - uniqueSourceIps: Maybe; - - uniqueSourceIpsHistogram: Maybe; - - uniqueDestinationIps: Maybe; - - uniqueDestinationIpsHistogram: Maybe; - - inspect: Maybe; - }; - - export type HostsHistogram = KpiHostChartFields.Fragment; - - export type AuthSuccessHistogram = KpiHostChartFields.Fragment; - - export type AuthFailureHistogram = KpiHostChartFields.Fragment; - - export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment; - - export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUncommonProcessesQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - pagination: PaginationInputPaginated; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - UncommonProcesses: UncommonProcesses; - }; - - export type UncommonProcesses = { - __typename?: 'UncommonProcessesData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UncommonProcessesEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UncommonProcessItem'; - - _id: string; - - instances: number; - - process: Process; - - user: Maybe; - - hosts: Hosts[]; - }; - - export type Process = { - __typename?: 'ProcessEcsFields'; - - args: Maybe; - - name: Maybe; - }; - - export type User = { - __typename?: 'UserEcsFields'; - - id: Maybe; - - name: Maybe; - }; - - export type Hosts = { - __typename?: 'HostEcsFields'; - - name: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetIpOverviewQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - ip: string; - defaultIndex: string[]; - inspect: boolean; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - IpOverview: Maybe; - }; - - export type IpOverview = { - __typename?: 'IpOverviewData'; - - source: Maybe<_Source>; - - destination: Maybe; - - host: Host; - - inspect: Maybe; - }; - - export type _Source = { - __typename?: 'Overview'; - - firstSeen: Maybe; - - lastSeen: Maybe; - - autonomousSystem: AutonomousSystem; - - geo: Geo; - }; - - export type AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe; - }; - - export type Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Destination = { - __typename?: 'Overview'; - - firstSeen: Maybe; - - lastSeen: Maybe; - - autonomousSystem: _AutonomousSystem; - - geo: _Geo; - }; - - export type _AutonomousSystem = { - __typename?: 'AutonomousSystem'; - - number: Maybe; - - organization: Maybe<_Organization>; - }; - - export type _Organization = { - __typename?: 'AutonomousSystemOrganization'; - - name: Maybe; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - city_name: Maybe; - - country_iso_code: Maybe; - - country_name: Maybe; - - location: Maybe<_Location>; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type _Location = { - __typename?: 'Location'; - - lat: Maybe; - - lon: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - architecture: Maybe; - - id: Maybe; - - ip: Maybe; - - mac: Maybe; - - name: Maybe; - - os: Maybe; - - type: Maybe; - }; - - export type Os = { - __typename?: 'OsEcsFields'; - - family: Maybe; - - name: Maybe; - - platform: Maybe; - - version: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetKpiNetworkQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - KpiNetwork: Maybe; - }; - - export type KpiNetwork = { - __typename?: 'KpiNetworkData'; - - networkEvents: Maybe; - - uniqueFlowId: Maybe; - - uniqueSourcePrivateIps: Maybe; - - uniqueSourcePrivateIpsHistogram: Maybe; - - uniqueDestinationPrivateIps: Maybe; - - uniqueDestinationPrivateIpsHistogram: Maybe; - - dnsQueries: Maybe; - - tlsHandshakes: Maybe; - - inspect: Maybe; - }; - - export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment; - - export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetNetworkDnsQuery { - export type Variables = { - defaultIndex: string[]; - filterQuery?: Maybe; - inspect: boolean; - isPtrIncluded: boolean; - pagination: PaginationInputPaginated; - sort: NetworkDnsSortField; - sourceId: string; - stackByField?: Maybe; - timerange: TimerangeInput; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - NetworkDns: NetworkDns; - }; - - export type NetworkDns = { - __typename?: 'NetworkDnsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'NetworkDnsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'NetworkDnsItem'; - - _id: Maybe; - - dnsBytesIn: Maybe; - - dnsBytesOut: Maybe; - - dnsName: Maybe; - - queryCount: Maybe; - - uniqueDomains: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace 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; - ip?: Maybe; - filterQuery?: Maybe; - pagination: PaginationInputPaginated; - sort: NetworkTopTablesSortField; - flowTarget: FlowTargetSourceDest; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - NetworkTopCountries: NetworkTopCountries; - }; - - export type NetworkTopCountries = { - __typename?: 'NetworkTopCountriesData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'NetworkTopCountriesEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'NetworkTopCountriesItem'; - - source: Maybe<_Source>; - - destination: Maybe; - - network: Maybe; - }; - - export type _Source = { - __typename?: 'TopCountriesItemSource'; - - country: Maybe; - - destination_ips: Maybe; - - flows: Maybe; - - source_ips: Maybe; - }; - - export type Destination = { - __typename?: 'TopCountriesItemDestination'; - - country: Maybe; - - destination_ips: Maybe; - - flows: Maybe; - - source_ips: Maybe; - }; - - export type Network = { - __typename?: 'TopNetworkTablesEcsField'; - - bytes_in: Maybe; - - bytes_out: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetNetworkTopNFlowQuery { - export type Variables = { - sourceId: string; - ip?: Maybe; - filterQuery?: Maybe; - pagination: PaginationInputPaginated; - sort: NetworkTopTablesSortField; - flowTarget: FlowTargetSourceDest; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - NetworkTopNFlow: NetworkTopNFlow; - }; - - export type NetworkTopNFlow = { - __typename?: 'NetworkTopNFlowData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'NetworkTopNFlowEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'NetworkTopNFlowItem'; - - source: Maybe<_Source>; - - destination: Maybe; - - network: Maybe; - }; - - export type _Source = { - __typename?: 'TopNFlowItemSource'; - - autonomous_system: Maybe; - - domain: Maybe; - - ip: Maybe; - - location: Maybe; - - flows: Maybe; - - destination_ips: Maybe; - }; - - export type AutonomousSystem = { - __typename?: 'AutonomousSystemItem'; - - name: Maybe; - - number: Maybe; - }; - - export type Location = { - __typename?: 'GeoItem'; - - geo: Maybe; - - flowTarget: Maybe; - }; - - export type Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - country_name: Maybe; - - country_iso_code: Maybe; - - city_name: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Destination = { - __typename?: 'TopNFlowItemDestination'; - - autonomous_system: Maybe<_AutonomousSystem>; - - domain: Maybe; - - ip: Maybe; - - location: Maybe<_Location>; - - flows: Maybe; - - source_ips: Maybe; - }; - - export type _AutonomousSystem = { - __typename?: 'AutonomousSystemItem'; - - name: Maybe; - - number: Maybe; - }; - - export type _Location = { - __typename?: 'GeoItem'; - - geo: Maybe<_Geo>; - - flowTarget: Maybe; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - country_name: Maybe; - - country_iso_code: Maybe; - - city_name: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Network = { - __typename?: 'TopNetworkTablesEcsField'; - - bytes_in: Maybe; - - bytes_out: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetTlsQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTargetSourceDest; - ip: string; - pagination: PaginationInputPaginated; - sort: TlsSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Tls: Tls; - }; - - export type Tls = { - __typename?: 'TlsData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'TlsEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'TlsNode'; - - _id: Maybe; - - subjects: Maybe; - - ja3: Maybe; - - issuers: Maybe; - - notAfter: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetUsersQuery { - export type Variables = { - sourceId: string; - filterQuery?: Maybe; - flowTarget: FlowTarget; - ip: string; - pagination: PaginationInputPaginated; - sort: UsersSortField; - timerange: TimerangeInput; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Users: Users; - }; - - export type Users = { - __typename?: 'UsersData'; - - totalCount: number; - - edges: Edges[]; - - pageInfo: PageInfo; - - inspect: Maybe; - }; - - export type Edges = { - __typename?: 'UsersEdges'; - - node: Node; - - cursor: Cursor; - }; - - export type Node = { - __typename?: 'UsersNode'; - - user: Maybe; - }; - - export type User = { - __typename?: 'UsersItem'; - - name: Maybe; - - id: Maybe; - - groupId: Maybe; - - groupName: Maybe; - - count: Maybe; - }; - - export type Cursor = { - __typename?: 'CursorType'; - - value: Maybe; - }; - - export type PageInfo = { - __typename?: 'PageInfoPaginated'; - - activePage: number; - - fakeTotalCount: number; - - showMorePagesIndicator: boolean; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetOverviewHostQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - OverviewHost: Maybe; - }; - - export type OverviewHost = { - __typename?: 'OverviewHostData'; - - auditbeatAuditd: Maybe; - - auditbeatFIM: Maybe; - - auditbeatLogin: Maybe; - - auditbeatPackage: Maybe; - - auditbeatProcess: Maybe; - - auditbeatUser: Maybe; - - endgameDns: Maybe; - - endgameFile: Maybe; - - endgameImageLoad: Maybe; - - endgameNetwork: Maybe; - - endgameProcess: Maybe; - - endgameRegistry: Maybe; - - endgameSecurity: Maybe; - - filebeatSystemModule: Maybe; - - winlogbeatSecurity: Maybe; - - winlogbeatMWSysmonOperational: Maybe; - - inspect: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetOverviewNetworkQuery { - export type Variables = { - sourceId: string; - timerange: TimerangeInput; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - OverviewNetwork: Maybe; - }; - - export type OverviewNetwork = { - __typename?: 'OverviewNetworkData'; - - auditbeatSocket: Maybe; - - filebeatCisco: Maybe; - - filebeatNetflow: Maybe; - - filebeatPanw: Maybe; - - filebeatSuricata: Maybe; - - filebeatZeek: Maybe; - - packetbeatDNS: Maybe; - - packetbeatFlow: Maybe; - - packetbeatTLS: Maybe; - - inspect: Maybe; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; -} - -export namespace GetAllTimeline { - export type Variables = { - pageInfo: PageInfoTimeline; - search?: Maybe; - sort?: Maybe; - onlyUserFavorite?: Maybe; - timelineType?: Maybe; - status?: Maybe; - }; - - export type Query = { - __typename?: 'Query'; - - getAllTimeline: GetAllTimeline; - }; - - export type GetAllTimeline = { - __typename?: 'ResponseTimelines'; - - totalCount: Maybe; - - defaultTimelineCount: Maybe; - - templateTimelineCount: Maybe; - - elasticTemplateTimelineCount: Maybe; - - customTemplateTimelineCount: Maybe; - - favoriteCount: Maybe; - - timeline: (Maybe)[]; - }; - - export type Timeline = { - __typename?: 'TimelineResult'; - - savedObjectId: string; - - description: Maybe; - - favorite: Maybe; - - eventIdToNoteIds: Maybe; - - excludedRowRendererIds: Maybe; - - notes: Maybe; - - noteIds: Maybe; - - pinnedEventIds: Maybe; - - status: Maybe; - - title: Maybe; - - timelineType: Maybe; - - templateTimelineId: Maybe; - - templateTimelineVersion: Maybe; - - created: Maybe; - - createdBy: Maybe; - - updated: Maybe; - - updatedBy: Maybe; - - version: string; - }; - - export type Favorite = { - __typename?: 'FavoriteTimelineResult'; - - fullName: Maybe; - - userName: Maybe; - - favoriteDate: Maybe; - }; - - export type EventIdToNoteIds = { - __typename?: 'NoteResult'; - - eventId: Maybe; - - note: Maybe; - - timelineId: Maybe; - - noteId: string; - - created: Maybe; - - createdBy: Maybe; - - timelineVersion: Maybe; - - updated: Maybe; - - updatedBy: Maybe; - - version: Maybe; - }; - - export type Notes = { - __typename?: 'NoteResult'; - - eventId: Maybe; - - note: Maybe; - - timelineId: Maybe; - - timelineVersion: Maybe; - - noteId: string; - - created: Maybe; - - createdBy: Maybe; - - updated: Maybe; - - updatedBy: Maybe; - - version: Maybe; - }; -} - -export namespace DeleteTimelineMutation { - export type Variables = { - id: string[]; - }; - - export type Mutation = { - __typename?: 'Mutation'; - - deleteTimeline: boolean; - }; -} - -export namespace GetTimelineDetailsQuery { - export type Variables = { - sourceId: string; - eventId: string; - indexName: string; - defaultIndex: string[]; - docValueFields: DocValueFieldsInput[]; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - TimelineDetails: TimelineDetails; - }; - - export type TimelineDetails = { - __typename?: 'TimelineDetailsData'; - - data: Maybe; - }; - - export type Data = { - __typename?: 'DetailItem'; - - field: string; - - values: Maybe; - - originalValue: Maybe; - }; -} - -export namespace PersistTimelineFavoriteMutation { - export type Variables = { - timelineId?: Maybe; - }; - - export type Mutation = { - __typename?: 'Mutation'; - - persistFavorite: PersistFavorite; - }; - - export type PersistFavorite = { - __typename?: 'ResponseFavoriteTimeline'; - - savedObjectId: string; - - version: string; - - favorite: Maybe; - }; - - export type Favorite = { - __typename?: 'FavoriteTimelineResult'; - - fullName: Maybe; - - userName: Maybe; - - favoriteDate: Maybe; - }; -} - -export namespace GetTimelineQuery { - export type Variables = { - sourceId: string; - fieldRequested: string[]; - pagination: PaginationInput; - sortField: SortField; - filterQuery?: Maybe; - defaultIndex: string[]; - inspect: boolean; - docValueFields: DocValueFieldsInput[]; - timerange: TimerangeInput; - }; - - export type Query = { - __typename?: 'Query'; - - source: Source; - }; - - export type Source = { - __typename?: 'Source'; - - id: string; - - Timeline: Timeline; - }; - - export type Timeline = { - __typename?: 'TimelineData'; - - totalCount: number; - - inspect: Maybe; - - pageInfo: PageInfo; - - edges: Edges[]; - }; - - export type Inspect = { - __typename?: 'Inspect'; - - dsl: string[]; - - response: string[]; - }; - - export type PageInfo = { - __typename?: 'PageInfo'; - - endCursor: Maybe; - - hasNextPage: Maybe; - }; - - export type EndCursor = { - __typename?: 'CursorType'; - - value: Maybe; - - tiebreaker: Maybe; - }; - - export type Edges = { - __typename?: 'TimelineEdges'; - - node: Node; - }; - - export type Node = { - __typename?: 'TimelineItem'; - - _id: string; - - _index: Maybe; - - data: Data[]; - - ecs: Ecs; - }; - - export type Data = { - __typename?: 'TimelineNonEcsData'; - - field: string; - - value: Maybe; - }; - - export type Ecs = { - __typename?: 'ECS'; - - _id: string; - - _index: Maybe; - - timestamp: Maybe; - - message: Maybe; - - system: Maybe; - - event: Maybe; - - agent: Maybe; - - auditd: Maybe; - - file: Maybe; - - host: Maybe; - - rule: Maybe; - - source: Maybe<_Source>; - - destination: Maybe; - - dns: Maybe; - - endgame: Maybe; - - geo: Maybe<__Geo>; - - signal: Maybe; - - suricata: Maybe; - - network: Maybe; - - http: Maybe; - - tls: Maybe; - - url: Maybe; - - user: Maybe; - - winlog: Maybe; - - process: Maybe; - - zeek: Maybe; - }; - - export type System = { - __typename?: 'SystemEcsField'; - - auth: Maybe; - - audit: Maybe; - }; - - export type Auth = { - __typename?: 'AuthEcsFields'; - - ssh: Maybe; - }; - - export type Ssh = { - __typename?: 'SshEcsFields'; - - signature: Maybe; - - method: Maybe; - }; - - export type Audit = { - __typename?: 'AuditEcsFields'; - - package: Maybe; - }; - - export type Package = { - __typename?: 'PackageEcsFields'; - - arch: Maybe; - - entity_id: Maybe; - - name: Maybe; - - size: Maybe; - - summary: Maybe; - - version: Maybe; - }; - - export type Event = { - __typename?: 'EventEcsFields'; - - action: Maybe; - - category: Maybe; - - code: Maybe; - - created: Maybe; - - dataset: Maybe; - - duration: Maybe; - - end: Maybe; - - hash: Maybe; - - id: Maybe; - - kind: Maybe; - - module: Maybe; - - original: Maybe; - - outcome: Maybe; - - risk_score: Maybe; - - risk_score_norm: Maybe; - - severity: Maybe; - - start: Maybe; - - timezone: Maybe; - - type: Maybe; - }; - - export type Agent = { - __typename?: 'AgentEcsField'; - - type: Maybe; - }; - - export type Auditd = { - __typename?: 'AuditdEcsFields'; - - result: Maybe; - - session: Maybe; - - data: Maybe<_Data>; - - summary: Maybe; - }; - - export type _Data = { - __typename?: 'AuditdData'; - - acct: Maybe; - - terminal: Maybe; - - op: Maybe; - }; - - export type Summary = { - __typename?: 'Summary'; - - actor: Maybe; - - object: Maybe; - - how: Maybe; - - message_type: Maybe; - - sequence: Maybe; - }; - - export type Actor = { - __typename?: 'PrimarySecondary'; - - primary: Maybe; - - secondary: Maybe; - }; - - export type Object = { - __typename?: 'PrimarySecondary'; - - primary: Maybe; - - secondary: Maybe; - - type: Maybe; - }; - - export type File = { - __typename?: 'FileFields'; - - name: Maybe; - - path: Maybe; - - target_path: Maybe; - - extension: Maybe; - - type: Maybe; - - device: Maybe; - - inode: Maybe; - - uid: Maybe; - - owner: Maybe; - - gid: Maybe; - - group: Maybe; - - mode: Maybe; - - size: Maybe; - - mtime: Maybe; - - ctime: Maybe; - }; - - export type Host = { - __typename?: 'HostEcsFields'; - - id: Maybe; - - name: Maybe; - - ip: Maybe; - }; - - export type Rule = { - __typename?: 'RuleEcsField'; - - reference: Maybe; - }; - - export type _Source = { - __typename?: 'SourceEcsFields'; - - bytes: Maybe; - - ip: Maybe; - - packets: Maybe; - - port: Maybe; - - geo: Maybe; - }; - - export type Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - country_name: Maybe; - - country_iso_code: Maybe; - - city_name: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Destination = { - __typename?: 'DestinationEcsFields'; - - bytes: Maybe; - - ip: Maybe; - - packets: Maybe; - - port: Maybe; - - geo: Maybe<_Geo>; - }; - - export type _Geo = { - __typename?: 'GeoEcsFields'; - - continent_name: Maybe; - - country_name: Maybe; - - country_iso_code: Maybe; - - city_name: Maybe; - - region_iso_code: Maybe; - - region_name: Maybe; - }; - - export type Dns = { - __typename?: 'DnsEcsFields'; - - question: Maybe; - - resolved_ip: Maybe; - - response_code: Maybe; - }; - - export type Question = { - __typename?: 'DnsQuestionData'; - - name: Maybe; - - type: Maybe; - }; - - export type Endgame = { - __typename?: 'EndgameEcsFields'; - - exit_code: Maybe; - - file_name: Maybe; - - file_path: Maybe; - - logon_type: Maybe; - - parent_process_name: Maybe; - - pid: Maybe; - - process_name: Maybe; - - subject_domain_name: Maybe; - - subject_logon_id: Maybe; - - subject_user_name: Maybe; - - target_domain_name: Maybe; - - target_logon_id: Maybe; - - target_user_name: Maybe; - }; - - export type __Geo = { - __typename?: 'GeoEcsFields'; - - region_name: Maybe; - - country_iso_code: Maybe; - }; - - export type Signal = { - __typename?: 'SignalField'; - - status: Maybe; - - original_time: Maybe; - - rule: Maybe<_Rule>; - }; + export type Host = { + __typename?: 'HostEcsFields'; - export type _Rule = { - __typename?: 'RuleField'; + architecture: Maybe; id: Maybe; - saved_id: Maybe; - - timeline_id: Maybe; + ip: Maybe; - timeline_title: Maybe; + mac: Maybe; - output_index: Maybe; + name: Maybe; - from: Maybe; + os: Maybe; - index: Maybe; + type: Maybe; + }; - language: Maybe; + export type Os = { + __typename?: 'OsEcsFields'; - query: Maybe; + family: Maybe; - to: Maybe; + name: Maybe; - filters: Maybe; + platform: Maybe; - note: Maybe; + version: Maybe; + }; - type: Maybe; + export type Cloud = { + __typename?: 'CloudFields'; - threshold: Maybe; + instance: Maybe; - exceptions_list: Maybe; - }; + machine: Maybe; - export type Suricata = { - __typename?: 'SuricataEcsFields'; + provider: Maybe[]>; - eve: Maybe; + region: Maybe[]>; }; - export type Eve = { - __typename?: 'SuricataEveData'; + export type Instance = { + __typename?: 'CloudInstance'; - proto: Maybe; + id: Maybe[]>; + }; - flow_id: Maybe; + export type Machine = { + __typename?: 'CloudMachine'; - alert: Maybe; + type: Maybe[]>; }; - export type Alert = { - __typename?: 'SuricataAlertData'; + export type Inspect = { + __typename?: 'Inspect'; - signature: Maybe; + dsl: string[]; - signature_id: Maybe; + response: string[]; }; - export type Network = { - __typename?: 'NetworkEcsField'; + export type Endpoint = { + __typename?: 'EndpointFields'; - bytes: Maybe; + endpointPolicy: Maybe; - community_id: Maybe; + policyStatus: Maybe; - direction: Maybe; + sensorVersion: Maybe; + }; +} - packets: Maybe; +export namespace GetHostFirstLastSeenQuery { + export type Variables = { + sourceId: string; + hostName: string; + defaultIndex: string[]; + docValueFields: DocValueFieldsInput[]; + }; - protocol: Maybe; + export type Query = { + __typename?: 'Query'; - transport: Maybe; + source: Source; }; - export type Http = { - __typename?: 'HttpEcsFields'; - - version: Maybe; + export type Source = { + __typename?: 'Source'; - request: Maybe; + id: string; - response: Maybe; + HostFirstLastSeen: HostFirstLastSeen; }; - export type Request = { - __typename?: 'HttpRequestData'; - - method: Maybe; + export type HostFirstLastSeen = { + __typename?: 'FirstLastSeenHost'; - body: Maybe; + firstSeen: Maybe; - referrer: Maybe; + lastSeen: Maybe; }; +} - export type Body = { - __typename?: 'HttpBodyData'; +export namespace GetHostsTableQuery { + export type Variables = { + sourceId: string; + timerange: TimerangeInput; + pagination: PaginationInputPaginated; + sort: HostsSortField; + filterQuery?: Maybe; + defaultIndex: string[]; + inspect: boolean; + docValueFields: DocValueFieldsInput[]; + }; - bytes: Maybe; + export type Query = { + __typename?: 'Query'; - content: Maybe; + source: Source; }; - export type Response = { - __typename?: 'HttpResponseData'; + export type Source = { + __typename?: 'Source'; - status_code: Maybe; + id: string; - body: Maybe<_Body>; + Hosts: Hosts; }; - export type _Body = { - __typename?: 'HttpBodyData'; + export type Hosts = { + __typename?: 'HostsData'; + + totalCount: number; + + edges: Edges[]; - bytes: Maybe; + pageInfo: PageInfo; - content: Maybe; + inspect: Maybe; }; - export type Tls = { - __typename?: 'TlsEcsFields'; - - client_certificate: Maybe; + export type Edges = { + __typename?: 'HostsEdges'; - fingerprints: Maybe; + node: Node; - server_certificate: Maybe; + cursor: Cursor; }; - export type ClientCertificate = { - __typename?: 'TlsClientCertificateData'; + export type Node = { + __typename?: 'HostItem'; - fingerprint: Maybe; - }; + _id: Maybe; - export type Fingerprint = { - __typename?: 'FingerprintData'; + lastSeen: Maybe; - sha1: Maybe; + host: Maybe; }; - export type Fingerprints = { - __typename?: 'TlsFingerprintsData'; + export type Host = { + __typename?: 'HostEcsFields'; - ja3: Maybe; - }; + id: Maybe; - export type Ja3 = { - __typename?: 'TlsJa3Data'; + name: Maybe; - hash: Maybe; + os: Maybe; }; - export type ServerCertificate = { - __typename?: 'TlsServerCertificateData'; + export type Os = { + __typename?: 'OsEcsFields'; + + name: Maybe; - fingerprint: Maybe<_Fingerprint>; + version: Maybe; }; - export type _Fingerprint = { - __typename?: 'FingerprintData'; + export type Cursor = { + __typename?: 'CursorType'; - sha1: Maybe; + value: Maybe; }; - export type Url = { - __typename?: 'UrlEcsFields'; - - original: Maybe; + export type PageInfo = { + __typename?: 'PageInfoPaginated'; - domain: Maybe; + activePage: number; - username: Maybe; + fakeTotalCount: number; - password: Maybe; + showMorePagesIndicator: boolean; }; - export type User = { - __typename?: 'UserEcsFields'; + export type Inspect = { + __typename?: 'Inspect'; - domain: Maybe; + dsl: string[]; - name: Maybe; + response: string[]; }; +} - export type Winlog = { - __typename?: 'WinlogEcsFields'; - - event_id: Maybe; +export namespace GetAllTimeline { + export type Variables = { + pageInfo: PageInfoTimeline; + search?: Maybe; + sort?: Maybe; + onlyUserFavorite?: Maybe; + timelineType?: Maybe; + status?: Maybe; }; - export type Process = { - __typename?: 'ProcessEcsFields'; + export type Query = { + __typename?: 'Query'; - hash: Maybe; + getAllTimeline: GetAllTimeline; + }; - pid: Maybe; + export type GetAllTimeline = { + __typename?: 'ResponseTimelines'; - name: Maybe; + totalCount: Maybe; - ppid: Maybe; + defaultTimelineCount: Maybe; - args: Maybe; + templateTimelineCount: Maybe; - entity_id: Maybe; + elasticTemplateTimelineCount: Maybe; - executable: Maybe; + customTemplateTimelineCount: Maybe; - title: Maybe; + favoriteCount: Maybe; - working_directory: Maybe; + timeline: Maybe[]; }; - export type Hash = { - __typename?: 'ProcessHashData'; - - md5: Maybe; - - sha1: Maybe; - - sha256: Maybe; - }; + export type Timeline = { + __typename?: 'TimelineResult'; - export type Zeek = { - __typename?: 'ZeekEcsFields'; + savedObjectId: string; - session_id: Maybe; + description: Maybe; - connection: Maybe; + favorite: Maybe; - notice: Maybe; + eventIdToNoteIds: Maybe; - dns: Maybe<_Dns>; + excludedRowRendererIds: Maybe; - http: Maybe<_Http>; + notes: Maybe; - files: Maybe; + noteIds: Maybe; - ssl: Maybe; - }; + pinnedEventIds: Maybe; - export type Connection = { - __typename?: 'ZeekConnectionData'; + status: Maybe; - local_resp: Maybe; + title: Maybe; - local_orig: Maybe; + timelineType: Maybe; - missed_bytes: Maybe; + templateTimelineId: Maybe; - state: Maybe; + templateTimelineVersion: Maybe; - history: Maybe; - }; + created: Maybe; - export type Notice = { - __typename?: 'ZeekNoticeData'; + createdBy: Maybe; - suppress_for: Maybe; + updated: Maybe; - msg: Maybe; + updatedBy: Maybe; - note: Maybe; + version: string; + }; - sub: Maybe; + export type Favorite = { + __typename?: 'FavoriteTimelineResult'; - dst: Maybe; + fullName: Maybe; - dropped: Maybe; + userName: Maybe; - peer_descr: Maybe; + favoriteDate: Maybe; }; - export type _Dns = { - __typename?: 'ZeekDnsData'; - - AA: Maybe; + export type EventIdToNoteIds = { + __typename?: 'NoteResult'; - qclass_name: Maybe; + eventId: Maybe; - RD: Maybe; + note: Maybe; - qtype_name: Maybe; + timelineId: Maybe; - rejected: Maybe; + noteId: string; - qtype: Maybe; + created: Maybe; - query: Maybe; + createdBy: Maybe; - trans_id: Maybe; + timelineVersion: Maybe; - qclass: Maybe; + updated: Maybe; - RA: Maybe; + updatedBy: Maybe; - TC: Maybe; + version: Maybe; }; - export type _Http = { - __typename?: 'ZeekHttpData'; - - resp_mime_types: Maybe; - - trans_depth: Maybe; - - status_msg: Maybe; - - resp_fuids: Maybe; + export type Notes = { + __typename?: 'NoteResult'; - tags: Maybe; - }; + eventId: Maybe; - export type Files = { - __typename?: 'ZeekFileData'; + note: Maybe; - session_ids: Maybe; + timelineId: Maybe; - timedout: Maybe; + timelineVersion: Maybe; - local_orig: Maybe; + noteId: string; - tx_host: Maybe; + created: Maybe; - source: Maybe; + createdBy: Maybe; - is_orig: Maybe; + updated: Maybe; - overflow_bytes: Maybe; + updatedBy: Maybe; - sha1: Maybe; + version: Maybe; + }; +} - duration: Maybe; +export namespace DeleteTimelineMutation { + export type Variables = { + id: string[]; + }; - depth: Maybe; + export type Mutation = { + __typename?: 'Mutation'; - analyzers: Maybe; + deleteTimeline: boolean; + }; +} - mime_type: Maybe; +export namespace PersistTimelineFavoriteMutation { + export type Variables = { + timelineId?: Maybe; + }; - rx_host: Maybe; + export type Mutation = { + __typename?: 'Mutation'; - total_bytes: Maybe; + persistFavorite: PersistFavorite; + }; - fuid: Maybe; + export type PersistFavorite = { + __typename?: 'ResponseFavoriteTimeline'; - seen_bytes: Maybe; + savedObjectId: string; - missing_bytes: Maybe; + version: string; - md5: Maybe; + favorite: Maybe; }; - export type Ssl = { - __typename?: 'ZeekSslData'; - - cipher: Maybe; + export type Favorite = { + __typename?: 'FavoriteTimelineResult'; - established: Maybe; + fullName: Maybe; - resumed: Maybe; + userName: Maybe; - version: Maybe; + favoriteDate: Maybe; }; } @@ -5558,6 +2201,8 @@ export namespace GetOneTimeline { kqlQuery: Maybe; + indexNames: Maybe; + notes: Maybe; noteIds: Maybe; @@ -5890,6 +2535,8 @@ export namespace PersistTimelineMutation { kqlQuery: Maybe; + indexNames: Maybe; + title: Maybe; dateRange: Maybe; @@ -6125,33 +2772,3 @@ export namespace PersistTimelinePinnedEventMutation { version: Maybe; }; } - -export namespace KpiHostDetailsChartFields { - export type Fragment = { - __typename?: 'KpiHostHistogramData'; - - x: Maybe; - - y: Maybe; - }; -} - -export namespace KpiHostChartFields { - export type Fragment = { - __typename?: 'KpiHostHistogramData'; - - x: Maybe; - - y: Maybe; - }; -} - -export namespace KpiNetworkChartFields { - export type Fragment = { - __typename?: 'KpiNetworkHistogramData'; - - x: Maybe; - - y: Maybe; - }; -} diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/translations.ts index 0e918275e2b18..d437a6b73f71a 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/translations.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/translations.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; export const AUTHENTICATIONS = i18n.translate( - 'xpack.securitySolution.authenticationsTable.authenticationFailures', + 'xpack.securitySolution.authenticationsTable.authentications', { defaultMessage: 'Authentications', } diff --git a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx index 606b43c6508fb..4f2dff47af5a6 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.test.tsx @@ -6,8 +6,7 @@ import React from 'react'; -// we don't have the types for waitFor just yet, so using "as waitFor" until when we do -import { render, act, wait as waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import { useFirstLastSeenHost } from '../../containers/hosts/first_last_seen'; import { TestProviders } from '../../../common/mock'; @@ -26,21 +25,16 @@ describe('FirstLastSeen Component', () => { const firstSeen = 'Apr 8, 2019 @ 16:09:40.692'; const lastSeen = 'Apr 8, 2019 @ 18:35:45.064'; - // Suppress warnings about "react-apollo" until we migrate to apollo@3 - /* eslint-disable no-console */ - const originalError = console.error; - beforeAll(() => { - console.error = jest.fn(); - }); - afterAll(() => { - console.error = originalError; - }); - test('Loading', async () => { useFirstLastSeenHostMock.mockReturnValue([true, MOCKED_RESPONSE]); const { container } = render( - + ); expect(container.innerHTML).toBe( @@ -52,33 +46,39 @@ describe('FirstLastSeen Component', () => { useFirstLastSeenHostMock.mockReturnValue([false, MOCKED_RESPONSE]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${firstSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${firstSeen}
` + ); + }); }); test('Last Seen', async () => { useFirstLastSeenHostMock.mockReturnValue([false, MOCKED_RESPONSE]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${lastSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${lastSeen}
` + ); + }); }); test('First Seen is empty but not Last Seen', async () => { @@ -91,17 +91,20 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${lastSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${lastSeen}
` + ); + }); }); test('Last Seen is empty but not First Seen', async () => { @@ -114,17 +117,20 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.innerHTML).toBe( - `
${firstSeen}
` - ); - }) - ); + await waitFor(() => { + expect(container.innerHTML).toBe( + `
${firstSeen}
` + ); + }); }); test('First Seen With a bad date time string', async () => { @@ -137,14 +143,17 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.textContent).toBe('something-invalid'); - }) - ); + await waitFor(() => { + expect(container.textContent).toBe('something-invalid'); + }); }); test('Last Seen With a bad date time string', async () => { @@ -157,13 +166,16 @@ describe('FirstLastSeen Component', () => { ]); const { container } = render( - + ); - await act(() => - waitFor(() => { - expect(container.textContent).toBe('something-invalid'); - }) - ); + await waitFor(() => { + expect(container.textContent).toBe('something-invalid'); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx index a1b72fb39069c..ee415560cf9de 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/first_last_seen_host/index.tsx @@ -10,6 +10,7 @@ import React, { useMemo } from 'react'; import { useFirstLastSeenHost } from '../../containers/hosts/first_last_seen'; import { getEmptyTagValue } from '../../../common/components/empty_value'; import { FormattedRelativePreferenceDate } from '../../../common/components/formatted_date'; +import { DocValueFields } from '../../../../common/search_strategy'; export enum FirstLastSeenHostType { FIRST_SEEN = 'first-seen', @@ -17,47 +18,53 @@ export enum FirstLastSeenHostType { } interface FirstLastSeenHostProps { + docValueFields: DocValueFields[]; hostName: string; + indexNames: string[]; type: FirstLastSeenHostType; } -export const FirstLastSeenHost = React.memo(({ hostName, type }) => { - const [loading, { firstSeen, lastSeen, errorMessage }] = useFirstLastSeenHost({ - hostName, - }); - const valueSeen = useMemo( - () => (type === FirstLastSeenHostType.FIRST_SEEN ? firstSeen : lastSeen), - [firstSeen, lastSeen, type] - ); +export const FirstLastSeenHost = React.memo( + ({ docValueFields, hostName, type, indexNames }) => { + const [loading, { firstSeen, lastSeen, errorMessage }] = useFirstLastSeenHost({ + docValueFields, + hostName, + indexNames, + }); + const valueSeen = useMemo( + () => (type === FirstLastSeenHostType.FIRST_SEEN ? firstSeen : lastSeen), + [firstSeen, lastSeen, type] + ); + + if (errorMessage != null) { + return ( + + + + ); + } - if (errorMessage != null) { return ( - - - + <> + {loading && } + {!loading && valueSeen != null && new Date(valueSeen).toString() === 'Invalid Date' + ? valueSeen + : !loading && + valueSeen != null && ( + + + + )} + {!loading && valueSeen == null && getEmptyTagValue()} + ); } - - return ( - <> - {loading && } - {!loading && valueSeen != null && new Date(valueSeen).toString() === 'Invalid Date' - ? valueSeen - : !loading && - valueSeen != null && ( - - - - )} - {!loading && valueSeen == null && getEmptyTagValue()} - - ); -}); +); FirstLastSeenHost.displayName = 'FirstLastSeenHost'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap index 3143e680913b2..1d70f4f72ac8b 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/__snapshots__/index.test.tsx.snap @@ -68,97 +68,6 @@ exports[`Hosts Table rendering it renders the default Hosts table 1`] = ` } fakeTotalCount={50} id="hostsQuery" - indexPattern={ - Object { - "fields": Array [ - Object { - "aggregatable": true, - "name": "@timestamp", - "searchable": true, - "type": "date", - }, - Object { - "aggregatable": true, - "name": "@version", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.ephemeral_id", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.hostname", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.id", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test1", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test2", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test3", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test4", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test5", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test6", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test7", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "agent.test8", - "searchable": true, - "type": "string", - }, - Object { - "aggregatable": true, - "name": "host.name", - "searchable": true, - "type": "string", - }, - ], - "title": "filebeat-*,auditbeat-*,packetbeat-*", - } - } isInspect={false} loadPage={[MockFunction]} loading={false} diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index c4a391687843c..29e4dc48ae3c7 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -12,7 +12,6 @@ import { MockedProvider } from 'react-apollo/test-utils'; import '../../../common/mock/match_media'; import { apolloClientObservable, - mockIndexPattern, mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, @@ -69,7 +68,6 @@ describe('Hosts Table', () => { data={mockData.Hosts.edges} id="hostsQuery" isInspect={false} - indexPattern={mockIndexPattern} fakeTotalCount={getOr(50, 'fakeTotalCount', mockData.Hosts.pageInfo)} loading={false} loadPage={loadPage} @@ -92,7 +90,6 @@ describe('Hosts Table', () => { void; @@ -77,7 +75,6 @@ const HostsTableComponent = React.memo( direction, fakeTotalCount, id, - indexPattern, isInspect, limit, loading, diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 2b2a35945bdf1..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,155 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`kpiHostsComponent render it should render KpiHostDetailsData 1`] = ` - - - - -`; - -exports[`kpiHostsComponent render it should render KpiHostsData 1`] = ` - - - - - -`; - -exports[`kpiHostsComponent render it should render spinner if it is loading 1`] = ` - - - - - -`; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx new file mode 100644 index 0000000000000..84003e5dea5e9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/index.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { StatItems } from '../../../../common/components/stat_items'; +import { useHostsKpiAuthentications } from '../../../containers/kpi_hosts/authentications'; +import { HostsKpiBaseComponentManage } from '../common'; +import { HostsKpiProps, HostsKpiChartColors } from '../types'; +import * as i18n from './translations'; + +export const fieldsMapping: Readonly = [ + { + key: 'authentication', + fields: [ + { + key: 'authenticationsSuccess', + name: i18n.SUCCESS_CHART_LABEL, + description: i18n.SUCCESS_UNIT_LABEL, + value: null, + color: HostsKpiChartColors.authenticationsSuccess, + icon: 'check', + }, + { + key: 'authenticationsFailure', + name: i18n.FAIL_CHART_LABEL, + description: i18n.FAIL_UNIT_LABEL, + value: null, + color: HostsKpiChartColors.authenticationsFailure, + icon: 'cross', + }, + ], + enableAreaChart: true, + enableBarChart: true, + description: i18n.USER_AUTHENTICATIONS, + }, +]; + +const HostsKpiAuthenticationsComponent: React.FC = ({ + filterQuery, + from, + indexNames, + to, + narrowDateRange, + setQuery, + skip, +}) => { + const [loading, { refetch, id, inspect, ...data }] = useHostsKpiAuthentications({ + filterQuery, + endDate: to, + indexNames, + startDate: from, + skip, + }); + + return ( + + ); +}; + +export const HostsKpiAuthentications = React.memo(HostsKpiAuthenticationsComponent); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/translations.ts new file mode 100644 index 0000000000000..5175781159c91 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/authentications/translations.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const USER_AUTHENTICATIONS = i18n.translate( + 'xpack.securitySolution.kpiHosts.userAuthentications.title', + { + defaultMessage: 'User authentications', + } +); + +export const SUCCESS_UNIT_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.userAuthentications.successUnitLabel', + { + defaultMessage: 'success', + } +); + +export const FAIL_UNIT_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.userAuthentications.failUnitLabel', + { + defaultMessage: 'fail', + } +); + +export const SUCCESS_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.userAuthentications.successChartLabel', + { + defaultMessage: 'Succ.', + } +); + +export const FAIL_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.userAuthentications.failChartLabel', + { + defaultMessage: 'Fail', + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/common/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/common/index.tsx new file mode 100644 index 0000000000000..7c51a503092af --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/common/index.tsx @@ -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. + */ + +import React from 'react'; + +import { EuiFlexItem, EuiLoadingSpinner, EuiFlexGroup } from '@elastic/eui'; +import styled from 'styled-components'; + +import { manageQuery } from '../../../../common/components/page/manage_query'; +import { HostsKpiStrategyResponse } from '../../../../../common/search_strategy'; +import { + StatItemsComponent, + StatItemsProps, + useKpiMatrixStatus, + StatItems, +} from '../../../../common/components/stat_items'; +import { UpdateDateRange } from '../../../../common/components/charts/common'; + +const kpiWidgetHeight = 247; + +export const FlexGroup = styled(EuiFlexGroup)` + min-height: ${kpiWidgetHeight}px; +`; + +FlexGroup.displayName = 'FlexGroup'; + +export const HostsKpiBaseComponent = React.memo<{ + fieldsMapping: Readonly; + data: HostsKpiStrategyResponse; + loading?: boolean; + id: string; + from: string; + to: string; + narrowDateRange: UpdateDateRange; +}>(({ fieldsMapping, data, id, loading = false, from, to, narrowDateRange }) => { + const statItemsProps: StatItemsProps[] = useKpiMatrixStatus( + fieldsMapping, + data, + id, + from, + to, + narrowDateRange + ); + + if (loading) { + return ( + + + + + + ); + } + + return ( + + {statItemsProps.map((mappedStatItemProps) => ( + + ))} + + ); +}); + +HostsKpiBaseComponent.displayName = 'HostsKpiBaseComponent'; + +export const HostsKpiBaseComponentManage = manageQuery(HostsKpiBaseComponent); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.tsx new file mode 100644 index 0000000000000..908ff717e2711 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/index.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 from 'react'; + +import { StatItems } from '../../../../common/components/stat_items'; +import { useHostsKpiHosts } from '../../../containers/kpi_hosts/hosts'; +import { HostsKpiBaseComponentManage } from '../common'; +import { HostsKpiProps, HostsKpiChartColors } from '../types'; +import * as i18n from './translations'; + +export const fieldsMapping: Readonly = [ + { + key: 'hosts', + fields: [ + { + key: 'hosts', + value: null, + color: HostsKpiChartColors.hosts, + icon: 'storage', + }, + ], + enableAreaChart: true, + description: i18n.HOSTS, + }, +]; + +const HostsKpiHostsComponent: React.FC = ({ + filterQuery, + from, + indexNames, + to, + narrowDateRange, + setQuery, + skip, +}) => { + const [loading, { refetch, id, inspect, ...data }] = useHostsKpiHosts({ + filterQuery, + endDate: to, + indexNames, + startDate: from, + skip, + }); + + return ( + + ); +}; + +export const HostsKpiHosts = React.memo(HostsKpiHostsComponent); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/translations.ts new file mode 100644 index 0000000000000..7754591ab415b --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/hosts/translations.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 { i18n } from '@kbn/i18n'; + +export const HOSTS = i18n.translate('xpack.securitySolution.kpiHosts.hosts.title', { + defaultMessage: 'Hosts', +}); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.test.tsx deleted file mode 100644 index 7731881df6d2c..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.test.tsx +++ /dev/null @@ -1,107 +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 { mockKpiHostsData, mockKpiHostDetailsData } from './mock'; -import React from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import '../../../common/mock/match_media'; -import { KpiHostsComponentBase } from '.'; -import * as statItems from '../../../common/components/stat_items'; -import { kpiHostsMapping } from './kpi_hosts_mapping'; -import { kpiHostDetailsMapping } from './kpi_host_details_mapping'; - -describe('kpiHostsComponent', () => { - const ID = 'kpiHost'; - const from = '2019-06-15T06:00:00.000Z'; - const to = '2019-06-18T06:00:00.000Z'; - const narrowDateRange = () => {}; - describe('render', () => { - test('it should render spinner if it is loading', () => { - const wrapper: ShallowWrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); - }); - - test('it should render KpiHostsData', () => { - const wrapper: ShallowWrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); - }); - - test('it should render KpiHostDetailsData', () => { - const wrapper: ShallowWrapper = shallow( - - ); - expect(wrapper).toMatchSnapshot(); - }); - }); - - const table = [ - [mockKpiHostsData, kpiHostsMapping] as [typeof mockKpiHostsData, typeof kpiHostsMapping], - [mockKpiHostDetailsData, kpiHostDetailsMapping] as [ - typeof mockKpiHostDetailsData, - typeof kpiHostDetailsMapping - ], - ]; - - describe.each(table)( - 'it should handle KpiHostsProps and KpiHostDetailsProps', - (data, mapping) => { - let mockUseKpiMatrixStatus: jest.SpyInstance; - beforeAll(() => { - mockUseKpiMatrixStatus = jest.spyOn(statItems, 'useKpiMatrixStatus'); - }); - - beforeEach(() => { - shallow( - - ); - }); - - afterEach(() => { - mockUseKpiMatrixStatus.mockClear(); - }); - - afterAll(() => { - mockUseKpiMatrixStatus.mockRestore(); - }); - - test(`it should apply correct mapping by given data type`, () => { - expect(mockUseKpiMatrixStatus).toBeCalledWith(mapping, data, ID, from, to, narrowDateRange); - }); - } - ); -}); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx index c39e86591013f..6174e174db5a6 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/index.tsx @@ -4,81 +4,83 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import React from 'react'; -import styled from 'styled-components'; +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import { KpiHostsData, KpiHostDetailsData } from '../../../graphql/types'; -import { - StatItemsComponent, - StatItemsProps, - useKpiMatrixStatus, -} from '../../../common/components/stat_items'; -import { kpiHostsMapping } from './kpi_hosts_mapping'; -import { kpiHostDetailsMapping } from './kpi_host_details_mapping'; -import { UpdateDateRange } from '../../../common/components/charts/common'; +import { HostsKpiAuthentications } from './authentications'; +import { HostsKpiHosts } from './hosts'; +import { HostsKpiUniqueIps } from './unique_ips'; +import { HostsKpiProps } from './types'; -const kpiWidgetHeight = 247; - -interface GenericKpiHostProps { - from: string; - id: string; - loading: boolean; - to: string; - narrowDateRange: UpdateDateRange; -} - -interface KpiHostsProps extends GenericKpiHostProps { - data: KpiHostsData; -} - -interface KpiHostDetailsProps extends GenericKpiHostProps { - data: KpiHostDetailsData; -} - -const FlexGroupSpinner = styled(EuiFlexGroup)` - { - min-height: ${kpiWidgetHeight}px; - } -`; - -FlexGroupSpinner.displayName = 'FlexGroupSpinner'; - -export const KpiHostsComponentBase = ({ - data, - from, - loading, - id, - to, - narrowDateRange, -}: KpiHostsProps | KpiHostDetailsProps) => { - const mappings = - (data as KpiHostsData).hosts !== undefined ? kpiHostsMapping : kpiHostDetailsMapping; - const statItemsProps: StatItemsProps[] = useKpiMatrixStatus( - mappings, - data, - id, - from, - to, - narrowDateRange - ); - return loading ? ( - - - +export const HostsKpiComponent = React.memo( + ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => ( + + + + + + + + + - - ) : ( - - {statItemsProps.map((mappedStatItemProps, idx) => { - return ; - })} - ); -}; + ) +); -KpiHostsComponentBase.displayName = 'KpiHostsComponentBase'; +HostsKpiComponent.displayName = 'HostsKpiComponent'; -export const KpiHostsComponent = React.memo(KpiHostsComponentBase); +export const HostsDetailsKpiComponent = React.memo( + ({ filterQuery, from, indexNames, to, setQuery, skip, narrowDateRange }) => ( + + + + + + + + + ) +); -KpiHostsComponent.displayName = 'KpiHostsComponent'; +HostsDetailsKpiComponent.displayName = 'HostsDetailsKpiComponent'; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_host_details_mapping.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_host_details_mapping.ts deleted file mode 100644 index b3e98b70c4cb0..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_host_details_mapping.ts +++ /dev/null @@ -1,64 +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 * as i18n from './translations'; -import { StatItems } from '../../../common/components/stat_items'; -import { KpiHostsChartColors } from './types'; - -export const kpiHostDetailsMapping: Readonly = [ - { - key: 'authentication', - index: 0, - fields: [ - { - key: 'authSuccess', - name: i18n.SUCCESS_CHART_LABEL, - description: i18n.SUCCESS_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.authSuccess, - icon: 'check', - }, - { - key: 'authFailure', - name: i18n.FAIL_CHART_LABEL, - description: i18n.FAIL_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.authFailure, - icon: 'cross', - }, - ], - enableAreaChart: true, - enableBarChart: true, - grow: 1, - description: i18n.USER_AUTHENTICATIONS, - }, - { - key: 'uniqueIps', - index: 1, - fields: [ - { - key: 'uniqueSourceIps', - name: i18n.SOURCE_CHART_LABEL, - description: i18n.SOURCE_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.uniqueSourceIps, - icon: 'visMapCoordinate', - }, - { - key: 'uniqueDestinationIps', - name: i18n.DESTINATION_CHART_LABEL, - description: i18n.DESTINATION_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.uniqueDestinationIps, - icon: 'visMapCoordinate', - }, - ], - enableAreaChart: true, - enableBarChart: true, - grow: 1, - description: i18n.UNIQUE_IPS, - }, -]; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_hosts_mapping.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_hosts_mapping.ts deleted file mode 100644 index 78a9fd5b84d1f..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/kpi_hosts_mapping.ts +++ /dev/null @@ -1,79 +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 * as i18n from './translations'; -import { KpiHostsChartColors } from './types'; -import { StatItems } from '../../../common/components/stat_items'; - -export const kpiHostsMapping: Readonly = [ - { - key: 'hosts', - index: 0, - fields: [ - { - key: 'hosts', - value: null, - color: KpiHostsChartColors.hosts, - icon: 'storage', - }, - ], - enableAreaChart: true, - grow: 2, - description: i18n.HOSTS, - }, - { - key: 'authentication', - index: 1, - fields: [ - { - key: 'authSuccess', - name: i18n.SUCCESS_CHART_LABEL, - description: i18n.SUCCESS_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.authSuccess, - icon: 'check', - }, - { - key: 'authFailure', - name: i18n.FAIL_CHART_LABEL, - description: i18n.FAIL_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.authFailure, - icon: 'cross', - }, - ], - enableAreaChart: true, - enableBarChart: true, - grow: 4, - description: i18n.USER_AUTHENTICATIONS, - }, - { - key: 'uniqueIps', - index: 2, - fields: [ - { - key: 'uniqueSourceIps', - name: i18n.SOURCE_CHART_LABEL, - description: i18n.SOURCE_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.uniqueSourceIps, - icon: 'visMapCoordinate', - }, - { - key: 'uniqueDestinationIps', - name: i18n.DESTINATION_CHART_LABEL, - description: i18n.DESTINATION_UNIT_LABEL, - value: null, - color: KpiHostsChartColors.uniqueDestinationIps, - icon: 'visMapCoordinate', - }, - ], - enableAreaChart: true, - enableBarChart: true, - grow: 4, - description: i18n.UNIQUE_IPS, - }, -]; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/mock.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/mock.tsx deleted file mode 100644 index a1d081af20435..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/mock.tsx +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const mockKpiHostsData = { - hosts: 986, - hostsHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 919, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 82, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 4, - }, - ], - authSuccess: 61, - authSuccessHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 8, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 52, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 1, - }, - ], - authFailure: 15722, - authFailureHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 11731, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 3979, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 12, - }, - ], - uniqueSourceIps: 1407, - uniqueSourceIpsHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 1182, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 364, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 63, - }, - ], - uniqueDestinationIps: 1954, - uniqueDestinationIpsHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 1809, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 407, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 64, - }, - ], -}; -export const mockKpiHostDetailsData = { - authSuccess: 61, - authSuccessHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 8, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 52, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 1, - }, - ], - authFailure: 15722, - authFailureHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 11731, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 3979, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 12, - }, - ], - uniqueSourceIps: 1407, - uniqueSourceIpsHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 1182, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 364, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 63, - }, - ], - uniqueDestinationIps: 1954, - uniqueDestinationIpsHistogram: [ - { - x: new Date('2019-05-03T13:00:00.000Z').valueOf(), - y: 1809, - }, - { - x: new Date('2019-05-04T01:00:00.000Z').valueOf(), - y: 407, - }, - { - x: new Date('2019-05-04T13:00:00.000Z').valueOf(), - y: 64, - }, - ], -}; diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/translations.ts deleted file mode 100644 index 82543e6f106fa..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/translations.ts +++ /dev/null @@ -1,77 +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 { i18n } from '@kbn/i18n'; - -export const HOSTS = i18n.translate('xpack.securitySolution.kpiHosts.hosts.title', { - defaultMessage: 'Hosts', -}); - -export const USER_AUTHENTICATIONS = i18n.translate( - 'xpack.securitySolution.kpiHosts.userAuthentications.title', - { - defaultMessage: 'User authentications', - } -); - -export const SUCCESS_UNIT_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.userAuthentications.successUnitLabel', - { - defaultMessage: 'success', - } -); - -export const FAIL_UNIT_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.userAuthentications.failUnitLabel', - { - defaultMessage: 'fail', - } -); - -export const SUCCESS_CHART_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.userAuthentications.successChartLabel', - { - defaultMessage: 'Succ.', - } -); - -export const FAIL_CHART_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.userAuthentications.failChartLabel', - { - defaultMessage: 'Fail', - } -); - -export const UNIQUE_IPS = i18n.translate('xpack.securitySolution.kpiHosts.uniqueIps.title', { - defaultMessage: 'Unique IPs', -}); - -export const SOURCE_UNIT_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.uniqueIps.sourceUnitLabel', - { - defaultMessage: 'source', - } -); - -export const DESTINATION_UNIT_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.uniqueIps.destinationUnitLabel', - { - defaultMessage: 'destination', - } -); - -export const SOURCE_CHART_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.uniqueIps.sourceChartLabel', - { - defaultMessage: 'Src.', - } -); - -export const DESTINATION_CHART_LABEL = i18n.translate( - 'xpack.securitySolution.kpiHosts.uniqueIps.destinationChartLabel', - { - defaultMessage: 'Dest.', - } -); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts index fd48368124795..159d9dbcd005a 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts @@ -4,9 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -export enum KpiHostsChartColors { - authSuccess = '#54B399', - authFailure = '#E7664C', +import { UpdateDateRange } from '../../../common/components/charts/common'; +import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; + +export interface HostsKpiProps { + filterQuery: string; + from: string; + to: string; + indexNames: string[]; + narrowDateRange: UpdateDateRange; + setQuery: GlobalTimeArgs['setQuery']; + skip: boolean; +} + +export enum HostsKpiChartColors { + authenticationsSuccess = '#54B399', + authenticationsFailure = '#E7664C', uniqueSourceIps = '#D36086', uniqueDestinationIps = '#9170B8', hosts = '#6092C0', diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/index.tsx new file mode 100644 index 0000000000000..fc821ea8bbb55 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/index.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { StatItems } from '../../../../common/components/stat_items'; +import { useHostsKpiUniqueIps } from '../../../containers/kpi_hosts/unique_ips'; +import { HostsKpiBaseComponentManage } from '../common'; +import { HostsKpiProps, HostsKpiChartColors } from '../types'; +import * as i18n from './translations'; + +export const fieldsMapping: Readonly = [ + { + key: 'uniqueIps', + fields: [ + { + key: 'uniqueSourceIps', + name: i18n.SOURCE_CHART_LABEL, + description: i18n.SOURCE_UNIT_LABEL, + value: null, + color: HostsKpiChartColors.uniqueSourceIps, + icon: 'visMapCoordinate', + }, + { + key: 'uniqueDestinationIps', + name: i18n.DESTINATION_CHART_LABEL, + description: i18n.DESTINATION_UNIT_LABEL, + value: null, + color: HostsKpiChartColors.uniqueDestinationIps, + icon: 'visMapCoordinate', + }, + ], + enableAreaChart: true, + enableBarChart: true, + description: i18n.UNIQUE_IPS, + }, +]; + +const HostsKpiUniqueIpsComponent: React.FC = ({ + filterQuery, + from, + indexNames, + to, + narrowDateRange, + setQuery, + skip, +}) => { + const [loading, { refetch, id, inspect, ...data }] = useHostsKpiUniqueIps({ + filterQuery, + endDate: to, + indexNames, + startDate: from, + skip, + }); + + return ( + + ); +}; + +export const HostsKpiUniqueIps = React.memo(HostsKpiUniqueIpsComponent); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/translations.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/translations.ts new file mode 100644 index 0000000000000..6cc651880be7b --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/unique_ips/translations.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const UNIQUE_IPS = i18n.translate('xpack.securitySolution.kpiHosts.uniqueIps.title', { + defaultMessage: 'Unique IPs', +}); + +export const SOURCE_UNIT_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.uniqueIps.sourceUnitLabel', + { + defaultMessage: 'source', + } +); + +export const DESTINATION_UNIT_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.uniqueIps.destinationUnitLabel', + { + defaultMessage: 'destination', + } +); + +export const SOURCE_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.uniqueIps.sourceChartLabel', + { + defaultMessage: 'Src.', + } +); + +export const DESTINATION_CHART_LABEL = i18n.translate( + 'xpack.securitySolution.kpiHosts.uniqueIps.destinationChartLabel', + { + defaultMessage: 'Dest.', + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx index 5ace3439a2de6..41f443f14cafe 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx @@ -30,18 +30,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = shallow( @@ -54,18 +50,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -79,18 +71,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -105,18 +93,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -131,18 +115,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -157,18 +137,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -183,18 +159,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -208,18 +180,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( @@ -233,18 +201,14 @@ describe('Uncommon Process Table Component', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx index 31d7fb10edb1c..c7025bb489ae4 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.tsx @@ -9,7 +9,10 @@ import React, { useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { UncommonProcessesEdges, UncommonProcessItem } from '../../../graphql/types'; +import { + HostsUncommonProcessesEdges, + HostsUncommonProcessItem, +} from '../../../../common/search_strategy'; import { State } from '../../../common/store'; import { hostsActions, hostsModel, hostsSelectors } from '../../store'; import { defaultToEmptyTag, getEmptyValue } from '../../../common/components/empty_value'; @@ -21,7 +24,7 @@ import { getRowItemDraggables } from '../../../common/components/tables/helpers' import { HostsType } from '../../store/model'; const tableType = hostsModel.HostsTableType.uncommonProcesses; interface OwnProps { - data: UncommonProcessesEdges[]; + data: HostsUncommonProcessesEdges[]; fakeTotalCount: number; id: string; isInspect: boolean; @@ -33,12 +36,12 @@ interface OwnProps { } export type UncommonProcessTableColumns = [ - Columns, - Columns, - Columns, - Columns, - Columns, - Columns + Columns, + Columns, + Columns, + Columns, + Columns, + Columns ]; type UncommonProcessTableProps = OwnProps & PropsFromRedux; @@ -212,7 +215,7 @@ const getUncommonColumns = (): UncommonProcessTableColumns => [ }, ]; -export const getHostNames = (node: UncommonProcessItem): string[] => { +export const getHostNames = (node: HostsUncommonProcessItem): string[] => { if (node.hosts != null) { return node.hosts .filter((host) => host.name != null && host.name[0] != null) diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts index 52b835278634b..56853c1bfaae1 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/mock.ts @@ -4,116 +4,115 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UncommonProcessesData } from '../../../graphql/types'; +import { HostsUncommonProcessesStrategyResponse } from '../../../../common/search_strategy'; -export const mockData: { UncommonProcess: UncommonProcessesData } = { - UncommonProcess: { - totalCount: 5, - edges: [ - { - node: { - _id: 'cPsuhGcB0WOhS6qyTKC0', - process: { - title: ['Hello World'], - name: ['elrond.elstc.co'], - }, - hosts: [], - instances: 93, - user: { - id: ['0'], - name: ['root'], - }, +export const mockData: HostsUncommonProcessesStrategyResponse = { + totalCount: 5, + edges: [ + { + node: { + _id: 'cPsuhGcB0WOhS6qyTKC0', + process: { + title: ['Hello World'], + name: ['elrond.elstc.co'], }, - cursor: { - value: '98966fa2013c396155c460d35c0902be', + hosts: [], + instances: 93, + user: { + id: ['0'], + name: ['root'], }, }, - { - node: { - _id: 'cPsuhGcB0WOhS6qyTKC0', - process: { - title: ['Hello World'], - name: ['elrond.elstc.co'], - }, - hosts: [{ id: ['host-id-1'], name: ['hello-world'] }], - instances: 93, - user: { - id: ['0'], - name: ['root'], - }, + cursor: { + value: '98966fa2013c396155c460d35c0902be', + }, + }, + { + node: { + _id: 'cPsuhGcB0WOhS6qyTKC0', + process: { + title: ['Hello World'], + name: ['elrond.elstc.co'], }, - cursor: { - value: '98966fa2013c396155c460d35c0902be', + hosts: [{ id: ['host-id-1'], name: ['hello-world'] }], + instances: 93, + user: { + id: ['0'], + name: ['root'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [ - { id: ['host-id-1'], name: ['hello-world'] }, - { id: ['host-id-2'], name: ['hello-world-2'] }, - ], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: '98966fa2013c396155c460d35c0902be', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [ + { id: ['host-id-1'], name: ['hello-world'] }, + { id: ['host-id-2'], name: ['hello-world-2'] }, + ], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [{ ip: ['127.0.0.1'] }], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [{ ip: ['127.0.0.1'] }], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - { - node: { - _id: 'KwQDiWcB0WOhS6qyXmrW', - process: { - title: ['Hello World'], - name: ['siem-kibana'], - }, - hosts: [ - { ip: ['127.0.0.1'] }, - { id: ['host-id-1'], name: ['hello-world'] }, - { ip: ['127.0.0.1'] }, - { id: ['host-id-2'], name: ['hello-world-2'] }, - { ip: ['127.0.0.1'] }, - ], - instances: 97, - user: { - id: ['1'], - name: ['Evan'], - }, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, + }, + { + node: { + _id: 'KwQDiWcB0WOhS6qyXmrW', + process: { + title: ['Hello World'], + name: ['siem-kibana'], }, - cursor: { - value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + hosts: [ + { ip: ['127.0.0.1'] }, + { id: ['host-id-1'], name: ['hello-world'] }, + { ip: ['127.0.0.1'] }, + { id: ['host-id-2'], name: ['hello-world-2'] }, + { ip: ['127.0.0.1'] }, + ], + instances: 97, + user: { + id: ['1'], + name: ['Evan'], }, }, - ], - pageInfo: { - activePage: 1, - fakeTotalCount: 50, - showMorePagesIndicator: true, + cursor: { + value: 'aa7ca589f1b8220002f2fc61c64cfbf1', + }, }, + ], + pageInfo: { + activePage: 1, + fakeTotalCount: 50, + showMorePagesIndicator: true, }, + rawResponse: {} as HostsUncommonProcessesStrategyResponse['rawResponse'], }; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.gql_query.ts b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.gql_query.ts deleted file mode 100644 index c68816b34c175..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.gql_query.ts +++ /dev/null @@ -1,74 +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 gql from 'graphql-tag'; - -export const authenticationsQuery = gql` - query GetAuthenticationsQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $pagination: PaginationInputPaginated! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - $docValueFields: [docValueFieldsInput!]! - ) { - source(id: $sourceId) { - id - Authentications( - timerange: $timerange - pagination: $pagination - filterQuery: $filterQuery - defaultIndex: $defaultIndex - docValueFields: $docValueFields - ) { - totalCount - edges { - node { - _id - failures - successes - user { - name - } - lastSuccess { - timestamp - source { - ip - } - host { - id - name - } - } - lastFailure { - timestamp - source { - ip - } - host { - id - name - } - } - } - cursor { - value - } - } - pageInfo { - activePage - fakeTotalCount - showMorePagesIndicator - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx index 34f2385051f4c..bc7137097c646 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/authentications/index.tsx @@ -6,12 +6,14 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { shallowEqual, useSelector } from 'react-redux'; import deepEqual from 'fast-deep-equal'; -import { AbortError } from '../../../../../../../src/plugins/data/common'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../src/plugins/data/common'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { HostsQueries } from '../../../../common/search_strategy/security_solution'; import { HostAuthenticationsRequestOptions, @@ -23,7 +25,8 @@ import { } from '../../../../common/search_strategy'; import { ESTermQuery } from '../../../../common/typed_json'; -import { inputsModel, State } from '../../../common/store'; +import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; +import { inputsModel } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; import { useKibana } from '../../../common/lib/kibana'; @@ -52,6 +55,7 @@ interface UseAuthentications { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; startDate: string; type: hostsModel.HostsType; skip: boolean; @@ -61,24 +65,23 @@ export const useAuthentications = ({ docValueFields, filterQuery, endDate, + indexNames, startDate, type, skip, }: UseAuthentications): [boolean, AuthenticationArgs] => { const getAuthenticationsSelector = hostsSelectors.authenticationsSelector(); - const { activePage, limit } = useSelector( - (state: State) => getAuthenticationsSelector(state, type), - shallowEqual + const { activePage, limit } = useShallowEqualSelector((state) => + getAuthenticationsSelector(state, type) ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [authenticationsRequest, setAuthenticationsRequest] = useState< HostAuthenticationsRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.authentications, filterQuery: createFilter(filterQuery), @@ -136,7 +139,7 @@ export const useAuthentications = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setAuthenticationsResponse((prevResponse) => ({ @@ -149,7 +152,7 @@ export const useAuthentications = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -182,7 +185,7 @@ export const useAuthentications = ({ setAuthenticationsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -197,7 +200,7 @@ export const useAuthentications = ({ } return prevRequest; }); - }, [activePage, defaultIndex, docValueFields, endDate, filterQuery, limit, skip, startDate]); + }, [activePage, docValueFields, endDate, filterQuery, indexNames, limit, skip, startDate]); useEffect(() => { authenticationsSearch(authenticationsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx index 7b248d867bb76..5b69e20398a35 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/_index.tsx @@ -10,7 +10,6 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; import { @@ -21,7 +20,11 @@ import { } from '../../../../../common/search_strategy/security_solution/hosts'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../../src/plugins/data/common'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../../src/plugins/data/common'; import { getInspectResponse } from '../../../../helpers'; import { InspectResponse } from '../../../../types'; @@ -37,9 +40,10 @@ export interface HostDetailsArgs { } interface UseHostDetails { - id?: string; - hostName: string; endDate: string; + hostName: string; + id?: string; + indexNames: string[]; skip?: boolean; startDate: string; } @@ -47,17 +51,17 @@ interface UseHostDetails { export const useHostDetails = ({ endDate, hostName, + indexNames, + id = ID, skip = false, startDate, - id = ID, }: UseHostDetails): [boolean, HostDetailsArgs] => { - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostDetailsRequest, setHostDetailsRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, hostName, factoryQueryType: HostsQueries.details, timerange: { @@ -93,7 +97,7 @@ export const useHostDetails = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setHostDetailsResponse((prevResponse) => ({ @@ -104,7 +108,7 @@ export const useHostDetails = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -138,7 +142,7 @@ export const useHostDetails = ({ setHostDetailsRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, hostName, timerange: { interval: '12h', @@ -151,7 +155,7 @@ export const useHostDetails = ({ } return prevRequest; }); - }, [defaultIndex, endDate, hostName, startDate, skip]); + }, [endDate, hostName, indexNames, startDate, skip]); useEffect(() => { hostDetailsSearch(hostDetailsRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx index 12a82c7980b61..0236270d18618 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/details/index.tsx @@ -10,11 +10,9 @@ import { Query } from 'react-apollo'; import { connect } from 'react-redux'; import { compose } from 'redux'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { inputsModel, inputsSelectors, State } from '../../../../common/store'; import { getDefaultFetchPolicy } from '../../../../common/containers/helpers'; import { QueryTemplate, QueryTemplateProps } from '../../../../common/containers/query_template'; -import { withKibana, WithKibanaProps } from '../../../../common/lib/kibana'; import { HostOverviewQuery } from './host_overview.gql_query'; import { GetHostOverviewQuery, HostItem } from '../../../../graphql/types'; @@ -42,7 +40,7 @@ export interface OwnProps extends QueryTemplateProps { endDate: string; } -type HostsOverViewProps = OwnProps & HostOverviewReduxProps & WithKibanaProps; +type HostsOverViewProps = OwnProps & HostOverviewReduxProps; class HostOverviewByNameComponentQuery extends QueryTemplate< HostsOverViewProps, @@ -52,10 +50,10 @@ class HostOverviewByNameComponentQuery extends QueryTemplate< public render() { const { id = ID, + indexNames, isInspected, children, hostName, - kibana, skip, sourceId, startDate, @@ -75,7 +73,7 @@ class HostOverviewByNameComponentQuery extends QueryTemplate< from: startDate, to: endDate, }, - defaultIndex: kibana.services.uiSettings.get(DEFAULT_INDEX_KEY), + defaultIndex: indexNames, inspect: isInspected, }} > @@ -108,6 +106,5 @@ const makeMapStateToProps = () => { }; export const HostOverviewByNameQuery = compose>( - connect(makeMapStateToProps), - withKibana + connect(makeMapStateToProps) )(HostOverviewByNameComponentQuery); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx index 169fe58e9a2cc..cc944a59571f1 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/first_last_seen/index.tsx @@ -7,18 +7,20 @@ import deepEqual from 'fast-deep-equal'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; - import { useKibana } from '../../../../common/lib/kibana'; import { HostsQueries, HostFirstLastSeenRequestOptions, HostFirstLastSeenStrategyResponse, } from '../../../../../common/search_strategy/security_solution'; -import { useWithSource } from '../../../../common/containers/source'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../../src/plugins/data/common'; +import { DocValueFields } from '../../../../../common/search_strategy'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../../src/plugins/data/common'; const ID = 'firstLastSeenHostQuery'; @@ -29,21 +31,23 @@ export interface FirstLastSeenHostArgs { lastSeen?: string | null; } interface UseHostFirstLastSeen { + docValueFields: DocValueFields[]; hostName: string; + indexNames: string[]; } export const useFirstLastSeenHost = ({ + docValueFields, hostName, + indexNames, }: UseHostFirstLastSeen): [boolean, FirstLastSeenHostArgs] => { - const { docValueFields } = useWithSource('default'); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [firstLastSeenHostRequest, setFirstLastSeenHostRequest] = useState< HostFirstLastSeenRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.firstLastSeen, hostName, @@ -72,7 +76,7 @@ export const useFirstLastSeenHost = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setFirstLastSeenHostResponse((prevResponse) => ({ @@ -83,7 +87,7 @@ export const useFirstLastSeenHost = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -120,7 +124,7 @@ export const useFirstLastSeenHost = ({ setFirstLastSeenHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], hostName, }; @@ -129,7 +133,7 @@ export const useFirstLastSeenHost = ({ } return prevRequest; }); - }, [defaultIndex, docValueFields, hostName]); + }, [indexNames, docValueFields, hostName]); useEffect(() => { firstLastSeenHostSearch(firstLastSeenHostRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx index 11b853e0ebeb0..77f4567fc6a5f 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx @@ -7,12 +7,11 @@ import deepEqual from 'fast-deep-equal'; import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { inputsModel, State } from '../../../common/store'; import { createFilter } from '../../../common/containers/helpers'; import { useKibana } from '../../../common/lib/kibana'; +import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { hostsModel, hostsSelectors } from '../../store'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; import { @@ -26,7 +25,11 @@ import { import { ESTermQuery } from '../../../../common/typed_json'; import * as i18n from './translations'; -import { AbortError } from '../../../../../../../src/plugins/data/common'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../src/plugins/data/common'; import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; @@ -50,6 +53,7 @@ interface UseAllHost { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; type: hostsModel.HostsType; @@ -59,21 +63,21 @@ export const useAllHost = ({ docValueFields, filterQuery, endDate, + indexNames, skip = false, startDate, type, }: UseAllHost): [boolean, HostsArgs] => { const getHostsSelector = hostsSelectors.hostsSelector(); - const { activePage, direction, limit, sortField } = useSelector((state: State) => + const { activePage, direction, limit, sortField } = useShallowEqualSelector((state: State) => getHostsSelector(state, type) ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [hostsRequest, setHostRequest] = useState({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.hosts, filterQuery: createFilter(filterQuery), @@ -133,7 +137,7 @@ export const useAllHost = ({ }) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setHostsResponse((prevResponse) => ({ @@ -146,7 +150,7 @@ export const useAllHost = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -177,7 +181,7 @@ export const useAllHost = ({ setHostRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -198,11 +202,11 @@ export const useAllHost = ({ }); }, [ activePage, - defaultIndex, direction, docValueFields, endDate, filterQuery, + indexNames, limit, skip, startDate, diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.gql_query.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.gql_query.tsx deleted file mode 100644 index 077f49c4bdfa6..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.gql_query.tsx +++ /dev/null @@ -1,52 +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 gql from 'graphql-tag'; - -export const kpiHostDetailsQuery = gql` - fragment KpiHostDetailsChartFields on KpiHostHistogramData { - x - y - } - - query GetKpiHostDetailsQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - KpiHostDetails( - timerange: $timerange - filterQuery: $filterQuery - defaultIndex: $defaultIndex - ) { - authSuccess - authSuccessHistogram { - ...KpiHostDetailsChartFields - } - authFailure - authFailureHistogram { - ...KpiHostDetailsChartFields - } - uniqueSourceIps - uniqueSourceIpsHistogram { - ...KpiHostDetailsChartFields - } - uniqueDestinationIps - uniqueDestinationIpsHistogram { - ...KpiHostDetailsChartFields - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx deleted file mode 100644 index 1551e7d706714..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_host_details/index.tsx +++ /dev/null @@ -1,85 +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 { getOr } from 'lodash/fp'; -import React from 'react'; -import { Query } from 'react-apollo'; -import { connect, ConnectedProps } from 'react-redux'; - -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { KpiHostDetailsData, GetKpiHostDetailsQuery } from '../../../graphql/types'; -import { inputsModel, inputsSelectors, State } from '../../../common/store'; -import { useUiSetting } from '../../../common/lib/kibana'; -import { createFilter, getDefaultFetchPolicy } from '../../../common/containers/helpers'; -import { QueryTemplateProps } from '../../../common/containers/query_template'; - -import { kpiHostDetailsQuery } from './index.gql_query'; - -const ID = 'kpiHostDetailsQuery'; - -export interface KpiHostDetailsArgs { - id: string; - inspect: inputsModel.InspectQuery; - kpiHostDetails: KpiHostDetailsData; - loading: boolean; - refetch: inputsModel.Refetch; -} - -export interface QueryKpiHostDetailsProps extends QueryTemplateProps { - children: (args: KpiHostDetailsArgs) => React.ReactNode; -} - -const KpiHostDetailsComponentQuery = React.memo( - ({ id = ID, children, endDate, filterQuery, isInspected, skip, sourceId, startDate }) => ( - - query={kpiHostDetailsQuery} - fetchPolicy={getDefaultFetchPolicy()} - notifyOnNetworkStatusChange - skip={skip} - variables={{ - sourceId, - timerange: { - interval: '12h', - from: startDate!, - to: endDate!, - }, - filterQuery: createFilter(filterQuery), - defaultIndex: useUiSetting(DEFAULT_INDEX_KEY), - inspect: isInspected, - }} - > - {({ data, loading, refetch }) => { - const kpiHostDetails = getOr({}, `source.KpiHostDetails`, data); - return children({ - id, - inspect: getOr(null, 'source.KpiHostDetails.inspect', data), - kpiHostDetails, - loading, - refetch, - }); - }} - - ) -); - -KpiHostDetailsComponentQuery.displayName = 'KpiHostDetailsComponentQuery'; - -const makeMapStateToProps = () => { - const getQuery = inputsSelectors.globalQueryByIdSelector(); - const mapStateToProps = (state: State, { id = ID }: QueryKpiHostDetailsProps) => { - const { isInspected } = getQuery(state, id); - return { - isInspected, - }; - }; - return mapStateToProps; -}; - -const connector = connect(makeMapStateToProps); - -type PropsFromRedux = ConnectedProps; - -export const KpiHostDetailsQuery = connector(KpiHostDetailsComponentQuery); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx new file mode 100644 index 0000000000000..404231be1e6cd --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/index.tsx @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 deepEqual from 'fast-deep-equal'; +import { noop } from 'lodash/fp'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import { inputsModel } from '../../../../common/store'; +import { createFilter } from '../../../../common/containers/helpers'; +import { useKibana } from '../../../../common/lib/kibana'; +import { + HostsKpiQueries, + HostsKpiAuthenticationsRequestOptions, + HostsKpiAuthenticationsStrategyResponse, +} from '../../../../../common/search_strategy'; +import { ESTermQuery } from '../../../../../common/typed_json'; + +import * as i18n from './translations'; +import { AbortError } from '../../../../../../../../src/plugins/data/common'; +import { getInspectResponse } from '../../../../helpers'; +import { InspectResponse } from '../../../../types'; + +const ID = 'hostsKpiAuthenticationsQuery'; + +export interface HostsKpiAuthenticationsArgs + extends Omit { + id: string; + inspect: InspectResponse; + isInspected: boolean; + refetch: inputsModel.Refetch; +} + +interface UseHostsKpiAuthentications { + filterQuery?: ESTermQuery | string; + endDate: string; + indexNames: string[]; + skip?: boolean; + startDate: string; +} + +export const useHostsKpiAuthentications = ({ + filterQuery, + endDate, + indexNames, + skip = false, + startDate, +}: UseHostsKpiAuthentications): [boolean, HostsKpiAuthenticationsArgs] => { + const { data, notifications } = useKibana().services; + const refetch = useRef(noop); + const abortCtrl = useRef(new AbortController()); + const [loading, setLoading] = useState(false); + const [hostsKpiAuthenticationsRequest, setHostsKpiAuthenticationsRequest] = useState< + HostsKpiAuthenticationsRequestOptions + >({ + defaultIndex: indexNames, + factoryQueryType: HostsKpiQueries.kpiAuthentications, + filterQuery: createFilter(filterQuery), + id: ID, + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }); + + const [hostsKpiAuthenticationsResponse, setHostsKpiAuthenticationsResponse] = useState< + HostsKpiAuthenticationsArgs + >({ + authenticationsSuccess: 0, + authenticationsSuccessHistogram: [], + authenticationsFailure: 0, + authenticationsFailureHistogram: [], + id: ID, + inspect: { + dsl: [], + response: [], + }, + isInspected: false, + refetch: refetch.current, + }); + + const hostsKpiAuthenticationsSearch = useCallback( + (request: HostsKpiAuthenticationsRequestOptions) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + + const searchSubscription$ = data.search + .search( + request, + { + strategy: 'securitySolutionSearchStrategy', + abortSignal: abortCtrl.current.signal, + } + ) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + setHostsKpiAuthenticationsResponse((prevResponse) => ({ + ...prevResponse, + authenticationsSuccess: response.authenticationsSuccess, + authenticationsSuccessHistogram: response.authenticationsSuccessHistogram, + authenticationsFailure: response.authenticationsFailure, + authenticationsFailureHistogram: response.authenticationsFailureHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + } + searchSubscription$.unsubscribe(); + } else if (response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + } + // TODO: Make response error status clearer + notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_AUTHENTICATIONS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_AUTHENTICATIONS, + text: msg.message, + }); + } + }, + }); + }; + abortCtrl.current.abort(); + asyncSearch(); + refetch.current = asyncSearch; + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, notifications.toasts] + ); + + useEffect(() => { + setHostsKpiAuthenticationsRequest((prevRequest) => { + const myRequest = { + ...prevRequest, + defaultIndex: indexNames, + filterQuery: createFilter(filterQuery), + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }; + if (!skip && !deepEqual(prevRequest, myRequest)) { + return myRequest; + } + return prevRequest; + }); + }, [indexNames, endDate, filterQuery, skip, startDate]); + + useEffect(() => { + hostsKpiAuthenticationsSearch(hostsKpiAuthenticationsRequest); + }, [hostsKpiAuthenticationsRequest, hostsKpiAuthenticationsSearch]); + + return [loading, hostsKpiAuthenticationsResponse]; +}; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/translations.ts b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/translations.ts new file mode 100644 index 0000000000000..fb5af83d0acef --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/authentications/translations.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ERROR_HOSTS_KPI_AUTHENTICATIONS = i18n.translate( + 'xpack.securitySolution.hostsKpiAuthentications.errorSearchDescription', + { + defaultMessage: `An error has occurred on hosts kpi authentications search`, + } +); + +export const FAIL_HOSTS_KPI_AUTHENTICATIONS = i18n.translate( + 'xpack.securitySolution.hostsKpiAuthentications.failSearchDescription', + { + defaultMessage: `Failed to run search on hosts kpi authentications`, + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx new file mode 100644 index 0000000000000..bb918a9214f40 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/index.tsx @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 deepEqual from 'fast-deep-equal'; +import { noop } from 'lodash/fp'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import { inputsModel } from '../../../../common/store'; +import { createFilter } from '../../../../common/containers/helpers'; +import { useKibana } from '../../../../common/lib/kibana'; +import { + HostsKpiQueries, + HostsKpiHostsRequestOptions, + HostsKpiHostsStrategyResponse, +} from '../../../../../common/search_strategy'; +import { ESTermQuery } from '../../../../../common/typed_json'; + +import * as i18n from './translations'; +import { AbortError } from '../../../../../../../../src/plugins/data/common'; +import { getInspectResponse } from '../../../../helpers'; +import { InspectResponse } from '../../../../types'; + +const ID = 'hostsKpiHostsQuery'; + +export interface HostsKpiHostsArgs extends Omit { + id: string; + inspect: InspectResponse; + isInspected: boolean; + refetch: inputsModel.Refetch; +} + +interface UseHostsKpiHosts { + filterQuery?: ESTermQuery | string; + endDate: string; + indexNames: string[]; + skip?: boolean; + startDate: string; +} + +export const useHostsKpiHosts = ({ + filterQuery, + endDate, + indexNames, + skip = false, + startDate, +}: UseHostsKpiHosts): [boolean, HostsKpiHostsArgs] => { + const { data, notifications } = useKibana().services; + const refetch = useRef(noop); + const abortCtrl = useRef(new AbortController()); + const [loading, setLoading] = useState(false); + const [hostsKpiHostsRequest, setHostsKpiHostsRequest] = useState({ + defaultIndex: indexNames, + factoryQueryType: HostsKpiQueries.kpiHosts, + filterQuery: createFilter(filterQuery), + id: ID, + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }); + + const [hostsKpiHostsResponse, setHostsKpiHostsResponse] = useState({ + hosts: 0, + hostsHistogram: [], + id: ID, + inspect: { + dsl: [], + response: [], + }, + isInspected: false, + refetch: refetch.current, + }); + + const hostsKpiHostsSearch = useCallback( + (request: HostsKpiHostsRequestOptions) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + + const searchSubscription$ = data.search + .search(request, { + strategy: 'securitySolutionSearchStrategy', + abortSignal: abortCtrl.current.signal, + }) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + setHostsKpiHostsResponse((prevResponse) => ({ + ...prevResponse, + hosts: response.hosts, + hostsHistogram: response.hostsHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + } + searchSubscription$.unsubscribe(); + } else if (response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + } + // TODO: Make response error status clearer + notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_HOSTS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_HOSTS, + text: msg.message, + }); + } + }, + }); + }; + abortCtrl.current.abort(); + asyncSearch(); + refetch.current = asyncSearch; + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, notifications.toasts] + ); + + useEffect(() => { + setHostsKpiHostsRequest((prevRequest) => { + const myRequest = { + ...prevRequest, + defaultIndex: indexNames, + filterQuery: createFilter(filterQuery), + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }; + if (!skip && !deepEqual(prevRequest, myRequest)) { + return myRequest; + } + return prevRequest; + }); + }, [indexNames, endDate, filterQuery, skip, startDate]); + + useEffect(() => { + hostsKpiHostsSearch(hostsKpiHostsRequest); + }, [hostsKpiHostsRequest, hostsKpiHostsSearch]); + + return [loading, hostsKpiHostsResponse]; +}; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/translations.ts b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/translations.ts new file mode 100644 index 0000000000000..2a15563a4b1cd --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/hosts/translations.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ERROR_HOSTS_KPI_HOSTS = i18n.translate( + 'xpack.securitySolution.hostsKpiHosts.errorSearchDescription', + { + defaultMessage: `An error has occurred on hosts kpi hosts search`, + } +); + +export const FAIL_HOSTS_KPI_HOSTS = i18n.translate( + 'xpack.securitySolution.hostsKpiHosts.failSearchDescription', + { + defaultMessage: `Failed to run search on hosts kpi hosts`, + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.gql_query.ts b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.gql_query.ts deleted file mode 100644 index 37d54455db1fd..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.gql_query.ts +++ /dev/null @@ -1,52 +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 gql from 'graphql-tag'; - -export const kpiHostsQuery = gql` - fragment KpiHostChartFields on KpiHostHistogramData { - x - y - } - - query GetKpiHostsQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - KpiHosts(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) { - hosts - hostsHistogram { - ...KpiHostChartFields - } - authSuccess - authSuccessHistogram { - ...KpiHostChartFields - } - authFailure - authFailureHistogram { - ...KpiHostChartFields - } - uniqueSourceIps - uniqueSourceIpsHistogram { - ...KpiHostChartFields - } - uniqueDestinationIps - uniqueDestinationIpsHistogram { - ...KpiHostChartFields - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.tsx index 1a6df58f04597..c0ae767219aae 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/index.tsx @@ -4,82 +4,6 @@ * 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, ConnectedProps } from 'react-redux'; - -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { GetKpiHostsQuery, KpiHostsData } from '../../../graphql/types'; -import { inputsModel, inputsSelectors, State } from '../../../common/store'; -import { useUiSetting } from '../../../common/lib/kibana'; -import { createFilter, getDefaultFetchPolicy } from '../../../common/containers/helpers'; -import { QueryTemplateProps } from '../../../common/containers/query_template'; - -import { kpiHostsQuery } from './index.gql_query'; - -const ID = 'kpiHostsQuery'; - -export interface KpiHostsArgs { - id: string; - inspect: inputsModel.InspectQuery; - kpiHosts: KpiHostsData; - loading: boolean; - refetch: inputsModel.Refetch; -} - -export interface KpiHostsProps extends QueryTemplateProps { - children: (args: KpiHostsArgs) => React.ReactNode; -} - -const KpiHostsComponentQuery = React.memo( - ({ id = ID, children, endDate, filterQuery, isInspected, skip, sourceId, startDate }) => ( - - query={kpiHostsQuery} - fetchPolicy={getDefaultFetchPolicy()} - notifyOnNetworkStatusChange - skip={skip} - variables={{ - sourceId, - timerange: { - interval: '12h', - from: startDate!, - to: endDate!, - }, - filterQuery: createFilter(filterQuery), - defaultIndex: useUiSetting(DEFAULT_INDEX_KEY), - inspect: isInspected, - }} - > - {({ data, loading, refetch }) => { - const kpiHosts = getOr({}, `source.KpiHosts`, data); - return children({ - id, - inspect: getOr(null, 'source.KpiHosts.inspect', data), - kpiHosts, - loading, - refetch, - }); - }} - - ) -); - -KpiHostsComponentQuery.displayName = 'KpiHostsComponentQuery'; - -const makeMapStateToProps = () => { - const getQuery = inputsSelectors.globalQueryByIdSelector(); - const mapStateToProps = (state: State, { id = ID }: KpiHostsProps) => { - const { isInspected } = getQuery(state, id); - return { - isInspected, - }; - }; - return mapStateToProps; -}; - -const connector = connect(makeMapStateToProps); - -type PropsFromRedux = ConnectedProps; - -export const KpiHostsQuery = connector(KpiHostsComponentQuery); +export * from './authentications'; +export * from './hosts'; +export * from './unique_ips'; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx new file mode 100644 index 0000000000000..b8e93eef8dc91 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/index.tsx @@ -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 deepEqual from 'fast-deep-equal'; +import { noop } from 'lodash/fp'; +import { useCallback, useEffect, useRef, useState } from 'react'; + +import { inputsModel } from '../../../../common/store'; +import { createFilter } from '../../../../common/containers/helpers'; +import { useKibana } from '../../../../common/lib/kibana'; +import { + HostsKpiQueries, + HostsKpiUniqueIpsRequestOptions, + HostsKpiUniqueIpsStrategyResponse, +} from '../../../../../common/search_strategy'; +import { ESTermQuery } from '../../../../../common/typed_json'; + +import * as i18n from './translations'; +import { AbortError } from '../../../../../../../../src/plugins/data/common'; +import { getInspectResponse } from '../../../../helpers'; +import { InspectResponse } from '../../../../types'; + +const ID = 'hostsKpiUniqueIpsQuery'; + +export interface HostsKpiUniqueIpsArgs + extends Omit { + id: string; + inspect: InspectResponse; + isInspected: boolean; + refetch: inputsModel.Refetch; +} + +interface UseHostsKpiUniqueIps { + filterQuery?: ESTermQuery | string; + endDate: string; + indexNames: string[]; + skip?: boolean; + startDate: string; +} + +export const useHostsKpiUniqueIps = ({ + filterQuery, + endDate, + indexNames, + skip = false, + startDate, +}: UseHostsKpiUniqueIps): [boolean, HostsKpiUniqueIpsArgs] => { + const { data, notifications } = useKibana().services; + const refetch = useRef(noop); + const abortCtrl = useRef(new AbortController()); + const [loading, setLoading] = useState(false); + const [hostsKpiUniqueIpsRequest, setHostsKpiUniqueIpsRequest] = useState< + HostsKpiUniqueIpsRequestOptions + >({ + defaultIndex: indexNames, + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + filterQuery: createFilter(filterQuery), + id: ID, + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }); + + const [hostsKpiUniqueIpsResponse, setHostsKpiUniqueIpsResponse] = useState( + { + uniqueSourceIps: 0, + uniqueSourceIpsHistogram: [], + uniqueDestinationIps: 0, + uniqueDestinationIpsHistogram: [], + id: ID, + inspect: { + dsl: [], + response: [], + }, + isInspected: false, + refetch: refetch.current, + } + ); + + const hostsKpiUniqueIpsSearch = useCallback( + (request: HostsKpiUniqueIpsRequestOptions) => { + let didCancel = false; + const asyncSearch = async () => { + abortCtrl.current = new AbortController(); + setLoading(true); + + const searchSubscription$ = data.search + .search(request, { + strategy: 'securitySolutionSearchStrategy', + abortSignal: abortCtrl.current.signal, + }) + .subscribe({ + next: (response) => { + if (!response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + setHostsKpiUniqueIpsResponse((prevResponse) => ({ + ...prevResponse, + uniqueSourceIps: response.uniqueSourceIps, + uniqueSourceIpsHistogram: response.uniqueSourceIpsHistogram, + uniqueDestinationIps: response.uniqueDestinationIps, + uniqueDestinationIpsHistogram: response.uniqueDestinationIpsHistogram, + inspect: getInspectResponse(response, prevResponse.inspect), + refetch: refetch.current, + })); + } + searchSubscription$.unsubscribe(); + } else if (response.isPartial && !response.isRunning) { + if (!didCancel) { + setLoading(false); + } + // TODO: Make response error status clearer + notifications.toasts.addWarning(i18n.ERROR_HOSTS_KPI_UNIQUE_IPS); + searchSubscription$.unsubscribe(); + } + }, + error: (msg) => { + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger({ + title: i18n.FAIL_HOSTS_KPI_UNIQUE_IPS, + text: msg.message, + }); + } + }, + }); + }; + abortCtrl.current.abort(); + asyncSearch(); + refetch.current = asyncSearch; + return () => { + didCancel = true; + abortCtrl.current.abort(); + }; + }, + [data.search, notifications.toasts] + ); + + useEffect(() => { + setHostsKpiUniqueIpsRequest((prevRequest) => { + const myRequest = { + ...prevRequest, + defaultIndex: indexNames, + filterQuery: createFilter(filterQuery), + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + }; + if (!skip && !deepEqual(prevRequest, myRequest)) { + return myRequest; + } + return prevRequest; + }); + }, [indexNames, endDate, filterQuery, skip, startDate]); + + useEffect(() => { + hostsKpiUniqueIpsSearch(hostsKpiUniqueIpsRequest); + }, [hostsKpiUniqueIpsRequest, hostsKpiUniqueIpsSearch]); + + return [loading, hostsKpiUniqueIpsResponse]; +}; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/translations.ts b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/translations.ts new file mode 100644 index 0000000000000..2d1574b080ac1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/containers/kpi_hosts/unique_ips/translations.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ERROR_HOSTS_KPI_UNIQUE_IPS = i18n.translate( + 'xpack.securitySolution.hostsKpiUniqueIps.errorSearchDescription', + { + defaultMessage: `An error has occurred on hosts kpi unique ips search`, + } +); + +export const FAIL_HOSTS_KPI_UNIQUE_IPS = i18n.translate( + 'xpack.securitySolution.hostsKpiUniqueIps.failSearchDescription', + { + defaultMessage: `Failed to run search on hosts kpi unique ips`, + } +); diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts deleted file mode 100644 index d984de020faa1..0000000000000 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query.ts +++ /dev/null @@ -1,59 +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 gql from 'graphql-tag'; - -export const uncommonProcessesQuery = gql` - query GetUncommonProcessesQuery( - $sourceId: ID! - $timerange: TimerangeInput! - $pagination: PaginationInputPaginated! - $filterQuery: String - $defaultIndex: [String!]! - $inspect: Boolean! - ) { - source(id: $sourceId) { - id - UncommonProcesses( - timerange: $timerange - pagination: $pagination - filterQuery: $filterQuery - defaultIndex: $defaultIndex - ) { - totalCount - edges { - node { - _id - instances - process { - args - name - } - user { - id - name - } - hosts { - name - } - } - cursor { - value - } - } - pageInfo { - activePage - fakeTotalCount - showMorePagesIndicator - } - inspect @include(if: $inspect) { - dsl - response - } - } - } - } -`; diff --git a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx index 82f5a97e9e413..4036837024025 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/uncommon_processes/index.tsx @@ -9,22 +9,26 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; -import { AbortError } from '../../../../../../../src/plugins/data/common'; +import { + AbortError, + isCompleteResponse, + isErrorResponse, +} from '../../../../../../../src/plugins/data/common'; -import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; -import { PageInfoPaginated, UncommonProcessesEdges } from '../../../graphql/types'; import { inputsModel, State } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; import { generateTablePaginationOptions } from '../../../common/components/paginated_table/helpers'; import { createFilter } from '../../../common/containers/helpers'; - import { hostsModel, hostsSelectors } from '../../store'; import { - HostUncommonProcessesRequestOptions, - HostUncommonProcessesStrategyResponse, -} from '../../../../common/search_strategy/security_solution/hosts/uncommon_processes'; -import { HostsQueries } from '../../../../common/search_strategy/security_solution/hosts'; -import { DocValueFields, SortField } from '../../../../common/search_strategy'; + DocValueFields, + SortField, + PageInfoPaginated, + HostsUncommonProcessesEdges, + HostsQueries, + HostsUncommonProcessesRequestOptions, + HostsUncommonProcessesStrategyResponse, +} from '../../../../common/search_strategy'; import * as i18n from './translations'; import { ESTermQuery } from '../../../../common/typed_json'; @@ -41,13 +45,14 @@ export interface UncommonProcessesArgs { pageInfo: PageInfoPaginated; refetch: inputsModel.Refetch; totalCount: number; - uncommonProcesses: UncommonProcessesEdges[]; + uncommonProcesses: HostsUncommonProcessesEdges[]; } interface UseUncommonProcesses { docValueFields?: DocValueFields[]; filterQuery?: ESTermQuery | string; endDate: string; + indexNames: string[]; skip?: boolean; startDate: string; type: hostsModel.HostsType; @@ -57,6 +62,7 @@ export const useUncommonProcesses = ({ docValueFields, filterQuery, endDate, + indexNames, skip = false, startDate, type, @@ -65,15 +71,14 @@ export const useUncommonProcesses = ({ const { activePage, limit } = useSelector((state: State) => getUncommonProcessesSelector(state, type) ); - const { data, notifications, uiSettings } = useKibana().services; + const { data, notifications } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); - const defaultIndex = uiSettings.get(DEFAULT_INDEX_KEY); const [loading, setLoading] = useState(false); const [uncommonProcessesRequest, setUncommonProcessesRequest] = useState< - HostUncommonProcessesRequestOptions + HostsUncommonProcessesRequestOptions >({ - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], factoryQueryType: HostsQueries.uncommonProcesses, filterQuery: createFilter(filterQuery), @@ -119,14 +124,14 @@ export const useUncommonProcesses = ({ ); const uncommonProcessesSearch = useCallback( - (request: HostUncommonProcessesRequestOptions) => { + (request: HostsUncommonProcessesRequestOptions) => { let didCancel = false; const asyncSearch = async () => { abortCtrl.current = new AbortController(); setLoading(true); const searchSubscription$ = data.search - .search( + .search( request, { strategy: 'securitySolutionSearchStrategy', @@ -135,7 +140,7 @@ export const useUncommonProcesses = ({ ) .subscribe({ next: (response) => { - if (!response.isPartial && !response.isRunning) { + if (isCompleteResponse(response)) { if (!didCancel) { setLoading(false); setUncommonProcessesResponse((prevResponse) => ({ @@ -148,7 +153,7 @@ export const useUncommonProcesses = ({ })); } searchSubscription$.unsubscribe(); - } else if (response.isPartial && !response.isRunning) { + } else if (isErrorResponse(response)) { if (!didCancel) { setLoading(false); } @@ -181,7 +186,7 @@ export const useUncommonProcesses = ({ setUncommonProcessesRequest((prevRequest) => { const myRequest = { ...prevRequest, - defaultIndex, + defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), pagination: generateTablePaginationOptions(activePage, limit), @@ -197,7 +202,7 @@ export const useUncommonProcesses = ({ } return prevRequest; }); - }, [activePage, defaultIndex, docValueFields, endDate, filterQuery, limit, skip, startDate]); + }, [activePage, indexNames, docValueFields, endDate, filterQuery, limit, skip, startDate]); useEffect(() => { uncommonProcessesSearch(uncommonProcessesRequest); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx index 11a268c7b64ad..708c8b2b40b35 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx @@ -84,6 +84,7 @@ describe('body', () => { setQuery={jest.fn()} setAbsoluteRangeDatePicker={(jest.fn() as unknown) as SetAbsoluteRangeDatePicker} hostDetailsPagePath={hostDetailsPagePath} + indexNames={[]} indexPattern={mockIndexPattern} type={type} pageFilters={mockHostDetailsPageFilters} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx index 4d4eead0e778a..284e6e27cf615 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx @@ -28,12 +28,13 @@ import { export const HostDetailsTabs = React.memo( ({ + detailName, docValueFields, - pageFilters, filterQuery, - detailName, - setAbsoluteRangeDatePicker, + indexNames, indexPattern, + pageFilters, + setAbsoluteRangeDatePicker, hostDetailsPagePath, }) => { const { from, to, isInitializing, deleteQuery, setQuery } = useGlobalTime(); @@ -73,6 +74,7 @@ export const HostDetailsTabs = React.memo( startDate: from, type, indexPattern, + indexNames, hostName: detailName, narrowDateRange, updateDateRange, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index d8cd59f119d52..a8b46769b7363 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -9,7 +9,7 @@ import { noop } from 'lodash/fp'; import React, { useEffect, useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { HostItem } from '../../../../common/search_strategy'; +import { HostItem, LastEventIndexKey } from '../../../../common/search_strategy'; import { SecurityPageName } from '../../../app/types'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { FiltersGlobal } from '../../../common/components/filters_global'; @@ -21,16 +21,13 @@ import { hasMlUserPermissions } from '../../../../common/machine_learning/has_ml import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities'; import { scoreIntervalToDateTime } from '../../../common/components/ml/score/score_interval_to_datetime'; import { SiemNavigation } from '../../../common/components/navigation'; -import { KpiHostsComponent } from '../../components/kpi_hosts'; +import { HostsDetailsKpiComponent } from '../../components/kpi_hosts'; import { HostOverview } from '../../../overview/components/host_overview'; import { manageQuery } from '../../../common/components/page/manage_query'; import { SiemSearchBar } from '../../../common/components/search_bar'; import { WrapperPage } from '../../../common/components/wrapper_page'; import { HostOverviewByNameQuery } from '../../containers/hosts/details'; -import { KpiHostDetailsQuery } from '../../containers/kpi_host_details'; import { useGlobalTime } from '../../../common/containers/use_global_time'; -import { useWithSource } from '../../../common/containers/source'; -import { LastEventIndexKey } from '../../../graphql/types'; import { useKibana } from '../../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../../common/lib/keury'; import { inputsSelectors, State } from '../../../common/store'; @@ -52,9 +49,9 @@ import { timelineSelectors } from '../../../timelines/store/timeline'; import { TimelineModel } from '../../../timelines/store/timeline/model'; import { TimelineId } from '../../../../common/types/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; const HostOverviewManage = manageQuery(HostOverview); -const KpiHostDetailsManage = manageQuery(KpiHostsComponent); const HostDetailsComponent = React.memo( ({ @@ -91,7 +88,7 @@ const HostDetailsComponent = React.memo( }, [setAbsoluteRangeDatePicker] ); - const { docValueFields, indicesExist, indexPattern } = useWithSource(); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, @@ -113,12 +110,18 @@ const HostDetailsComponent = React.memo( + } title={detailName} /> ( > {({ isLoadingAnomaliesData, anomaliesData }) => ( ( data={hostOverview as HostItem} anomaliesData={anomaliesData} isLoadingAnomaliesData={isLoadingAnomaliesData} + indexNames={selectedPatterns} loading={loading} startDate={from} endDate={to} @@ -160,27 +165,15 @@ const HostDetailsComponent = React.memo( - - {({ kpiHostDetails, id, inspect, loading, refetch }) => ( - - )} - + /> @@ -193,6 +186,7 @@ const HostDetailsComponent = React.memo( ; export type HostDetailsTabsProps = HostBodyComponentDispatchProps & HostsQueryProps & { docValueFields?: DocValueFields[]; + indexNames: string[]; pageFilters?: Filter[]; filterQuery: string; indexPattern: IIndexPattern; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx index 566f8f23efd39..b341647afdfbc 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx @@ -10,7 +10,6 @@ import { Router } from 'react-router-dom'; import { Filter } from '../../../../../../src/plugins/data/common/es_query'; import '../../common/mock/match_media'; -import { useWithSource } from '../../common/containers/source'; import { apolloClientObservable, TestProviders, @@ -25,8 +24,9 @@ import { State, createStore } from '../../common/store'; import { HostsComponentProps } from './types'; import { Hosts } from './hosts'; import { HostsTabs } from './hosts_tabs'; +import { useSourcererScope } from '../../common/containers/sourcerer'; -jest.mock('../../common/containers/source'); +jest.mock('../../common/containers/sourcerer'); // Test will fail because we will to need to mock some core services to make the test work // For now let's forget about SiemSearchBar and QueryBar @@ -58,14 +58,14 @@ const mockHistory = { createHref: jest.fn(), listen: jest.fn(), }; - +const mockUseSourcererScope = useSourcererScope as jest.Mock; describe('Hosts - rendering', () => { const hostProps: HostsComponentProps = { hostsPagePath: '', }; test('it renders the Setup Instructions text when no index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: false, }); @@ -80,7 +80,7 @@ describe('Hosts - rendering', () => { }); test('it DOES NOT render the Setup Instructions text when an index is available', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: {}, }); @@ -95,7 +95,7 @@ describe('Hosts - rendering', () => { }); test('it should render tab navigation', async () => { - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: {}, }); @@ -142,7 +142,7 @@ describe('Hosts - rendering', () => { }, }, ]; - (useWithSource as jest.Mock).mockReturnValue({ + mockUseSourcererScope.mockReturnValue({ indicesExist: true, indexPattern: { fields: [], title: 'title' }, }); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index ef88c255b1735..4835f7eff5b6f 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -17,16 +17,13 @@ import { HeaderPage } from '../../common/components/header_page'; import { LastEventTime } from '../../common/components/last_event_time'; import { hasMlUserPermissions } from '../../../common/machine_learning/has_ml_user_permissions'; import { SiemNavigation } from '../../common/components/navigation'; -import { KpiHostsComponent } from '../components/kpi_hosts'; -import { manageQuery } from '../../common/components/page/manage_query'; +import { HostsKpiComponent } from '../components/kpi_hosts'; import { SiemSearchBar } from '../../common/components/search_bar'; import { WrapperPage } from '../../common/components/wrapper_page'; -import { KpiHostsQuery } from '../containers/kpi_hosts'; import { useFullScreen } from '../../common/containers/use_full_screen'; import { useGlobalTime } from '../../common/containers/use_global_time'; -import { useWithSource } from '../../common/containers/source'; import { TimelineId } from '../../../common/types/timeline'; -import { LastEventIndexKey } from '../../graphql/types'; +import { LastEventIndexKey } from '../../../common/search_strategy'; import { useKibana } from '../../common/lib/kibana'; import { convertToBuildEsQuery } from '../../common/lib/keury'; import { inputsSelectors, State } from '../../common/store'; @@ -48,8 +45,7 @@ import { showGlobalFilters } from '../../timelines/components/timeline/helpers'; import { timelineSelectors } from '../../timelines/store/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { TimelineModel } from '../../timelines/store/timeline/model'; - -const KpiHostsComponentManage = manageQuery(KpiHostsComponent); +import { useSourcererScope } from '../../common/containers/sourcerer'; export const HostsComponent = React.memo( ({ filters, graphEventId, query, setAbsoluteRangeDatePicker, hostsPagePath }) => { @@ -78,7 +74,7 @@ export const HostsComponent = React.memo( }, [setAbsoluteRangeDatePicker] ); - const { docValueFields, indicesExist, indexPattern } = useWithSource(); + const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const filterQuery = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, @@ -105,31 +101,25 @@ export const HostsComponent = React.memo( } + subtitle={ + + } title={i18n.PAGE_TITLE} /> - - {({ kpiHosts, loading, id, inspect, refetch }) => ( - - )} - + narrowDateRange={narrowDateRange} + /> @@ -144,11 +134,11 @@ export const HostsComponent = React.memo( to={to} filterQuery={tabsFilterQuery} isInitializing={isInitializing} + indexNames={selectedPatterns} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} setQuery={setQuery} from={from} type={hostsModel.HostsType.page} - indexPattern={indexPattern} hostsPagePath={hostsPagePath} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx index 8e2ea06fd20cb..17dd20bac2d0d 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx @@ -28,24 +28,24 @@ export const HostsTabs = memo( deleteQuery, docValueFields, filterQuery, - setAbsoluteRangeDatePicker, - to, from, - setQuery, + indexNames, isInitializing, - type, - indexPattern, hostsPagePath, + setAbsoluteRangeDatePicker, + setQuery, + to, + type, }) => { const tabProps = { deleteQuery, endDate: to, filterQuery, + indexNames, skip: isInitializing, setQuery, startDate: from, type, - indexPattern, narrowDateRange: useCallback( (score: Anomaly, interval: string) => { const fromTo = scoreIntervalToDateTime(score, interval); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx index 65ddb9305f607..efce312fd85f2 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/authentications_query_tab_body.tsx @@ -16,7 +16,7 @@ import { MatrixHistogramConfigs, } from '../../../common/components/matrix_histogram/types'; import { MatrixHistogram } from '../../../common/components/matrix_histogram'; -import { KpiHostsChartColors } from '../../components/kpi_hosts/types'; +import { HostsKpiChartColors } from '../../components/kpi_hosts/types'; import * as i18n from '../translations'; import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; @@ -24,7 +24,7 @@ const AuthenticationTableManage = manageQuery(AuthenticationTable); const ID = 'authenticationsHistogramQuery'; -const authStackByOptions: MatrixHistogramOption[] = [ +const authenticationsStackByOptions: MatrixHistogramOption[] = [ { text: 'event.outcome', value: 'event.outcome', @@ -32,31 +32,32 @@ const authStackByOptions: MatrixHistogramOption[] = [ ]; const DEFAULT_STACK_BY = 'event.outcome'; -enum AuthMatrixDataGroup { - authSuccess = 'success', - authFailure = 'failure', +enum AuthenticationsMatrixDataGroup { + authenticationsSuccess = 'success', + authenticationsFailure = 'failure', } -export const authMatrixDataMappingFields: MatrixHistogramMappingTypes = { - [AuthMatrixDataGroup.authSuccess]: { - key: AuthMatrixDataGroup.authSuccess, +export const authenticationsMatrixDataMappingFields: MatrixHistogramMappingTypes = { + [AuthenticationsMatrixDataGroup.authenticationsSuccess]: { + key: AuthenticationsMatrixDataGroup.authenticationsSuccess, value: null, - color: KpiHostsChartColors.authSuccess, + color: HostsKpiChartColors.authenticationsSuccess, }, - [AuthMatrixDataGroup.authFailure]: { - key: AuthMatrixDataGroup.authFailure, + [AuthenticationsMatrixDataGroup.authenticationsFailure]: { + key: AuthenticationsMatrixDataGroup.authenticationsFailure, value: null, - color: KpiHostsChartColors.authFailure, + color: HostsKpiChartColors.authenticationsFailure, }, }; const histogramConfigs: MatrixHistogramConfigs = { defaultStackByOption: - authStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? authStackByOptions[0], + authenticationsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? + authenticationsStackByOptions[0], errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA, histogramType: MatrixHistogramType.authentications, - mapping: authMatrixDataMappingFields, - stackByOptions: authStackByOptions, + mapping: authenticationsMatrixDataMappingFields, + stackByOptions: authenticationsStackByOptions, title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, }; @@ -65,6 +66,7 @@ const AuthenticationsQueryTabBodyComponent: React.FC docValueFields, endDate, filterQuery, + indexNames, skip, setQuery, startDate, @@ -73,7 +75,15 @@ const AuthenticationsQueryTabBodyComponent: React.FC const [ loading, { authentications, totalCount, pageInfo, loadPage, id, inspect, isInspected, refetch }, - ] = useAuthentications({ docValueFields, endDate, filterQuery, skip, startDate, type }); + ] = useAuthentications({ + docValueFields, + endDate, + filterQuery, + indexNames, + skip, + startDate, + type, + }); useEffect(() => { return () => { @@ -89,6 +99,7 @@ const AuthenticationsQueryTabBodyComponent: React.FC endDate={endDate} filterQuery={filterQuery} id={ID} + indexNames={indexNames} setQuery={setQuery} startDate={startDate} {...histogramConfigs} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx index be8412caf7732..e30071ec04f0c 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx @@ -20,6 +20,7 @@ import { useFullScreen } from '../../../common/containers/use_full_screen'; import * as i18n from '../translations'; import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; +import { SourcererScopeName } from '../../../common/store/sourcerer/model'; const EVENTS_HISTOGRAM_ID = 'eventsHistogramQuery'; @@ -54,6 +55,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ deleteQuery, endDate, filterQuery, + indexNames, pageFilters, setQuery, startDate, @@ -85,6 +87,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ setQuery={setQuery} startDate={startDate} id={EVENTS_HISTOGRAM_ID} + indexNames={indexNames} {...histogramConfigs} /> )} @@ -92,6 +95,7 @@ const EventsQueryTabBodyComponent: React.FC = ({ defaultModel={eventsDefaultModel} end={endDate} id={TimelineId.hostsPageEvents} + scopeId={SourcererScopeName.default} start={startDate} pageFilters={pageFilters} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx index f8dcf9635c053..deda4b618fa64 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/hosts_query_tab_body.tsx @@ -18,7 +18,7 @@ export const HostsQueryTabBody = ({ docValueFields, endDate, filterQuery, - indexPattern, + indexNames, skip, setQuery, startDate, @@ -27,7 +27,7 @@ export const HostsQueryTabBody = ({ const [ loading, { hosts, totalCount, pageInfo, loadPage, id, inspect, isInspected, refetch }, - ] = useAllHost({ docValueFields, endDate, filterQuery, skip, startDate, type }); + ] = useAllHost({ docValueFields, endDate, filterQuery, indexNames, skip, startDate, type }); return ( { })}${appendSearch(search)}`; }; -const isDefaultOrMissing = ( - value: number | string | undefined, - defaultValue: number | undefined -) => { +const isDefaultOrMissing = (value: T | undefined, defaultValue: T) => { return value === undefined || value === defaultValue; }; diff --git a/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx b/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx index 372916581b35d..1d27c75de009d 100644 --- a/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/components/administration_list_page.tsx @@ -35,7 +35,12 @@ export const AdministrationListPage: FC - + {actions} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts index c2a838404b0bb..dce135dd213b3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts @@ -113,6 +113,26 @@ interface AppRequestedEndpointList { type: 'appRequestedEndpointList'; } +interface ServerReturnedAgenstWithEndpointsTotal { + type: 'serverReturnedAgenstWithEndpointsTotal'; + payload: number; +} + +interface ServerFailedToReturnAgenstWithEndpointsTotal { + type: 'serverFailedToReturnAgenstWithEndpointsTotal'; + payload: ServerApiError; +} + +interface ServerReturnedEndpointsTotal { + type: 'serverReturnedEndpointsTotal'; + payload: number; +} + +interface ServerFailedToReturnEndpointsTotal { + type: 'serverFailedToReturnEndpointsTotal'; + payload: ServerApiError; +} + export type EndpointAction = | ServerReturnedEndpointList | ServerFailedToReturnEndpointList @@ -131,5 +151,9 @@ export type EndpointAction = | ServerFailedToReturnMetadataPatterns | AppRequestedEndpointList | ServerReturnedEndpointNonExistingPolicies + | ServerReturnedAgenstWithEndpointsTotal | ServerReturnedEndpointAgentPolicies - | UserUpdatedEndpointListRefreshOptions; + | UserUpdatedEndpointListRefreshOptions + | ServerReturnedEndpointsTotal + | ServerFailedToReturnAgenstWithEndpointsTotal + | ServerFailedToReturnEndpointsTotal; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts index b4e00319485e9..1e3a92e6ec135 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts @@ -26,6 +26,10 @@ import { } from '../../../../common/store/test_utils'; import { getEndpointListPath } from '../../../common/routing'; +jest.mock('../../policy/store/policy_list/services/ingest', () => ({ + sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), + sendGetEndpointSecurityPackage: () => Promise.resolve({}), +})); describe('endpoint list pagination: ', () => { let fakeCoreStart: jest.Mocked; let depsStart: DepsStartMock; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index 61bcd222b1b1e..84d1dabe86910 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -58,6 +58,10 @@ describe('EndpointList store concerns', () => { patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, + agentsWithEndpointsTotal: 0, + endpointsTotal: 0, + agentsWithEndpointsTotalError: undefined, + endpointsTotalError: undefined, queryStrategyVersion: undefined, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index c4d2886f3e8e5..d19b3a0ce4177 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -24,8 +24,9 @@ import { endpointMiddlewareFactory } from './middleware'; import { getEndpointListPath } from '../../../common/routing'; jest.mock('../../policy/store/policy_list/services/ingest', () => ({ - sendGetEndpointSecurityPackage: () => Promise.resolve({}), sendGetAgentConfigList: () => Promise.resolve({ items: [] }), + sendGetAgentPolicyList: () => Promise.resolve({ items: [] }), + sendGetEndpointSecurityPackage: () => Promise.resolve({}), })); describe('endpoint list middleware', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index 7872c8824a8ee..17e0101426b07 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -24,6 +24,7 @@ import { sendGetEndpointSpecificPackagePolicies, sendGetEndpointSecurityPackage, sendGetAgentPolicyList, + sendGetFleetAgentsWithEndpoint, } from '../../policy/store/policy_list/services/ingest'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../ingest_manager/common'; import { metadataCurrentIndexPattern } from '../../../../../common/endpoint/constants'; @@ -87,6 +88,32 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory => { +const endpointsTotal = async (http: HttpStart): Promise => { try { return ( - ( - await http.post('/api/endpoint/metadata', { - body: JSON.stringify({ - paging_properties: [{ page_index: 0 }, { page_size: 1 }], - }), - }) - ).hosts.length !== 0 - ); + await http.post('/api/endpoint/metadata', { + body: JSON.stringify({ + paging_properties: [{ page_index: 0 }, { page_size: 1 }], + }), + }) + ).total; + } catch (error) { + // eslint-disable-next-line no-console + console.error(`error while trying to check for total endpoints`); + // eslint-disable-next-line no-console + console.error(error); + } + return 0; +}; + +const doEndpointsExist = async (http: HttpStart): Promise => { + try { + return (await endpointsTotal(http)) > 0; } catch (error) { // eslint-disable-next-line no-console console.error(`error while trying to check if endpoints exist`); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts index 43b6d4d349ddf..ff3bd2d9973c7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts @@ -18,11 +18,13 @@ import { INGEST_API_AGENT_POLICIES, INGEST_API_EPM_PACKAGES, INGEST_API_PACKAGE_POLICIES, + INGEST_API_FLEET_AGENTS, } from '../../policy/store/policy_list/services/ingest'; import { GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, GetPackagesResponse, + GetAgentsResponse, } from '../../../../../../ingest_manager/common/types/rest_spec'; import { GetPolicyListResponse } from '../../policy/types'; @@ -87,6 +89,7 @@ const endpointListApiPathHandlerMocks = ({ policyResponse = generator.generatePolicyResponse(), agentPolicy = generator.generateAgentPolicy(), queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2, + totalAgentsUsingEndpoint = 0, }: { /** route handlers will be setup for each individual host in this array */ endpointsResults?: HostResultList['hosts']; @@ -95,6 +98,7 @@ const endpointListApiPathHandlerMocks = ({ policyResponse?: HostPolicyResponse; agentPolicy?: GetAgentPoliciesResponseItem; queryStrategyVersion?: MetadataQueryStrategyVersions; + totalAgentsUsingEndpoint?: number; } = {}) => { const apiHandlers = { // endpoint package info @@ -143,6 +147,17 @@ const endpointListApiPathHandlerMocks = ({ total: endpointPackagePolicies?.length, }; }, + + // List of Agents using Endpoint + [INGEST_API_FLEET_AGENTS]: (): GetAgentsResponse => { + return { + total: totalAgentsUsingEndpoint, + list: [], + totalInactive: 0, + page: 1, + perPage: 10, + }; + }, }; // Build a GET route handler for each endpoint details based on the list of Endpoints passed on input @@ -185,11 +200,15 @@ export const setEndpointListApiMockImplementation: ( throw new Error(`un-expected call to http.post: ${args}`); }) // First time called, return list of endpoints + .mockImplementationOnce(async () => { + return apiHandlers['/api/endpoint/metadata'](); + }) + // Metadata is called a second time to get the full total of Endpoints regardless of filters. .mockImplementationOnce(async () => { return apiHandlers['/api/endpoint/metadata'](); }); - // If the endpoints list results is zero, then mock the second call to `/metadata` to return + // If the endpoints list results is zero, then mock the third call to `/metadata` to return // empty list - indicating there are no endpoints currently present on the system if (!endpointsResults.length) { mockedHttpService.post.mockImplementationOnce(async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 0f948f74a48e4..26d8dda2f4aec 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -36,6 +36,10 @@ export const initialEndpointListState: Immutable = { patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, + agentsWithEndpointsTotal: 0, + agentsWithEndpointsTotalError: undefined, + endpointsTotal: 0, + endpointsTotalError: undefined, queryStrategyVersion: undefined, }; @@ -160,6 +164,28 @@ export const endpointListReducer: ImmutableReducer = ( ...state, endpointsExist: action.payload, }; + } else if (action.type === 'serverReturnedAgenstWithEndpointsTotal') { + return { + ...state, + agentsWithEndpointsTotal: action.payload, + agentsWithEndpointsTotalError: undefined, + }; + } else if (action.type === 'serverFailedToReturnAgenstWithEndpointsTotal') { + return { + ...state, + agentsWithEndpointsTotalError: action.payload, + }; + } else if (action.type === 'serverReturnedEndpointsTotal') { + return { + ...state, + endpointsTotal: action.payload, + endpointsTotalError: undefined, + }; + } else if (action.type === 'serverFailedToReturnEndpointsTotal') { + return { + ...state, + endpointsTotalError: action.payload, + }; } else if (action.type === 'userUpdatedEndpointListRefreshOptions') { return { ...state, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index fe47d60afc339..29d9185b6cea5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -55,6 +55,14 @@ export const isAutoRefreshEnabled = (state: Immutable) => state.i export const autoRefreshInterval = (state: Immutable) => state.autoRefreshInterval; +export const areEndpointsEnrolling = (state: Immutable) => { + return state.agentsWithEndpointsTotal > state.endpointsTotal; +}; + +export const agentsWithEndpointsTotalError = (state: Immutable) => + state.agentsWithEndpointsTotalError; + +export const endpointsTotalError = (state: Immutable) => state.endpointsTotalError; const queryStrategyVersion = (state: Immutable) => state.queryStrategyVersion; export const endpointPackageVersion = createSelector( diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index bdd0d5e942cef..e3e2dc7b55a5e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -66,6 +66,14 @@ export interface EndpointState { isAutoRefreshEnabled: boolean; /** The current auto refresh interval for data in ms */ autoRefreshInterval: number; + /** The total Agents that contain an Endpoint package */ + agentsWithEndpointsTotal: number; + /** api error for total Agents that contain an Endpoint package */ + agentsWithEndpointsTotalError?: ServerApiError; + /** The total, actual number of Endpoints regardless of any filtering */ + endpointsTotal: number; + /** api error for total, actual Endpoints */ + endpointsTotalError?: ServerApiError; /** The query strategy version that informs whether the transform for KQL is enabled or not */ queryStrategyVersion?: MetadataQueryStrategyVersions; } diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 51a6be18471aa..debdde901407a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -7,7 +7,8 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; import { EndpointList } from './index'; -import '../../../../common/mock/match_media.ts'; +import '../../../../common/mock/match_media'; + import { mockEndpointDetailsApiResult, mockEndpointResultList, @@ -26,8 +27,25 @@ import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_da import { POLICY_STATUS_TO_HEALTH_COLOR, POLICY_STATUS_TO_TEXT } from './host_constants'; import { mockPolicyResultList } from '../../policy/store/policy_list/test_mock_utils'; -jest.mock('../../../../common/components/link_to'); +// not sure why this can't be imported from '../../../../common/mock/formatted_relative'; +// but sure enough it needs to be inline in this one file +jest.mock('@kbn/i18n/react', () => { + const originalModule = jest.requireActual('@kbn/i18n/react'); + const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + return { + ...originalModule, + FormattedRelative, + }; +}); +jest.mock('../../../../common/components/link_to'); +jest.mock('../../policy/store/policy_list/services/ingest', () => { + const originalModule = jest.requireActual('../../policy/store/policy_list/services/ingest'); + return { + ...originalModule, + sendGetEndpointSecurityPackage: () => Promise.resolve({}), + }; +}); describe('when on the list page', () => { const docGenerator = new EndpointDocGenerator(); let render: () => ReturnType; @@ -35,7 +53,6 @@ describe('when on the list page', () => { let store: AppContextTestRender['store']; let coreStart: AppContextTestRender['coreStart']; let middlewareSpy: AppContextTestRender['middlewareSpy']; - beforeEach(() => { const mockedContext = createAppRootMockRenderer(); ({ history, store, coreStart, middlewareSpy } = mockedContext); @@ -133,6 +150,63 @@ describe('when on the list page', () => { }); }); + describe('when determining when to show the enrolling message', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should display the enrolling message when there are less Endpoints than Agents', async () => { + reactTestingLibrary.act(() => { + const mockedEndpointListData = mockEndpointResultList({ + total: 4, + }); + setEndpointListApiMockImplementation(coreStart.http, { + endpointsResults: mockedEndpointListData.hosts, + totalAgentsUsingEndpoint: 5, + }); + }); + const renderResult = render(); + await reactTestingLibrary.act(async () => { + await middlewareSpy.waitForAction('serverReturnedAgenstWithEndpointsTotal'); + }); + expect(renderResult.queryByTestId('endpointsEnrollingNotification')).not.toBeNull(); + }); + + it('should NOT display the enrolling message when there are equal Endpoints than Agents', async () => { + reactTestingLibrary.act(() => { + const mockedEndpointListData = mockEndpointResultList({ + total: 5, + }); + setEndpointListApiMockImplementation(coreStart.http, { + endpointsResults: mockedEndpointListData.hosts, + totalAgentsUsingEndpoint: 5, + }); + }); + const renderResult = render(); + await reactTestingLibrary.act(async () => { + await middlewareSpy.waitForAction('serverReturnedAgenstWithEndpointsTotal'); + }); + expect(renderResult.queryByTestId('endpointsEnrollingNotification')).toBeNull(); + }); + + it('should NOT display the enrolling message when there are more Endpoints than Agents', async () => { + reactTestingLibrary.act(() => { + const mockedEndpointListData = mockEndpointResultList({ + total: 6, + }); + setEndpointListApiMockImplementation(coreStart.http, { + endpointsResults: mockedEndpointListData.hosts, + totalAgentsUsingEndpoint: 5, + }); + }); + const renderResult = render(); + await reactTestingLibrary.act(async () => { + await middlewareSpy.waitForAction('serverReturnedAgenstWithEndpointsTotal'); + }); + expect(renderResult.queryByTestId('endpointsEnrollingNotification')).toBeNull(); + }); + }); + describe('when there is no selected host in the url', () => { it('should not show the flyout', () => { const renderResult = render(); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 3e1f08eee7b94..4bb9335496ef4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -23,6 +23,7 @@ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiCallOut, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; @@ -135,6 +136,9 @@ export const EndpointList = () => { autoRefreshInterval, isAutoRefreshEnabled, patternsError, + areEndpointsEnrolling, + agentsWithEndpointsTotalError, + endpointsTotalError, isTransformEnabled, } = useEndpointSelector(selector); const { formatUrl, search } = useFormatUrl(SecurityPageName.administration); @@ -486,7 +490,7 @@ export const EndpointList = () => { }, [formatUrl, queryParams, search, agentPolicies, services?.application?.getUrlForApp]); const renderTableOrEmptyState = useMemo(() => { - if (endpointsExist) { + if (endpointsExist || areEndpointsEnrolling) { return ( { handleSelectableOnChange, selectionOptions, handleCreatePolicyClick, + areEndpointsEnrolling, ]); const hasListData = listData && listData.length > 0; @@ -544,6 +549,10 @@ export const EndpointList = () => { return !endpointsExist ? DEFAULT_POLL_INTERVAL : autoRefreshInterval; }, [endpointsExist, autoRefreshInterval]); + const hasErrorFindingTotals = useMemo(() => { + return endpointsTotalError || agentsWithEndpointsTotalError ? true : false; + }, [endpointsTotalError, agentsWithEndpointsTotalError]); + const shouldShowKQLBar = useMemo(() => { return endpointsExist && !patternsError && isTransformEnabled; }, [endpointsExist, patternsError, isTransformEnabled]); @@ -567,6 +576,21 @@ export const EndpointList = () => { > {hasSelectedEndpoint && } <> + {areEndpointsEnrolling && !hasErrorFindingTotals && ( + <> + + } + /> + + + )} {shouldShowKQLBar && ( diff --git a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx index c04d3b1ec1a90..bb947310644f8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.test.tsx @@ -15,10 +15,21 @@ jest.mock('../../common/hooks/endpoint/ingest_enabled'); describe('when in the Admistration tab', () => { let render: () => ReturnType; + let coreStart: AppContextTestRender['coreStart']; beforeEach(() => { const mockedContext = createAppRootMockRenderer(); + coreStart = mockedContext.coreStart; render = () => mockedContext.render(); + coreStart.http.get.mockImplementation(() => + Promise.resolve({ + response: [ + { + name: 'endpoint', + }, + ], + }) + ); }); it('should display the No Permissions view when Ingest is OFF', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index 959753cba7bd7..2d29e33c8d3d5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -71,14 +71,12 @@ const NoPermissions = memo(() => { /> } body={ -

- - - -

+ + + } /> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts index e3b12e06a7025..48ee6e4f67ad0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/services/ingest.ts @@ -8,6 +8,7 @@ import { HttpFetchOptions, HttpStart } from 'kibana/public'; import { GetPackagePoliciesRequest, GetAgentStatusResponse, + GetAgentsResponse, DeletePackagePoliciesResponse, DeletePackagePoliciesRequest, PACKAGE_POLICY_SAVED_OBJECT_TYPE, @@ -23,6 +24,7 @@ export const INGEST_API_PACKAGE_POLICIES = `${INGEST_API_ROOT}/package_policies` export const INGEST_API_AGENT_POLICIES = `${INGEST_API_ROOT}/agent_policies`; const INGEST_API_FLEET = `${INGEST_API_ROOT}/fleet`; const INGEST_API_FLEET_AGENT_STATUS = `${INGEST_API_FLEET}/agent-status`; +export const INGEST_API_FLEET_AGENTS = `${INGEST_API_FLEET}/agents`; export const INGEST_API_EPM_PACKAGES = `${INGEST_API_ROOT}/epm/packages`; const INGEST_API_DELETE_PACKAGE_POLICY = `${INGEST_API_PACKAGE_POLICIES}/delete`; @@ -131,6 +133,26 @@ export const sendGetFleetAgentStatusForPolicy = ( }); }; +/** + * Get a status summary for all Agents that are currently assigned to a given agent policy + * + * @param http + * @param options + */ +export const sendGetFleetAgentsWithEndpoint = ( + http: HttpStart, + options: Exclude = {} +): Promise => { + return http.get(INGEST_API_FLEET_AGENTS, { + ...options, + query: { + page: 1, + perPage: 1, + kuery: 'fleet-agents.packages : "endpoint"', + }, + }); +}; + /** * Get Endpoint Security Package information */ diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 235f150637116..40c982cfc071b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -203,6 +203,7 @@ export const PolicyDetails = React.memo(() => { )} ; + deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise; createTrustedApp(request: PostTrustedAppCreateRequest): Promise; } @@ -30,6 +37,10 @@ export class TrustedAppsHttpService implements TrustedAppsService { }); } + async deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise { + return this.http.delete(resolvePathVariables(TRUSTED_APPS_DELETE_API, request)); + } + async createTrustedApp(request: PostTrustedAppCreateRequest) { return this.http.post(TRUSTED_APPS_CREATE_API, { body: JSON.stringify(request), diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.test.ts new file mode 100644 index 0000000000000..c937b318e8961 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 { resolvePathVariables } from './utils'; + +describe('utils', () => { + describe('resolvePathVariables', () => { + it('should resolve defined variables', () => { + expect(resolvePathVariables('/segment1/{var1}/segment2', { var1: 'value1' })).toBe( + '/segment1/value1/segment2' + ); + }); + + it('should not resolve undefined variables', () => { + expect(resolvePathVariables('/segment1/{var1}/segment2', {})).toBe( + '/segment1/{var1}/segment2' + ); + }); + + it('should ignore unused variables', () => { + expect(resolvePathVariables('/segment1/{var1}/segment2', { var2: 'value2' })).toBe( + '/segment1/{var1}/segment2' + ); + }); + + it('should replace multiple variable occurences', () => { + expect(resolvePathVariables('/{var1}/segment1/{var1}', { var1: 'value1' })).toBe( + '/value1/segment1/value1' + ); + }); + + it('should replace multiple variables', () => { + const path = resolvePathVariables('/{var1}/segment1/{var2}', { + var1: 'value1', + var2: 'value2', + }); + + expect(path).toBe('/value1/segment1/value2'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.ts new file mode 100644 index 0000000000000..075d74da018b4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/utils.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 const resolvePathVariables = (path: string, variables: { [K: string]: string | number }) => + Object.keys(variables).reduce((acc, paramName) => { + return acc.replace(new RegExp(`\{${paramName}\}`, 'g'), String(variables[paramName])); + }, path); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.test.ts index 5e00d833981ed..534a4ec14861b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.test.ts @@ -13,6 +13,7 @@ import { isLoadingResourceState, isLoadedResourceState, isFailedResourceState, + isStaleResourceState, getLastLoadedResourceState, getCurrentResourceError, isOutdatedResourceState, @@ -137,6 +138,24 @@ describe('AsyncResourceState', () => { expect(isFailedResourceState(failedResourceStateInitially)).toBe(true); }); }); + + describe('isStaleResourceState()', () => { + it('returns true for UninitialisedResourceState', () => { + expect(isStaleResourceState(uninitialisedResourceState)).toBe(true); + }); + + it('returns false for LoadingResourceState', () => { + expect(isStaleResourceState(loadingResourceStateInitially)).toBe(false); + }); + + it('returns true for LoadedResourceState', () => { + expect(isStaleResourceState(loadedResourceState)).toBe(true); + }); + + it('returns true for FailedResourceState', () => { + expect(isStaleResourceState(failedResourceStateInitially)).toBe(true); + }); + }); }); describe('functions', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.ts index 4639a50a61865..bb868418e7f0d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/async_resource_state.ts @@ -35,7 +35,7 @@ export interface UninitialisedResourceState { * @param Data - type of the data that is referenced by resource state * @param Error - type of the error that can happen during attempt to update data */ -export interface LoadingResourceState { +export interface LoadingResourceState { type: 'LoadingResourceState'; previousState: StaleResourceState; } @@ -46,7 +46,7 @@ export interface LoadingResourceState { * * @param Data - type of the data that is referenced by resource state */ -export interface LoadedResourceState { +export interface LoadedResourceState { type: 'LoadedResourceState'; data: Data; } @@ -59,7 +59,7 @@ export interface LoadedResourceState { * @param Data - type of the data that is referenced by resource state * @param Error - type of the error that can happen during attempt to update data */ -export interface FailedResourceState { +export interface FailedResourceState { type: 'FailedResourceState'; error: Error; lastLoadedState?: LoadedResourceState; @@ -71,7 +71,7 @@ export interface FailedResourceState { * @param Data - type of the data that is referenced by resource state * @param Error - type of the error that can happen during attempt to update data */ -export type StaleResourceState = +export type StaleResourceState = | UninitialisedResourceState | LoadedResourceState | FailedResourceState; @@ -82,7 +82,7 @@ export type StaleResourceState = * @param Data - type of the data that is referenced by resource state * @param Error - type of the error that can happen during attempt to update data */ -export type AsyncResourceState = +export type AsyncResourceState = | UninitialisedResourceState | LoadingResourceState | LoadedResourceState @@ -106,6 +106,13 @@ export const isFailedResourceState = ( state: Immutable> ): state is Immutable> => state.type === 'FailedResourceState'; +export const isStaleResourceState = ( + state: Immutable> +): state is Immutable> => + isUninitialisedResourceState(state) || + isLoadedResourceState(state) || + isFailedResourceState(state); + // Set of functions to work with AsyncResourceState export const getLastLoadedResourceState = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts index 071557ec1a815..4c38ac0c4239a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ServerApiError } from '../../../../common/types'; import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps'; import { AsyncResourceState } from '.'; import { TrustedAppsUrlParams } from '../types'; -import { ServerApiError } from '../../../../common/types'; export interface PaginationInfo { index: number; @@ -18,6 +18,7 @@ export interface TrustedAppsListData { items: TrustedApp[]; totalItemsCount: number; paginationInfo: PaginationInfo; + timestamp: number; } /** Store State when an API request has been sent to create a new trusted app entry */ @@ -42,8 +43,14 @@ export interface TrustedAppsListPageState { listView: { currentListResourceState: AsyncResourceState; currentPaginationInfo: PaginationInfo; + freshDataTimestamp: number; show: TrustedAppsUrlParams['show'] | undefined; }; + deletionDialog: { + entry?: TrustedApp; + confirmed: boolean; + submissionResourceState: AsyncResourceState; + }; createView: | undefined | TrustedAppCreatePending diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts index 3a43ffe58262c..5315087c09655 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux'; + +import { TrustedApp } from '../../../../../common/endpoint/types'; import { AsyncResourceState, TrustedAppCreateFailure, @@ -12,12 +15,30 @@ import { TrustedAppsListData, } from '../state'; -export interface TrustedAppsListResourceStateChanged { - type: 'trustedAppsListResourceStateChanged'; +export type TrustedAppsListDataOutdated = Action<'trustedAppsListDataOutdated'>; + +interface ResourceStateChanged extends Action { + payload: { newState: AsyncResourceState }; +} + +export type TrustedAppsListResourceStateChanged = ResourceStateChanged< + 'trustedAppsListResourceStateChanged', + TrustedAppsListData +>; + +export type TrustedAppDeletionSubmissionResourceStateChanged = ResourceStateChanged< + 'trustedAppDeletionSubmissionResourceStateChanged' +>; + +export type TrustedAppDeletionDialogStarted = Action<'trustedAppDeletionDialogStarted'> & { payload: { - newState: AsyncResourceState; + entry: TrustedApp; }; -} +}; + +export type TrustedAppDeletionDialogConfirmed = Action<'trustedAppDeletionDialogConfirmed'>; + +export type TrustedAppDeletionDialogClosed = Action<'trustedAppDeletionDialogClosed'>; export interface UserClickedSaveNewTrustedAppButton { type: 'userClickedSaveNewTrustedAppButton'; @@ -35,7 +56,12 @@ export interface ServerReturnedCreateTrustedAppFailure { } export type TrustedAppsPageAction = + | TrustedAppsListDataOutdated | TrustedAppsListResourceStateChanged + | TrustedAppDeletionSubmissionResourceStateChanged + | TrustedAppDeletionDialogStarted + | TrustedAppDeletionDialogConfirmed + | TrustedAppDeletionDialogClosed | UserClickedSaveNewTrustedAppButton | ServerReturnedCreateTrustedAppSuccess | ServerReturnedCreateTrustedAppFailure; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index e5f00ee0ccf81..19c2d3a62781f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -10,8 +10,10 @@ import { createSpyMiddleware } from '../../../../common/store/test_utils'; import { createFailedListViewWithPagination, + createListLoadedResourceState, createLoadedListViewWithPagination, createLoadingListViewWithPagination, + createSampleTrustedApp, createSampleTrustedApps, createServerApiError, createUserChangedUrlAction, @@ -22,6 +24,14 @@ import { PaginationInfo, TrustedAppsListPageState } from '../state'; import { initialTrustedAppsPageState, trustedAppsPageReducer } from './reducer'; import { createTrustedAppsPageMiddleware } from './middleware'; +const initialNow = 111111; +const dateNowMock = jest.fn(); +dateNowMock.mockReturnValue(initialNow); + +Date.now = dateNowMock; + +const initialState = initialTrustedAppsPageState(); + const createGetTrustedListAppsResponse = (pagination: PaginationInfo, totalItemsCount: number) => ({ data: createSampleTrustedApps(pagination), page: pagination.index, @@ -31,6 +41,7 @@ const createGetTrustedListAppsResponse = (pagination: PaginationInfo, totalItems const createTrustedAppsServiceMock = (): jest.Mocked => ({ getTrustedAppsList: jest.fn(), + deleteTrustedApp: jest.fn(), createTrustedApp: jest.fn(), }); @@ -50,13 +61,19 @@ const createStoreSetup = (trustedAppsService: TrustedAppsService) => { }; describe('middleware', () => { - describe('refreshing list resource state', () => { + beforeEach(() => { + dateNowMock.mockReturnValue(initialNow); + }); + + describe('initial state', () => { it('sets initial state properly', async () => { expect(createStoreSetup(createTrustedAppsServiceMock()).store.getState()).toStrictEqual( - initialTrustedAppsPageState + initialState ); }); + }); + describe('refreshing list resource state', () => { it('refreshes the list when location changes and data gets outdated', async () => { const pagination = { index: 2, size: 50 }; const service = createTrustedAppsServiceMock(); @@ -69,17 +86,17 @@ describe('middleware', () => { store.dispatch(createUserChangedUrlAction('/trusted_apps', '?page_index=2&page_size=50')); expect(store.getState()).toStrictEqual({ - listView: createLoadingListViewWithPagination(pagination), + ...initialState, + listView: createLoadingListViewWithPagination(initialNow, pagination), active: true, - createView: undefined, }); await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); expect(store.getState()).toStrictEqual({ - listView: createLoadedListViewWithPagination(pagination, pagination, 500), + ...initialState, + listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500), active: true, - createView: undefined, }); }); @@ -100,13 +117,50 @@ describe('middleware', () => { expect(service.getTrustedAppsList).toBeCalledTimes(1); expect(store.getState()).toStrictEqual({ - listView: createLoadedListViewWithPagination(pagination, pagination, 500), + ...initialState, + listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500), active: true, - createView: undefined, }); }); - it('set list resource state to faile when failing to load data', async () => { + it('refreshes the list when data gets outdated with and outdate action', async () => { + const newNow = 222222; + const pagination = { index: 0, size: 10 }; + const service = createTrustedAppsServiceMock(); + const { store, spyMiddleware } = createStoreSetup(service); + + service.getTrustedAppsList.mockResolvedValue( + createGetTrustedListAppsResponse(pagination, 500) + ); + + store.dispatch(createUserChangedUrlAction('/trusted_apps')); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + dateNowMock.mockReturnValue(newNow); + + store.dispatch({ type: 'trustedAppsListDataOutdated' }); + + expect(store.getState()).toStrictEqual({ + ...initialState, + listView: createLoadingListViewWithPagination( + newNow, + pagination, + createListLoadedResourceState(pagination, 500, initialNow) + ), + active: true, + }); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + expect(store.getState()).toStrictEqual({ + ...initialState, + listView: createLoadedListViewWithPagination(newNow, pagination, pagination, 500), + active: true, + }); + }); + + it('set list resource state to failed when failing to load data', async () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -117,12 +171,13 @@ describe('middleware', () => { await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); expect(store.getState()).toStrictEqual({ + ...initialState, listView: createFailedListViewWithPagination( + initialNow, { index: 2, size: 50 }, createServerApiError('Internal Server Error') ), active: true, - createView: undefined, }); const infiniteLoopTest = async () => { @@ -132,4 +187,151 @@ describe('middleware', () => { await expect(infiniteLoopTest).rejects.not.toBeNull(); }); }); + + describe('submitting deletion dialog', () => { + const newNow = 222222; + const entry = createSampleTrustedApp(3); + const notFoundError = createServerApiError('Not Found'); + const pagination = { index: 0, size: 10 }; + const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500); + const listView = createLoadedListViewWithPagination(initialNow, pagination, pagination, 500); + const listViewNew = createLoadedListViewWithPagination(newNow, pagination, pagination, 500); + const testStartState = { ...initialState, listView, active: true }; + + it('does not submit when entry is undefined', async () => { + const service = createTrustedAppsServiceMock(); + const { store, spyMiddleware } = createStoreSetup(service); + + service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); + service.deleteTrustedApp.mockResolvedValue(); + + store.dispatch(createUserChangedUrlAction('/trusted_apps')); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); + + expect(store.getState()).toStrictEqual({ + ...testStartState, + deletionDialog: { ...testStartState.deletionDialog, confirmed: true }, + }); + }); + + it('submits successfully when entry is defined', async () => { + const service = createTrustedAppsServiceMock(); + const { store, spyMiddleware } = createStoreSetup(service); + + service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); + service.deleteTrustedApp.mockResolvedValue(); + + store.dispatch(createUserChangedUrlAction('/trusted_apps')); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + dateNowMock.mockReturnValue(newNow); + + store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); + store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); + + expect(store.getState()).toStrictEqual({ + ...testStartState, + deletionDialog: { + entry, + confirmed: true, + submissionResourceState: { + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }, + }); + + await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + expect(store.getState()).toStrictEqual({ ...testStartState, listView: listViewNew }); + expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); + expect(service.deleteTrustedApp).toBeCalledTimes(1); + }); + + it('does not submit twice', async () => { + const service = createTrustedAppsServiceMock(); + const { store, spyMiddleware } = createStoreSetup(service); + + service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); + service.deleteTrustedApp.mockResolvedValue(); + + store.dispatch(createUserChangedUrlAction('/trusted_apps')); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + dateNowMock.mockReturnValue(newNow); + + store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); + store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); + store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); + + expect(store.getState()).toStrictEqual({ + ...testStartState, + deletionDialog: { + entry, + confirmed: true, + submissionResourceState: { + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }, + }); + + await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + expect(store.getState()).toStrictEqual({ ...testStartState, listView: listViewNew }); + expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); + expect(service.deleteTrustedApp).toBeCalledTimes(1); + }); + + it('does not submit when server response with failure', async () => { + const service = createTrustedAppsServiceMock(); + const { store, spyMiddleware } = createStoreSetup(service); + + service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); + service.deleteTrustedApp.mockRejectedValue(notFoundError); + + store.dispatch(createUserChangedUrlAction('/trusted_apps')); + + await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); + + store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); + store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); + + expect(store.getState()).toStrictEqual({ + ...testStartState, + deletionDialog: { + entry, + confirmed: true, + submissionResourceState: { + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }, + }); + + await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); + + expect(store.getState()).toStrictEqual({ + ...testStartState, + deletionDialog: { + entry, + confirmed: true, + submissionResourceState: { + type: 'FailedResourceState', + error: notFoundError, + lastLoadedState: undefined, + }, + }, + }); + expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); + expect(service.deleteTrustedApp).toBeCalledTimes(1); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index bf9cacff5caf0..dd96c8d807048 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -16,15 +16,22 @@ import { TrustedAppsHttpService, TrustedAppsService } from '../service'; import { AsyncResourceState, + getLastLoadedResourceState, + isStaleResourceState, StaleResourceState, TrustedAppsListData, TrustedAppsListPageState, } from '../state'; -import { TrustedAppsListResourceStateChanged } from './action'; +import { + TrustedAppDeletionSubmissionResourceStateChanged, + TrustedAppsListResourceStateChanged, +} from './action'; import { getCurrentListResourceState, + getDeletionDialogEntry, + getDeletionSubmissionResourceState, getLastLoadedListResourceState, getListCurrentPageIndex, getListCurrentPageSize, @@ -40,46 +47,98 @@ const createTrustedAppsListResourceStateChangedAction = ( payload: { newState }, }); -const refreshList = async ( +const refreshListIfNeeded = async ( store: ImmutableMiddlewareAPI, trustedAppsService: TrustedAppsService ) => { - store.dispatch( - createTrustedAppsListResourceStateChangedAction({ - type: 'LoadingResourceState', - // need to think on how to avoid the casting - previousState: getCurrentListResourceState(store.getState()) as Immutable< - StaleResourceState - >, - }) - ); - - try { - const pageIndex = getListCurrentPageIndex(store.getState()); - const pageSize = getListCurrentPageSize(store.getState()); - const response = await trustedAppsService.getTrustedAppsList({ - page: pageIndex + 1, - per_page: pageSize, - }); - + if (needsRefreshOfListData(store.getState())) { store.dispatch( createTrustedAppsListResourceStateChangedAction({ - type: 'LoadedResourceState', - data: { - items: response.data, - totalItemsCount: response.total, - paginationInfo: { index: pageIndex, size: pageSize }, - }, + type: 'LoadingResourceState', + // need to think on how to avoid the casting + previousState: getCurrentListResourceState(store.getState()) as Immutable< + StaleResourceState + >, }) ); - } catch (error) { + + try { + const pageIndex = getListCurrentPageIndex(store.getState()); + const pageSize = getListCurrentPageSize(store.getState()); + const response = await trustedAppsService.getTrustedAppsList({ + page: pageIndex + 1, + per_page: pageSize, + }); + + store.dispatch( + createTrustedAppsListResourceStateChangedAction({ + type: 'LoadedResourceState', + data: { + items: response.data, + totalItemsCount: response.total, + paginationInfo: { index: pageIndex, size: pageSize }, + timestamp: Date.now(), + }, + }) + ); + } catch (error) { + store.dispatch( + createTrustedAppsListResourceStateChangedAction({ + type: 'FailedResourceState', + error, + lastLoadedState: getLastLoadedListResourceState(store.getState()), + }) + ); + } + } +}; + +const createTrustedAppDeletionSubmissionResourceStateChanged = ( + newState: Immutable +): Immutable => ({ + type: 'trustedAppDeletionSubmissionResourceStateChanged', + payload: { newState }, +}); + +const submitDeletionIfNeeded = async ( + store: ImmutableMiddlewareAPI, + trustedAppsService: TrustedAppsService +) => { + const submissionResourceState = getDeletionSubmissionResourceState(store.getState()); + const entry = getDeletionDialogEntry(store.getState()); + + if (isStaleResourceState(submissionResourceState) && entry !== undefined) { store.dispatch( - createTrustedAppsListResourceStateChangedAction({ - type: 'FailedResourceState', - error, - lastLoadedState: getLastLoadedListResourceState(store.getState()), + createTrustedAppDeletionSubmissionResourceStateChanged({ + type: 'LoadingResourceState', + previousState: submissionResourceState, }) ); + + try { + await trustedAppsService.deleteTrustedApp({ id: entry.id }); + + store.dispatch( + createTrustedAppDeletionSubmissionResourceStateChanged({ + type: 'LoadedResourceState', + data: null, + }) + ); + store.dispatch({ + type: 'trustedAppDeletionDialogClosed', + }); + store.dispatch({ + type: 'trustedAppsListDataOutdated', + }); + } catch (error) { + store.dispatch( + createTrustedAppDeletionSubmissionResourceStateChanged({ + type: 'FailedResourceState', + error, + lastLoadedState: getLastLoadedResourceState(submissionResourceState), + }) + ); + } } }; @@ -102,7 +161,9 @@ const createTrustedApp = async ( data: createdTrustedApp, }, }); - refreshList(store, trustedAppsService); + store.dispatch({ + type: 'trustedAppsListDataOutdated', + }); } catch (error) { dispatch({ type: 'serverReturnedCreateTrustedAppFailure', @@ -122,8 +183,12 @@ export const createTrustedAppsPageMiddleware = ( next(action); // TODO: need to think if failed state is a good condition to consider need for refresh - if (action.type === 'userChangedUrl' && needsRefreshOfListData(store.getState())) { - await refreshList(store, trustedAppsService); + if (action.type === 'userChangedUrl' || action.type === 'trustedAppsListDataOutdated') { + await refreshListIfNeeded(store, trustedAppsService); + } + + if (action.type === 'trustedAppDeletionDialogConfirmed') { + await submitDeletionIfNeeded(store, trustedAppsService); } if (action.type === 'userClickedSaveNewTrustedAppButton') { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts index 76dd4b48e63d2..228f0932edd28 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts @@ -4,93 +4,180 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AsyncResourceState } from '../state'; import { initialTrustedAppsPageState, trustedAppsPageReducer } from './reducer'; import { + createSampleTrustedApp, createListLoadedResourceState, createLoadedListViewWithPagination, - createTrustedAppsListResourceStateChangedAction, createUserChangedUrlAction, + createTrustedAppsListResourceStateChangedAction, } from '../test_utils'; +const initialNow = 111111; +const dateNowMock = jest.fn(); +dateNowMock.mockReturnValue(initialNow); + +Date.now = dateNowMock; + +const initialState = initialTrustedAppsPageState(); + describe('reducer', () => { describe('UserChangedUrl', () => { it('makes page state active and extracts pagination parameters', () => { const result = trustedAppsPageReducer( - initialTrustedAppsPageState, + initialState, createUserChangedUrlAction('/trusted_apps', '?page_index=5&page_size=50') ); expect(result).toStrictEqual({ - listView: { - ...initialTrustedAppsPageState.listView, - currentPaginationInfo: { index: 5, size: 50 }, - }, + ...initialState, + listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, active: true, - createView: undefined, }); }); it('extracts default pagination parameters when none provided', () => { const result = trustedAppsPageReducer( { - ...initialTrustedAppsPageState, - listView: { - ...initialTrustedAppsPageState.listView, - currentPaginationInfo: { index: 5, size: 50 }, - }, + ...initialState, + listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, }, createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60') ); - expect(result).toStrictEqual({ - ...initialTrustedAppsPageState, - active: true, - }); + expect(result).toStrictEqual({ ...initialState, active: true }); }); it('extracts default pagination parameters when invalid provided', () => { const result = trustedAppsPageReducer( { - ...initialTrustedAppsPageState, - listView: { - ...initialTrustedAppsPageState.listView, - currentPaginationInfo: { index: 5, size: 50 }, - }, + ...initialState, + listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, }, createUserChangedUrlAction('/trusted_apps') ); - expect(result).toStrictEqual({ - ...initialTrustedAppsPageState, - active: true, - }); + expect(result).toStrictEqual({ ...initialState, active: true }); }); it('makes page state inactive and resets list to uninitialised state when navigating away', () => { const result = trustedAppsPageReducer( - { listView: createLoadedListViewWithPagination(), active: true, createView: undefined }, + { ...initialState, listView: createLoadedListViewWithPagination(initialNow), active: true }, createUserChangedUrlAction('/endpoints') ); - expect(result).toStrictEqual(initialTrustedAppsPageState); + expect(result).toStrictEqual(initialState); }); }); describe('TrustedAppsListResourceStateChanged', () => { it('sets the current list resource state', () => { - const listResourceState = createListLoadedResourceState({ index: 3, size: 50 }, 200); + const listResourceState = createListLoadedResourceState( + { index: 3, size: 50 }, + 200, + initialNow + ); const result = trustedAppsPageReducer( - initialTrustedAppsPageState, + initialState, createTrustedAppsListResourceStateChangedAction(listResourceState) ); expect(result).toStrictEqual({ - ...initialTrustedAppsPageState, - listView: { - ...initialTrustedAppsPageState.listView, - currentListResourceState: listResourceState, + ...initialState, + listView: { ...initialState.listView, currentListResourceState: listResourceState }, + }); + }); + }); + + describe('TrustedAppsListDataOutdated', () => { + it('sets the list view freshness timestamp', () => { + const newNow = 222222; + dateNowMock.mockReturnValue(newNow); + + const result = trustedAppsPageReducer(initialState, { type: 'trustedAppsListDataOutdated' }); + + expect(result).toStrictEqual({ + ...initialState, + listView: { ...initialState.listView, freshDataTimestamp: newNow }, + }); + }); + }); + + describe('TrustedAppDeletionSubmissionResourceStateChanged', () => { + it('sets the deletion dialog submission resource state', () => { + const submissionResourceState: AsyncResourceState = { + type: 'LoadedResourceState', + data: null, + }; + const result = trustedAppsPageReducer(initialState, { + type: 'trustedAppDeletionSubmissionResourceStateChanged', + payload: { newState: submissionResourceState }, + }); + + expect(result).toStrictEqual({ + ...initialState, + deletionDialog: { ...initialState.deletionDialog, submissionResourceState }, + }); + }); + }); + + describe('TrustedAppDeletionDialogStarted', () => { + it('sets the deletion dialog state to started', () => { + const entry = createSampleTrustedApp(3); + const result = trustedAppsPageReducer(initialState, { + type: 'trustedAppDeletionDialogStarted', + payload: { entry }, + }); + + expect(result).toStrictEqual({ + ...initialState, + deletionDialog: { ...initialState.deletionDialog, entry }, + }); + }); + }); + + describe('TrustedAppDeletionDialogConfirmed', () => { + it('sets the deletion dialog state to confirmed', () => { + const entry = createSampleTrustedApp(3); + const result = trustedAppsPageReducer( + { + ...initialState, + deletionDialog: { + entry, + confirmed: false, + submissionResourceState: { type: 'UninitialisedResourceState' }, + }, + }, + { type: 'trustedAppDeletionDialogConfirmed' } + ); + + expect(result).toStrictEqual({ + ...initialState, + deletionDialog: { + entry, + confirmed: true, + submissionResourceState: { type: 'UninitialisedResourceState' }, }, }); }); }); + + describe('TrustedAppDeletionDialogClosed', () => { + it('sets the deletion dialog state to confirmed', () => { + const result = trustedAppsPageReducer( + { + ...initialState, + deletionDialog: { + entry: createSampleTrustedApp(3), + confirmed: true, + submissionResourceState: { type: 'UninitialisedResourceState' }, + }, + }, + { type: 'trustedAppDeletionDialogClosed' } + ); + + expect(result).toStrictEqual(initialState); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts index d824a6e95c8d5..ec210254bf76f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts @@ -19,9 +19,14 @@ import { } from '../../../common/constants'; import { + TrustedAppDeletionDialogClosed, + TrustedAppDeletionDialogConfirmed, + TrustedAppDeletionDialogStarted, + TrustedAppDeletionSubmissionResourceStateChanged, + TrustedAppsListDataOutdated, + TrustedAppsListResourceStateChanged, ServerReturnedCreateTrustedAppFailure, ServerReturnedCreateTrustedAppSuccess, - TrustedAppsListResourceStateChanged, UserClickedSaveNewTrustedAppButton, } from './action'; import { TrustedAppsListPageState } from '../state'; @@ -41,6 +46,16 @@ const isTrustedAppsPageLocation = (location: Immutable) => { ); }; +const trustedAppsListDataOutdated: CaseReducer = (state, action) => { + return { + ...state, + listView: { + ...state.listView, + freshDataTimestamp: Date.now(), + }, + }; +}; + const trustedAppsListResourceStateChanged: CaseReducer = ( state, action @@ -54,6 +69,44 @@ const trustedAppsListResourceStateChanged: CaseReducer = ( + state, + action +) => { + return { + ...state, + deletionDialog: { ...state.deletionDialog, submissionResourceState: action.payload.newState }, + }; +}; + +const trustedAppDeletionDialogStarted: CaseReducer = ( + state, + action +) => { + return { + ...state, + deletionDialog: { + entry: action.payload.entry, + confirmed: false, + submissionResourceState: { type: 'UninitialisedResourceState' }, + }, + }; +}; + +const trustedAppDeletionDialogConfirmed: CaseReducer = ( + state, + action +) => { + return { ...state, deletionDialog: { ...state.deletionDialog, confirmed: true } }; +}; + +const trustedAppDeletionDialogClosed: CaseReducer = ( + state, + action +) => { + return { ...state, deletionDialog: initialDeletionDialogState() }; +}; + const userChangedUrl: CaseReducer = (state, action) => { if (isTrustedAppsPageLocation(action.payload)) { const parsedUrlsParams = parse(action.payload.search.slice(1)); @@ -75,7 +128,7 @@ const userChangedUrl: CaseReducer = (state, action) => { active: true, }; } else { - return initialTrustedAppsPageState; + return initialTrustedAppsPageState(); } }; @@ -90,27 +143,49 @@ const trustedAppsCreateResourceChanged: CaseReducer< }; }; -export const initialTrustedAppsPageState: TrustedAppsListPageState = { +const initialDeletionDialogState = (): TrustedAppsListPageState['deletionDialog'] => ({ + confirmed: false, + submissionResourceState: { type: 'UninitialisedResourceState' }, +}); + +export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({ listView: { currentListResourceState: { type: 'UninitialisedResourceState' }, currentPaginationInfo: { index: MANAGEMENT_DEFAULT_PAGE, size: MANAGEMENT_DEFAULT_PAGE_SIZE, }, + freshDataTimestamp: Date.now(), show: undefined, }, + deletionDialog: initialDeletionDialogState(), createView: undefined, active: false, -}; +}); export const trustedAppsPageReducer: StateReducer = ( - state = initialTrustedAppsPageState, + state = initialTrustedAppsPageState(), action ) => { switch (action.type) { + case 'trustedAppsListDataOutdated': + return trustedAppsListDataOutdated(state, action); + case 'trustedAppsListResourceStateChanged': return trustedAppsListResourceStateChanged(state, action); + case 'trustedAppDeletionSubmissionResourceStateChanged': + return trustedAppDeletionSubmissionResourceStateChanged(state, action); + + case 'trustedAppDeletionDialogStarted': + return trustedAppDeletionDialogStarted(state, action); + + case 'trustedAppDeletionDialogConfirmed': + return trustedAppDeletionDialogConfirmed(state, action); + + case 'trustedAppDeletionDialogClosed': + return trustedAppDeletionDialogClosed(state, action); + case 'userChangedUrl': return userChangedUrl(state, action); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts index 453afa1befa6b..0be4d0b05acc4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AsyncResourceState, TrustedAppsListPageState } from '../state'; +import { initialTrustedAppsPageState } from './reducer'; import { getCurrentListResourceState, getLastLoadedListResourceState, @@ -14,6 +16,12 @@ import { getListTotalItemsCount, isListLoading, needsRefreshOfListData, + isDeletionDialogOpen, + isDeletionInProgress, + isDeletionSuccessful, + getDeletionError, + getDeletionDialogEntry, + getDeletionSubmissionResourceState, } from './selectors'; import { @@ -23,96 +31,118 @@ import { createListFailedResourceState, createListLoadedResourceState, createLoadedListViewWithPagination, + createSampleTrustedApp, createSampleTrustedApps, + createServerApiError, createUninitialisedResourceState, } from '../test_utils'; +const initialNow = 111111; +const dateNowMock = jest.fn(); +dateNowMock.mockReturnValue(initialNow); + +Date.now = dateNowMock; + +const initialState = initialTrustedAppsPageState(); + +const createStateWithDeletionSubmissionResourceState = ( + submissionResourceState: AsyncResourceState +): TrustedAppsListPageState => ({ + ...initialState, + deletionDialog: { ...initialState.deletionDialog, submissionResourceState }, +}); + describe('selectors', () => { describe('needsRefreshOfListData()', () => { it('returns false for outdated resource state and inactive state', () => { - expect( - needsRefreshOfListData({ - listView: createDefaultListView(), - active: false, - createView: undefined, - }) - ).toBe(false); + expect(needsRefreshOfListData(initialState)).toBe(false); }); it('returns true for outdated resource state and active state', () => { - expect( - needsRefreshOfListData({ - listView: createDefaultListView(), - active: true, - createView: undefined, - }) - ).toBe(true); + expect(needsRefreshOfListData({ ...initialState, active: true })).toBe(true); }); it('returns true when current loaded page index is outdated', () => { - const listView = createLoadedListViewWithPagination({ index: 1, size: 20 }); + const listView = createLoadedListViewWithPagination(initialNow, { index: 1, size: 20 }); - expect(needsRefreshOfListData({ listView, active: true, createView: undefined })).toBe(true); + expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); }); it('returns true when current loaded page size is outdated', () => { - const listView = createLoadedListViewWithPagination({ index: 0, size: 50 }); + const listView = createLoadedListViewWithPagination(initialNow, { index: 0, size: 50 }); + + expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); + }); + + it('returns true when current loaded data timestamp is outdated', () => { + const listView = { + ...createLoadedListViewWithPagination(111111), + freshDataTimestamp: 222222, + }; - expect(needsRefreshOfListData({ listView, active: true, createView: undefined })).toBe(true); + expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); }); it('returns false when current loaded data is up to date', () => { - const listView = createLoadedListViewWithPagination(); + const listView = createLoadedListViewWithPagination(initialNow); - expect(needsRefreshOfListData({ listView, active: true, createView: undefined })).toBe(false); + expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(false); }); }); describe('getCurrentListResourceState()', () => { it('returns current list resource state', () => { - const listView = createDefaultListView(); + const state = { ...initialState, listView: createDefaultListView(initialNow) }; - expect( - getCurrentListResourceState({ listView, active: false, createView: undefined }) - ).toStrictEqual(createUninitialisedResourceState()); + expect(getCurrentListResourceState(state)).toStrictEqual(createUninitialisedResourceState()); }); }); describe('getLastLoadedListResourceState()', () => { it('returns last loaded list resource state', () => { - const listView = { - currentListResourceState: createListComplexLoadingResourceState( - createDefaultPaginationInfo(), - 200 - ), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListComplexLoadingResourceState( + createDefaultPaginationInfo(), + 200, + initialNow + ), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect( - getLastLoadedListResourceState({ listView, active: false, createView: undefined }) - ).toStrictEqual(createListLoadedResourceState(createDefaultPaginationInfo(), 200)); + expect(getLastLoadedListResourceState(state)).toStrictEqual( + createListLoadedResourceState(createDefaultPaginationInfo(), 200, initialNow) + ); }); }); describe('getListItems()', () => { it('returns empty list when no valid data loaded', () => { - expect( - getListItems({ listView: createDefaultListView(), active: false, createView: undefined }) - ).toStrictEqual([]); + const state = { ...initialState, listView: createDefaultListView(initialNow) }; + + expect(getListItems(state)).toStrictEqual([]); }); it('returns last loaded list items', () => { - const listView = { - currentListResourceState: createListComplexLoadingResourceState( - createDefaultPaginationInfo(), - 200 - ), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListComplexLoadingResourceState( + createDefaultPaginationInfo(), + 200, + initialNow + ), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect(getListItems({ listView, active: false, createView: undefined })).toStrictEqual( + expect(getListItems(state)).toStrictEqual( createSampleTrustedApps(createDefaultPaginationInfo()) ); }); @@ -120,100 +150,239 @@ describe('selectors', () => { describe('getListTotalItemsCount()', () => { it('returns 0 when no valid data loaded', () => { - expect( - getListTotalItemsCount({ - listView: createDefaultListView(), - active: false, - createView: undefined, - }) - ).toBe(0); + const state = { ...initialState, listView: createDefaultListView(initialNow) }; + + expect(getListTotalItemsCount(state)).toBe(0); }); it('returns last loaded total items count', () => { - const listView = { - currentListResourceState: createListComplexLoadingResourceState( - createDefaultPaginationInfo(), - 200 - ), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListComplexLoadingResourceState( + createDefaultPaginationInfo(), + 200, + initialNow + ), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect(getListTotalItemsCount({ listView, active: false, createView: undefined })).toBe(200); + expect(getListTotalItemsCount(state)).toBe(200); }); }); describe('getListCurrentPageIndex()', () => { it('returns page index', () => { - expect( - getListCurrentPageIndex({ - listView: createDefaultListView(), - active: false, - createView: undefined, - }) - ).toBe(0); + const state = { ...initialState, listView: createDefaultListView(initialNow) }; + + expect(getListCurrentPageIndex(state)).toBe(0); }); }); describe('getListCurrentPageSize()', () => { - it('returns page index', () => { - expect( - getListCurrentPageSize({ - listView: createDefaultListView(), - active: false, - createView: undefined, - }) - ).toBe(20); + it('returns page size', () => { + const state = { ...initialState, listView: createDefaultListView(initialNow) }; + + expect(getListCurrentPageSize(state)).toBe(20); }); }); describe('getListErrorMessage()', () => { it('returns undefined when not in failed state', () => { - const listView = { - currentListResourceState: createListComplexLoadingResourceState( - createDefaultPaginationInfo(), - 200 - ), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListComplexLoadingResourceState( + createDefaultPaginationInfo(), + 200, + initialNow + ), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect( - getListErrorMessage({ listView, active: false, createView: undefined }) - ).toBeUndefined(); + expect(getListErrorMessage(state)).toBeUndefined(); }); it('returns message when not in failed state', () => { - const listView = { - currentListResourceState: createListFailedResourceState('Internal Server Error'), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListFailedResourceState('Internal Server Error'), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect(getListErrorMessage({ listView, active: false, createView: undefined })).toBe( - 'Internal Server Error' - ); + expect(getListErrorMessage(state)).toBe('Internal Server Error'); }); }); describe('isListLoading()', () => { it('returns false when no loading is happening', () => { - expect( - isListLoading({ listView: createDefaultListView(), active: false, createView: undefined }) - ).toBe(false); + expect(isListLoading(initialState)).toBe(false); }); it('returns true when loading is in progress', () => { - const listView = { - currentListResourceState: createListComplexLoadingResourceState( - createDefaultPaginationInfo(), - 200 - ), - currentPaginationInfo: createDefaultPaginationInfo(), - show: undefined, + const state = { + ...initialState, + listView: { + currentListResourceState: createListComplexLoadingResourceState( + createDefaultPaginationInfo(), + 200, + initialNow + ), + currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp: initialNow, + show: undefined, + }, }; - expect(isListLoading({ listView, active: false, createView: undefined })).toBe(true); + expect(isListLoading(state)).toBe(true); + }); + }); + + describe('isDeletionDialogOpen()', () => { + it('returns false when no entry is set', () => { + expect(isDeletionDialogOpen(initialState)).toBe(false); + }); + + it('returns true when entry is set', () => { + const state = { + ...initialState, + deletionDialog: { + ...initialState.deletionDialog, + entry: createSampleTrustedApp(5), + }, + }; + + expect(isDeletionDialogOpen(state)).toBe(true); + }); + }); + + describe('isDeletionInProgress()', () => { + it('returns false when resource state is uninitialised', () => { + expect(isDeletionInProgress(initialState)).toBe(false); + }); + + it('returns true when resource state is loading', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }); + + expect(isDeletionInProgress(state)).toBe(true); + }); + + it('returns false when resource state is loaded', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadedResourceState', + data: null, + }); + + expect(isDeletionInProgress(state)).toBe(false); + }); + + it('returns false when resource state is failed', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'FailedResourceState', + error: createServerApiError('Not Found'), + }); + + expect(isDeletionInProgress(state)).toBe(false); + }); + }); + + describe('isDeletionSuccessful()', () => { + it('returns false when resource state is uninitialised', () => { + expect(isDeletionSuccessful(initialState)).toBe(false); + }); + + it('returns false when resource state is loading', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }); + + expect(isDeletionSuccessful(state)).toBe(false); + }); + + it('returns true when resource state is loaded', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadedResourceState', + data: null, + }); + + expect(isDeletionSuccessful(state)).toBe(true); + }); + + it('returns false when resource state is failed', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'FailedResourceState', + error: createServerApiError('Not Found'), + }); + + expect(isDeletionSuccessful(state)).toBe(false); + }); + }); + + describe('getDeletionError()', () => { + it('returns undefined when resource state is uninitialised', () => { + expect(getDeletionError(initialState)).toBeUndefined(); + }); + + it('returns undefined when resource state is loading', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }); + + expect(getDeletionError(state)).toBeUndefined(); + }); + + it('returns undefined when resource state is loaded', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'LoadedResourceState', + data: null, + }); + + expect(getDeletionError(state)).toBeUndefined(); + }); + + it('returns error when resource state is failed', () => { + const state = createStateWithDeletionSubmissionResourceState({ + type: 'FailedResourceState', + error: createServerApiError('Not Found'), + }); + + expect(getDeletionError(state)).toStrictEqual(createServerApiError('Not Found')); + }); + }); + + describe('getDeletionSubmissionResourceState()', () => { + it('returns submission resource state', () => { + expect(getDeletionSubmissionResourceState(initialState)).toStrictEqual({ + type: 'UninitialisedResourceState', + }); + }); + }); + + describe('getDeletionDialogEntry()', () => { + it('returns undefined when no entry is set', () => { + expect(getDeletionDialogEntry(initialState)).toBeUndefined(); + }); + + it('returns entry when entry is set', () => { + const entry = createSampleTrustedApp(5); + const state = { ...initialState, deletionDialog: { ...initialState.deletionDialog, entry } }; + + expect(getDeletionDialogEntry(state)).toStrictEqual(entry); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts index f074b21f79f4e..6239b425efe2f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts @@ -5,12 +5,15 @@ */ import { createSelector } from 'reselect'; +import { ServerApiError } from '../../../../common/types'; import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types'; import { AsyncResourceState, getCurrentResourceError, getLastLoadedResourceState, + isFailedResourceState, + isLoadedResourceState, isLoadingResourceState, isOutdatedResourceState, LoadedResourceState, @@ -32,12 +35,15 @@ const pageInfosEqual = (pageInfo1: PaginationInfo, pageInfo2: PaginationInfo): b export const needsRefreshOfListData = (state: Immutable): boolean => { const currentPageInfo = state.listView.currentPaginationInfo; const currentPage = state.listView.currentListResourceState; + const freshDataTimestamp = state.listView.freshDataTimestamp; return ( state.active && - isOutdatedResourceState(currentPage, (data) => - pageInfosEqual(currentPageInfo, data.paginationInfo) - ) + isOutdatedResourceState(currentPage, (data) => { + return ( + pageInfosEqual(currentPageInfo, data.paginationInfo) && data.timestamp >= freshDataTimestamp + ); + }) ); }; @@ -104,6 +110,38 @@ export const isListLoading = (state: Immutable): boole return isLoadingResourceState(state.listView.currentListResourceState); }; +export const isDeletionDialogOpen = (state: Immutable): boolean => { + return state.deletionDialog.entry !== undefined; +}; + +export const isDeletionInProgress = (state: Immutable): boolean => { + return isLoadingResourceState(state.deletionDialog.submissionResourceState); +}; + +export const isDeletionSuccessful = (state: Immutable): boolean => { + return isLoadedResourceState(state.deletionDialog.submissionResourceState); +}; + +export const getDeletionError = ( + state: Immutable +): Immutable | undefined => { + const submissionResourceState = state.deletionDialog.submissionResourceState; + + return isFailedResourceState(submissionResourceState) ? submissionResourceState.error : undefined; +}; + +export const getDeletionSubmissionResourceState = ( + state: Immutable +): AsyncResourceState => { + return state.deletionDialog.submissionResourceState; +}; + +export const getDeletionDialogEntry = ( + state: Immutable +): Immutable | undefined => { + return state.deletionDialog.entry; +}; + export const isCreatePending: (state: Immutable) => boolean = ({ createView, }) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts index 70e4e1e685b01..020a87f526e52 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts @@ -4,10 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { combineReducers, createStore } from 'redux'; import { ServerApiError } from '../../../../common/types'; import { TrustedApp } from '../../../../../common/endpoint/types'; import { RoutingAction } from '../../../../common/store/routing'; +import { + MANAGEMENT_STORE_GLOBAL_NAMESPACE, + MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, +} from '../../../common/constants'; + import { AsyncResourceState, FailedResourceState, @@ -20,30 +26,36 @@ import { UninitialisedResourceState, } from '../state'; +import { trustedAppsPageReducer } from '../store/reducer'; import { TrustedAppsListResourceStateChanged } from '../store/action'; -import { initialTrustedAppsPageState } from '../store/reducer'; const OS_LIST: Array = ['windows', 'macos', 'linux']; -export const createSampleTrustedApps = (paginationInfo: PaginationInfo): TrustedApp[] => { - return [...new Array(paginationInfo.size).keys()].map((i) => ({ - id: String(paginationInfo.index + i), - name: `trusted app ${paginationInfo.index + i}`, - description: `Trusted App ${paginationInfo.index + i}`, +export const createSampleTrustedApp = (i: number): TrustedApp => { + return { + id: String(i), + name: `trusted app ${i}`, + description: `Trusted App ${i}`, created_at: '1 minute ago', created_by: 'someone', os: OS_LIST[i % 3], entries: [], - })); + }; +}; + +export const createSampleTrustedApps = (paginationInfo: PaginationInfo): TrustedApp[] => { + return [...new Array(paginationInfo.size).keys()].map(createSampleTrustedApp); }; export const createTrustedAppsListData = ( paginationInfo: PaginationInfo, - totalItemsCount: number + totalItemsCount: number, + timestamp: number ) => ({ items: createSampleTrustedApps(paginationInfo), totalItemsCount, paginationInfo, + timestamp, }); export const createServerApiError = (message: string) => ({ @@ -58,10 +70,11 @@ export const createUninitialisedResourceState = (): UninitialisedResourceState = export const createListLoadedResourceState = ( paginationInfo: PaginationInfo, - totalItemsCount: number + totalItemsCount: number, + timestamp: number ): LoadedResourceState => ({ type: 'LoadedResourceState', - data: createTrustedAppsListData(paginationInfo, totalItemsCount), + data: createTrustedAppsListData(paginationInfo, totalItemsCount, timestamp), }); export const createListFailedResourceState = ( @@ -82,50 +95,64 @@ export const createListLoadingResourceState = ( export const createListComplexLoadingResourceState = ( paginationInfo: PaginationInfo, - totalItemsCount: number + totalItemsCount: number, + timestamp: number ): LoadingResourceState => createListLoadingResourceState( createListFailedResourceState( 'Internal Server Error', - createListLoadedResourceState(paginationInfo, totalItemsCount) + createListLoadedResourceState(paginationInfo, totalItemsCount, timestamp) ) ); export const createDefaultPaginationInfo = () => ({ index: 0, size: 20 }); -export const createDefaultListView = () => ({ - ...initialTrustedAppsPageState.listView, +export const createDefaultListView = ( + freshDataTimestamp: number +): TrustedAppsListPageState['listView'] => ({ currentListResourceState: createUninitialisedResourceState(), currentPaginationInfo: createDefaultPaginationInfo(), + freshDataTimestamp, + show: undefined, }); export const createLoadingListViewWithPagination = ( + freshDataTimestamp: number, currentPaginationInfo: PaginationInfo, previousState: StaleResourceState = createUninitialisedResourceState() ): TrustedAppsListPageState['listView'] => ({ - ...initialTrustedAppsPageState.listView, currentListResourceState: { type: 'LoadingResourceState', previousState }, currentPaginationInfo, + freshDataTimestamp, + show: undefined, }); export const createLoadedListViewWithPagination = ( + freshDataTimestamp: number, paginationInfo: PaginationInfo = createDefaultPaginationInfo(), currentPaginationInfo: PaginationInfo = createDefaultPaginationInfo(), totalItemsCount: number = 200 ): TrustedAppsListPageState['listView'] => ({ - ...initialTrustedAppsPageState.listView, - currentListResourceState: createListLoadedResourceState(paginationInfo, totalItemsCount), + currentListResourceState: createListLoadedResourceState( + paginationInfo, + totalItemsCount, + freshDataTimestamp + ), currentPaginationInfo, + freshDataTimestamp, + show: undefined, }); export const createFailedListViewWithPagination = ( + freshDataTimestamp: number, currentPaginationInfo: PaginationInfo, error: ServerApiError, lastLoadedState?: LoadedResourceState ): TrustedAppsListPageState['listView'] => ({ - ...initialTrustedAppsPageState.listView, currentListResourceState: { type: 'FailedResourceState', error, lastLoadedState }, currentPaginationInfo, + freshDataTimestamp, + show: undefined, }); export const createUserChangedUrlAction = (path: string, search: string = ''): RoutingAction => { @@ -138,3 +165,13 @@ export const createTrustedAppsListResourceStateChangedAction = ( type: 'trustedAppsListResourceStateChanged', payload: { newState }, }); + +export const createGlobalNoMiddlewareStore = () => { + return createStore( + combineReducers({ + [MANAGEMENT_STORE_GLOBAL_NAMESPACE]: combineReducers({ + [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, + }), + }) + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap new file mode 100644 index 0000000000000..fdb20f229f144 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap @@ -0,0 +1,315 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TrustedAppDeletionDialog renders correctly initially 1`] = ` + +
+ +`; + +exports[`TrustedAppDeletionDialog renders correctly when deletion failed 1`] = ` + +
+
+
+
+ +
+
+
+ Remove trusted application +
+
+
+
+
+

+ You are removing trusted application " + + trusted app 3 + + ". +

+

+ This action cannot be undone. Are you sure you wish to continue? +

+
+
+
+
+ + +
+
+
+
+
+ +`; + +exports[`TrustedAppDeletionDialog renders correctly when deletion is in progress 1`] = ` + +
+
+
+
+ +
+
+
+ Remove trusted application +
+
+
+
+
+

+ You are removing trusted application " + + trusted app 3 + + ". +

+

+ This action cannot be undone. Are you sure you wish to continue? +

+
+
+
+
+ + +
+
+
+
+
+ +`; + +exports[`TrustedAppDeletionDialog renders correctly when dialog started 1`] = ` + +
+
+
+
+ +
+
+
+ Remove trusted application +
+
+
+
+
+

+ You are removing trusted application " + + trusted app 3 + + ". +

+

+ This action cannot be undone. Are you sure you wish to continue? +

+
+
+
+
+ + +
+
+
+
+
+ +`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_list.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_list.test.tsx.snap index e0f846f5950f7..ccd94c63e96c8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_list.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_list.test.tsx.snap @@ -4,6 +4,7 @@ exports[`TrustedAppsList renders correctly initially 1`] = `
+ +
+ + Actions + +
+ @@ -106,7 +123,7 @@ exports[`TrustedAppsList renders correctly initially 1`] = ` >
+ +
+ + Actions + +
+ @@ -232,7 +266,7 @@ exports[`TrustedAppsList renders correctly when failed loading data for the firs >
+ +
+ + Actions + +
+ @@ -363,7 +414,7 @@ exports[`TrustedAppsList renders correctly when failed loading data for the seco >
+ +
+ + Actions + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
- -
- Name + + + + Delete + + +
+ + + + +
+ Name
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ @@ -2080,6 +2748,7 @@ exports[`TrustedAppsList renders correctly when loading data for the first time
+ +
+ + Actions + +
+ @@ -2182,7 +2867,7 @@ exports[`TrustedAppsList renders correctly when loading data for the first time >
+ +
+ + Actions + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ - + +
+ + + + Delete + + +
+ + + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ @@ -3890,10 +5192,11 @@ exports[`TrustedAppsList renders correctly when loading data for the second time `; -exports[`TrustedAppsList renders correctly when new page and page sie set (not loading yet) 1`] = ` +exports[`TrustedAppsList renders correctly when new page and page size set (not loading yet) 1`] = `
+ +
+ + Actions + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+
+ +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ + +
+ + + + Delete + + +
+ diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap deleted file mode 100644 index c8d9b46d5a0d2..0000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_apps_page.test.tsx.snap +++ /dev/null @@ -1,955 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TrustedAppsPage rendering 1`] = ` -Object { - "asFragment": [Function], - "baseElement": .c0 { - padding: 24px; -} - -.c0.siemWrapperPage--restrictWidthDefault, -.c0.siemWrapperPage--restrictWidthCustom { - box-sizing: content-box; - margin: 0 auto; -} - -.c0.siemWrapperPage--restrictWidthDefault { - max-width: 1000px; -} - -.c0.siemWrapperPage--fullHeight { - height: 100%; -} - -.c0.siemWrapperPage--withTimeline { - padding-right: 70px; -} - -.c0.siemWrapperPage--noPadding { - padding: 0; -} - -.c4 { - margin-top: 8px; -} - -.c4 .siemSubtitle__item { - color: #6a717d; - font-size: 12px; - line-height: 1.5; -} - -.c3 { - vertical-align: middle; -} - -.c1 { - margin-bottom: 24px; -} - -.c2 { - display: block; -} - -.c5 .euiFlyout { - z-index: 4001; -} - -@media only screen and (min-width:575px) { - .c4 .siemSubtitle__item { - display: inline-block; - margin-right: 16px; - } - - .c4 .siemSubtitle__item:last-child { - margin-right: 0; - } -} - - -
-
-
-
-
-

- Trusted Applications - - - Beta - -

-
-
- View and configure trusted applications -
-
-
-
- -
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - -
-
-
- - Name - -
-
-
- - OS - -
-
-
- - Date Created - -
-
-
- - Created By - -
-
-
- - No items found - -
-
-
-
-
-
-
- , - "container":
-
-
-
-
-

- Trusted Applications - - - Beta - -

-
-
- View and configure trusted applications -
-
-
-
- -
-
-
-
- - -
-
-
-
-
-
-
-
-
-
-
- - - - - - - - - - - - - - -
-
-
- - Name - -
-
-
- - OS - -
-
-
- - Date Created - -
-
-
- - Created By - -
-
-
- - No items found - -
-
-
-
-
-
-
, - "debug": [Function], - "findAllByAltText": [Function], - "findAllByDisplayValue": [Function], - "findAllByLabelText": [Function], - "findAllByPlaceholderText": [Function], - "findAllByRole": [Function], - "findAllByTestId": [Function], - "findAllByText": [Function], - "findAllByTitle": [Function], - "findByAltText": [Function], - "findByDisplayValue": [Function], - "findByLabelText": [Function], - "findByPlaceholderText": [Function], - "findByRole": [Function], - "findByTestId": [Function], - "findByText": [Function], - "findByTitle": [Function], - "getAllByAltText": [Function], - "getAllByDisplayValue": [Function], - "getAllByLabelText": [Function], - "getAllByPlaceholderText": [Function], - "getAllByRole": [Function], - "getAllByTestId": [Function], - "getAllByText": [Function], - "getAllByTitle": [Function], - "getByAltText": [Function], - "getByDisplayValue": [Function], - "getByLabelText": [Function], - "getByPlaceholderText": [Function], - "getByRole": [Function], - "getByTestId": [Function], - "getByText": [Function], - "getByTitle": [Function], - "queryAllByAltText": [Function], - "queryAllByDisplayValue": [Function], - "queryAllByLabelText": [Function], - "queryAllByPlaceholderText": [Function], - "queryAllByRole": [Function], - "queryAllByTestId": [Function], - "queryAllByText": [Function], - "queryAllByTitle": [Function], - "queryByAltText": [Function], - "queryByDisplayValue": [Function], - "queryByLabelText": [Function], - "queryByPlaceholderText": [Function], - "queryByRole": [Function], - "queryByTestId": [Function], - "queryByText": [Function], - "queryByTitle": [Function], - "rerender": [Function], - "unmount": [Function], -} -`; - -exports[`TrustedAppsPage when the Add Trusted App button is clicked should display create form 1`] = ` -@media only screen and (min-width:575px) { - -} - -
-
-
- -
-
-
-
- -
-
-
-
-
-
- -
-
-
-
- -
-
- - Select an option: Windows, is selected - - -
- - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
- - Select an option: Hash, is selected - - -
- - -
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
- -
-
-